printf and dynamic threads causing memory leaks

Hi,

I have recently been having some problems with my NetOS 7.5 on CC9P crashing. The nature of the crashes led me to believe that I had a memory leak. After much wailing and gnashing of teeth, I believe that I have tracked this down.

The system that I am working on periodically needs to send data via HTTP POST. These posts may be kicked of at various times and a different places in the application. The main application must also remain responsive whilst the POST is taking place.

To achieve this I decided to generate a routine that dynamically allocates a new thread, lets this new thread do the actual post, and then deletes the thread once it is complete. Within the new thread I have a number of printf statements to show the various states involved in looking up DNS, getting a connection to the host, sending the data, and waiting for the response.

It would appear that each time I created, ran and then deleted the threads, I was loosing 1032 bytes from the heap (checked with NABspGetHeapSnapshot()).

I then simplified things, by starting a new application. This defines a thread function that simply sleeps for a couple of seconds. The main application runs a very simple loop to create a thread, wait for it to complete, and then delete it.

If the thread includes a printf statement there is a 1032 byte loss of the heap. The following code shows the test I am using

Has anyone else seen this problem or have any ideas what exactly is going on?

#include 
#include 
#include 
#include 

#include "appconf_api.h"
#include "appconf.h"

/*****************************************************************************
  Defined Constants
*****************************************************************************/
#define THREAD_STACK_SZ   (4096)

static int GLB_PrevTotAlloc = 0;

void GetHeapInfo(void)
{
	int status;
	int alloc_change;
	NABspMallocStats stats;
	const char *MEM_FMT = "MEM:[%08ld] %s:	%d
";
	const char *ERR_FMT = "ERR:[%08ld] %s() failed with status %d
";

	status = NABspGetHeapSnapshot(&stats);
	if (status) {
		printf(ERR_FMT, tx_time_get(), "NABspMallocStats", status);
		return;
	}
//	printf(MEM_FMT, tx_time_get(), "Total RAM", stats.system);
//	printf(MEM_FMT, tx_time_get(), "Alloc RAM", stats.arena);
//	printf(MEM_FMT, tx_time_get(), "Chunks F", stats.ordblks);
//	printf(MEM_FMT, tx_time_get(), "Region Sp", stats.hblks);
//	printf(MEM_FMT, tx_time_get(), "Regions", stats.hblkhd);
	printf(MEM_FMT, tx_time_get(), "Usr Alloc", stats.uordblks);
//	printf(MEM_FMT, tx_time_get(), "UnAlloc Tot", stats.fordblks);
//	printf(MEM_FMT, tx_time_get(), "UnAlloc End", stats.fordblks);
	alloc_change = stats.uordblks - GLB_PrevTotAlloc;
	GLB_PrevTotAlloc = stats.uordblks;
	printf(MEM_FMT, tx_time_get(), "Alloc Chng", alloc_change);
}

static UINT GetThreadState(TX_THREAD *the_thread)
{
	CHAR *name;
	UINT state;
	ULONG run_count;
	UINT priority;
	UINT preemption_threshold;
	ULONG time_slice;
	TX_THREAD *next_thread;
	TX_THREAD *suspended_thread;

	UINT status;

	/* Retrieve information about the thread */
	status = tx_thread_info_get(the_thread,
															&name,
															&state,
															&run_count,
															&priority,
															&preemption_threshold,
															&time_slice,
															&next_thread,
															&suspended_thread);
	if (status != TX_SUCCESS) {
		printf("ERR:[%08ld] tx_thread_info_get() status:%d
", tx_time_get(), status);
		return(0xFFFFFFFF);
	}
	return (state);
}

static void ThreadFunction(ULONG initial_input)
{
//	printf("INF:[%08ld] ThreadFunction Started
", tx_time_get());
	tx_thread_sleep(200);
//	printf("INF:[%08ld] ThreadFunction Ended
", tx_time_get());
}

void MainApp(void)
{
	unsigned char ThreadStack[THREAD_STACK_SZ];
	TX_THREAD ThreadCtrlBlk = {0};
	UINT status;
	UINT thread_state;

	printf("________After Initialisation___________
");
	GetHeapInfo();

	while (1) {
		printf("_______________START___________________
");
		GetHeapInfo();

		/* Create the thread */
	  status = tx_thread_create(&ThreadCtrlBlk,	            /* control block for thread*/
	                          "A Thread",       		        /* thread name*/
	                          ThreadFunction,               /* entry function*/
	                          (ULONG)NULL,                  /* parameter*/
	                          ThreadStack,          				/* start of stack*/
	                          THREAD_STACK_SZ, 	            /* size of stack*/
	                          APP_DEFAULT_API_PRIORITY,     /* priority*/
	                          APP_DEFAULT_API_PRIORITY,     /* preemption threshold */
	                          1,                            /* time slice threshold*/
	                          TX_AUTO_START);               /* start immediately*/
		if (status != TX_SUCCESS) {
			printf("ERR:[%08ld] tx_thread_create() status:%d
", tx_time_get(), status);
			break;
		}

		/* Wait for thread to complete */
		while ((thread_state = GetThreadState(&ThreadCtrlBlk)) != TX_COMPLETED) {
			printf("INF:[%08ld] tx_thread_create() state:%d
", tx_time_get(), thread_state);
			tx_thread_sleep(10);
		}

		/* Delete the thread */
		status = tx_thread_delete(&ThreadCtrlBlk);
		if (status != TX_SUCCESS) {
			printf("ERR:[%08ld] tx_thread_delete() status:%d
", tx_time_get(), status);
			break;
		}

		tx_thread_sleep(100);
		printf("________________END____________________
");
	}
	printf("________________On Exit________________
");
	GetHeapInfo();
}

At one point Digi said that printf() wasn’t thread-safe.
My own usage of printf() is similar to yours (although I don’t, in general, dynamically create and delete threads), and I’ve not seen any problems related to use of printf().
I also monitor memory usage, and it generally settles down after a bit.
I wonder whether printf() has been made thread-safe with the creation of a buffer per thread, but this isn’t released when the thread is deleted.

Yes, I am also suspicious that this may be due to making printf thread safe. I use it quite extensively from multiple threads and have not seen any problems, when using a fixed number of static threads.

I did forget to say I am using the UDPDB interface rather than the serial interface, maybe I should check if the same thing occurs with serial.

I have check this using the serial port as the console and that has the same problem.

I then tried allocating a buffer myself and using sprintf() and puts(), but this also causes the problem, so it would appear to be due to the use of the stdout channel.

The final work around that I have come up with is to format the string in my own buffer and then use udpdb_write to directly send the data, rather than going via the standard device driver interface. I am not particularly happy with it as a solution but it does seem to work Ok.

There is (apparently) a know problem in the gcc library described at http://www.rtems.com/ml/rtems-users/2004/october/msg00317.html dealing with printf in combination with creating and deleting threads. In the entries below, the users that create threads but do not delete them will not see the issue. Since there are no known fixes, if you have found a method for getting around it using udpdb, then that is good.