malloc hangs with RTOS

02 Aug 2012

I tried to migrate a working project to mbed RTOS as it's complexity has reached a level that requires threads and synchronization. Unfortunately my application stopped working after importing mbed-rtos library. After investiging the problem I found out that malloc hangs at places where it wouldn't earlier.

Any hints on how to debug the problem? I searched for rtos specific memory handling issues, but could not find any.

I also made simple test cases to simplify the problem and I found that when there is not enough free memory then malloc freezes (causes hard fault) instead of returning null as it does without rtos. But that's not the only case when it hangs. Sometimes it even hangs when I call free to deallocate memory previously allocated with malloc.

Is it possible to write my own heap management functions for mbed-rtos similar to FreeRTOS?

02 Aug 2012

Sheldon Cooper wrote:

I tried to migrate a working project to mbed RTOS as it's complexity has reached a level that requires threads and synchronization. Unfortunately my application stopped working after importing mbed-rtos library. After investiging the problem I found out that malloc hangs at places where it wouldn't earlier.

Any hints on how to debug the problem? I searched for rtos specific memory handling issues, but could not find any.

I also made simple test cases to simplify the problem and I found that when there is not enough free memory then malloc freezes (causes hard fault) instead of returning null as it does without rtos. But that's not the only case when it hangs. Sometimes it even hangs when I call free to deallocate memory previously allocated with malloc.

This is a known issue we need to fix.

The standard library heap/stack collision algorithm is using "_current_sp()" to verify that the stack pointer is always above the heap pointer.

This is working fine with the normal mbed Memory Model where there is a single stack positioned above the heap.

This stopped working with the RTOS Memory Model where many stacks are allocated in the zero initialized memory region below the heap.

The solution is "simple": we need to write a new heap/stack collision detection algorithm that will check that the heap pointer is below the main stack ponter, not the _current_sp().

For the moment, lacking time (I am currently completing the work on the new networking libraries), I disabled this check. Therefore an application, instead of failing immediately at startup, will fail only when all the memory has been exhausted.

Sheldon Cooper wrote:

Is it possible to write my own heap management functions for mbed-rtos similar to FreeRTOS?

Yes, it is possible and in fact this is what we are going to do in the following weeks.

If you need a solution sooner, you can take a look at the standard library documentation: Tailoring the runtime memory model.

HTH, Emilio

03 Aug 2012

Thanks for the info. Just to clarify, does it mean that RTOS is broken in the current state and should not be used, or is it safe to use in an application that requires less memory.

Emilio Monti wrote:

Sheldon Cooper wrote:

Is it possible to write my own heap management functions for mbed-rtos similar to FreeRTOS?

Yes, it is possible and in fact this is what we are going to do in the following weeks.

If you need a solution sooner, you can take a look at the standard library documentation: Tailoring the runtime memory model.

HTH, Emilio

Before going too deep into understanding the memory model I'd like to know if it is possible to tailor the memory model from a user application or is it possible only from the mbed core library?

03 Aug 2012

Sheldon Cooper wrote:

does it mean that RTOS is broken in the current state and should not be used, or is it safe to use in an application that requires less memory.

The RTOS is perfectly safe to be used as long as you do not exhaust the RAM. When the RAM is exhausted, a proper heap/stack collision detection will only change how the failure is reported.

Sheldon Cooper wrote:

Before going too deep into understanding the memory model I'd like to know if it is possible to tailor the memory model from a user application or is it possible only from the mbed core library?

The tailoring of the runtime memory model is done overriding certain weak symbols in the C standard library.

You can override these symbols in any of your objects, there is nothing especial about the mbed core library.

For example, we are going to override these symbols in the open source rtx library.

Cheers, Emilio

19 May 2014

Hi Sheldon,

Could you provide any code which could reproduce the problem? Could you please say what board have you used and what version of the library?

21 May 2016

I should have run a quick test - here are links to similar discussions, one of which I posted a small sample:

