Abstract

WIMSL: What I miss in the standard library.

Yes. I need the memory management and need it better, than the standard library provides.

Prologue

So, what’s exactly SL gives us. Two memory management functions sets.

The old C pair:

void * malloc(size_t len);

void free(void *mem);

The new C++ pair:

void * operator new[] (size_t len); // for   char *mem=new char[len];

void operator delete[] (void *mem); // for   delete[] mem;

In any sane implementation they are the same, except the new throws an exception, but malloc just returns 0.

What is missing

Oh, a lot of things!

Basic

Let’s start with the basic functionality. We will discuss the global heap functions, but the same is relevant to heap class objects.

alloc/free:

void * TryMemAlloc(ulen len) noexcept;

void * MemAlloc(ulen len);

void MemFree(void *mem); // mem may == 0

First of all, no size_t, please. ulen is a much better name for the practicall programming, where the good name selection does matter.

Second, we need two allocation functions. One throws an exception, and another returns null. An we need functions, not operators, clean and simple.

Protection

What if we pass a wrong argument to MemFree()? Undefine behavior ? No, thanks. Heap must have a defined behavior in this case: print a diagnostic message and then call abort(). Can we implement this? Yes, we can! Can we do it efficiently? Yes! We can do it too! So it must be a required property of the heap:

MemFree(mem) : if mem is not a valid argument, it prints a diagnostic message and then call abort()

Expand and shrink

extend/shrink:

bool MemExtend(void *mem,ulen len); // mem may == 0

bool MemShrink(void *mem,ulen len); // mem may == 0

This two function can be used to extend or shrink an allocated block of memory in-place.

Why these functions are required?

Consider a dynamic array class, like std::vector. This class allocates a memory block, then creates a range of elements withing. When you append the array with a new element, it must find a memory behind the last element. If there is no one, it must reallocate a bigger block of memory, then move elements there. This is expensive. So, why don’t try to extend the memory block in-place? It may fall, and in such case we do full reallocation.

Another case, if you have an array, you may have an extra memory, reserved behind the last element. But if you don’t need to extend the array anymore, you may shrink the memory block to release the extra memory.

There is C function realloc(), I haven’t mentioned before and for a reason. This function does some memory reallocation. But it does it in such a way, it cannot be used in C++ with objects of non-trivial types.

So we need a simple and clean solution like presented above.

Both these functions are efficient.

Memory usage statistics

stats:

struct MemStatData
 {
  ulen block_count; // the count of allocated memory blocks
  ulen len_count;   // the total allocated memory

  .... // some methods
 };

struct MemStat : MemStatData
 {
  MemStat();
 };

struct MemPeak : MemStatData
 {
  MemPeak();
 };

These set of functions (not a functions, ofc, but a class-functions) returns the heap statistic information.

What are they good for?

For information and for memory leak detection. It can help detect memory leaks during testing.

MemStat returns the current heap statistic.

MemPeak returns the peak heap statistic.

Both values are usefull.

Utilities

utilities:

ulen MemLen(const void *mem); // mem may == 0

void MemLim(ulen limit);

void GuardNoMem(ulen len);

And some utilities at last.

MemLen() determins the length of the memory block.

MemLim() limits the heap capacity. I’s very useful for testing. You can easily simulate the situation of memory exhausted.

GuardNoMem() throws a “no-memory” exception. It is used by MemAlloc() to throw an exception, and can be used in custom situations to implement the similar behavior.

CCore memory management

When I’ve started development of CCore, the first prioryty task was to design the proper memory management subsystem, instead of standard. Why? For several reasons.

For real-time systems we need a real-time heap. On bare-metal, heap lives in a predefine memory region and we can make it real-time. On normal OS, heap makes system calls to get a huge memory regions and then split them on smaller ones. So it can be made quasi-real-time.

On bare-metal, we are limited in resources, so extend and shrink functions are a big plus. Heap protection and statistic are also helpful in development of reliable software.