In the first link, you'll see my test program. Here are the lines of interest. I swap the active for the commented lines and rebuild.

        char * p = new char [i];      // This never returns after 24000
        //char * p = (char *)malloc(i);     // This fails gracefully with NULL return after 24000

This test did not include the rtos.

09 Jun 2014

Hi,

I am sorry for replying so late.

The problem is that mbed is not supporting C++ exceptions and the libstdc++ library which is linked against is compiled using exceptions. Whenever you are trying to use new keyword and there is not enough memory for allocation new object the system tries to throw std::bad_alloc exception. This is why there is incompatibility at this moment between the mbed compiler configuration and the c++ library. As we didn't decide yet what will be the long term solution of this problem withing mbed IDE I would like to give you two short term solutions which you can include in your projects.

1. Implement your own new and delete operators.

void* operator new(size_t size) throw()
{   
	void *ptr;

	ptr = malloc(size);
	
	return ptr;
}

void operator delete(void *ptr) throw()
{
   free(ptr);
}

void operator delete[](void *ptr) throw()
{
   free(ptr);
}

2. Use 'nothrow' version of new:

#include <new>

unsigned char* EnormousAllocation(void)
{
  unsigned char *buffer = new (std::nothrow) unsigned char[1000000];

  return buffer;
}

Please let me know did it help.

11 Jun 2014

Hi Damian,

I applied my own new/delete as you suggested to a simple test program.

This does indeed keep it from failing badly as it did before, but oddly showed me something new - perhaps you will understand it better than I.

My test program simply eats memory to exhaustion (but not to failure). Part way through the process, the available memory *increased*, as oddly as that sounds. I am first wondering if it was printf, but it struck me as odd. Perhaps it is my test for free memory that is tripping it up. Here's the printout, see about half-way down where it reports 7030 bytes free, and then reports 11292 bytes free. I'm sure it must be something obvious I am overlooking.

Blinky eating memory - Build Jun 11 2014 10:54:16
  20556 bytes free
Allocating 2000 bytes in addition to 0 already allocated.
  18548 bytes free.
  Success!
Allocating 2000 bytes in addition to 2000 already allocated.
  16540 bytes free.
  Success!
Allocating 2000 bytes in addition to 4000 already allocated.
  14532 bytes free.
  Success!
Allocating 2000 bytes in addition to 6000 already allocated.
  12524 bytes free.
  Success!
Allocating 2000 bytes in addition to 8000 already allocated.
  10516 bytes free.
  Success!
Allocating 2000 bytes in addition to 10000 already allocated.
  8508 bytes free.
  Success!
Allocating 2000 bytes in addition to 12000 already allocated.
  7030 bytes free.
  Success!
Allocating 2000 bytes in addition to 14000 already allocated.
  11292 bytes free.
  Success!
Allocating 2000 bytes in addition to 16000 already allocated.
  9284 bytes free.
  Success!
Allocating 2000 bytes in addition to 18000 already allocated.
  7276 bytes free.
  Success!
Allocating 2000 bytes in addition to 20000 already allocated.
  5268 bytes free.
  Success!
Allocating 2000 bytes in addition to 22000 already allocated.
  3260 bytes free.
  Success!
Allocating 2000 bytes in addition to 24000 already allocated.
  1252 bytes free.
  Success!
Allocating 2000 bytes in addition to 26000 already allocated.
  1252 bytes free.
  Failed...
done.
11 Jun 2014

Hi David,

From what I see your Free() function is returning information what currently is the maximum monolithic piece of memory that you can allocate. If there is any memory fragmentation in the heap or malloc uses some sophisticated algorithms for speeding up the allocation then you can have situation presented by your program. I am guessing that it just shows a result of some kind of heap management and fragmentation in a given malloc implementation. I would not be too worried about it.

To see it try to add to your program two global static pointers:

static char * buffer_a = 0;
static char * buffer_b = 0;

and then the first thing in your main function place this code:

buffer_a = new char [100];
buffer_b = new char [1000];
delete buffer_a;
delete buffer_b;

run it again and show to us the results.

If the listing won't change try increasing 5 times allocated values for buffer_a and buffer_b.

12 Jun 2014

Hi Damian, I followed your suggestion with the pair of new/delete, and see exactly the same output as before (other than the Build date changed to confirm the program indeed updated).

And you were of course absolutely right, my "Free()" function indeed finds only the single largest chunk. I have seen references to reports that can be extracted about the allocated memory, but I was not successful in implementing them. If you know of a method that works, I would really like to create/see a complete report of each allocated block, the size of the block, the size of fragmented free sections, and perhaps the first dozen bytes in hex for each block.

Something like:

U/F Base (0x)    Size (X)   Contents
U:  10000440         64     23 3A 45 8B 30 31 FE ...
U:  100004A8        3E8     00 12 16 9A C3 47 12 ...
F:  10000894       8000     45 41 4C 52 37 92 15 ...
12 Jun 2014

David,

Can you make one more test but changing the first four lines in your main function to:

buffer_a = new char [1000];
buffer_b = new char [15000];
delete buffer_a;
delete buffer_b;
12 Jun 2014

Sure thing Damian, Here's the results.

Blinky eating memory - Build Jun 12 2014 22:46:24
  23060 bytes free
Allocating 2000 bytes in addition to 0 already allocated.
  21052 bytes free.
  Success!
Allocating 2000 bytes in addition to 2000 already allocated.
  19044 bytes free.
  Success!
Allocating 2000 bytes in addition to 4000 already allocated.
  17036 bytes free.
  Success!
Allocating 2000 bytes in addition to 6000 already allocated.
  15028 bytes free.
  Success!
Allocating 2000 bytes in addition to 8000 already allocated.
  13020 bytes free.
  Success!
Allocating 2000 bytes in addition to 10000 already allocated.
  11012 bytes free.
  Success!
Allocating 2000 bytes in addition to 12000 already allocated.
  9004 bytes free.
  Success!
Allocating 2000 bytes in addition to 14000 already allocated.
  6996 bytes free.
  Success!
Allocating 2000 bytes in addition to 16000 already allocated.
  4988 bytes free.
  Success!
Allocating 2000 bytes in addition to 18000 already allocated.
  6249 bytes free.
  Success!
Allocating 2000 bytes in addition to 20000 already allocated.
  4764 bytes free.
  Success!
Allocating 2000 bytes in addition to 22000 already allocated.
  2756 bytes free.
  Success!
Allocating 2000 bytes in addition to 24000 already allocated.
  748 bytes free.
  Success!
Allocating 2000 bytes in addition to 26000 already allocated.
  748 bytes free.
  Failed...
done.

So, now I see that pushed the "reverse step" down a little. It goes from 4988 to 6249 then 4764. I guess you have something in mind here?

12 Jun 2014

Hi Dave,

I just wanted to show something by this test. By allocating this memory at the beginning of the main function and then releasing it you could think that this should not change the structure of memory heap. This could be true for some memory allocation algorithms. Here you can see that after making this step the first line of your console listing shows that there is bigger monolithic block of memory available for allocation (23060 vs 20556). The reason behind that is that depending of the way of the allocator works it can be optimized for space, speed or partially for both. During memory allocation and releasing the code responsible for managing the heap can dynamically change the strategy of heap management. This could be done only when you will 'trigger' this dynamic optimization by allocating the memory in some special way (like for example here by allocating big block at the beginning). This strategy change could be triggered even in the middle of the loop in your Free() function! If you would add to your code functionality which will be measuring the speed of allocation then this would be much more interesting!

Regarding your question about extracting all information about the heap structure unfortunately I won't be able to help you here.

BTW. I have tested your code on GCC compiler and the console listing changes when much less memory is allocated at the beginning of the main function (100 and 1000 instead of 1000 and 15000) using LPC1768 board.