For Real-Time System Developers, "Dynamic" is a Dirty Word

May 15, 2019

Blog

For Real-Time System Developers, "Dynamic" is a Dirty Word

There is no keyword dynamic (in C/C++), but the word is widely used to describe particular behaviors and should strike a chill in the heart of any real-time system developers.

There are two words that show up in various technical contexts and have a variety of meanings: “static” and “dynamic.” Even within a single discipline, they can have a variety of meanings. For example, focusing on software, in C/C++, the keyword static has a number of essentially unrelated meanings. There is no keyword dynamic (in C/C++), but the word is widely used to describe particular behaviors and should strike a chill in the heart of any real-time system developers.

In software, dynamic behavior is when something can be created and destroyed as required. A real-time operating system (RTOS) is likely to have service calls that enable RTOS objects (tasks, queues, mailboxes, semaphores, etc.) to be created when required and discarded after use. This is of questionable utility; early RTOSes were purely static and I am not sure that this was not a better way to build an OS.

A much simpler example of dynamic behavior is memory allocation. In broad terms, I would say that dynamic allocation of memory in a real time system is a very bad idea. This is for two key reasons, both associated with the standard allocation function malloc(). First, an allocation call is non-deterministic - never good news when predictability of performance is a key requirement. Second, an allocation request may fail in a somewhat unpredictable manner. This is because of heap fragmentation - there is plenty of free memory available, but no contiguous block is large enough for a given request. In this example, there is 6K of free memory, but a request for a block larger than 3K will fail.

Of course, there is a solution. Just about every real time operating system provides facilities for the deterministic allocation of fixed size memory blocks. You simply set up a "pool" of blocks during initialization and request and release blocks as required. The allocation time is predictable and the failure mode well defined and controlled. Of course, fixed size blocks sound restricting, but that is not the case for many types of applications. If you really want to have something that looks like malloc(), you can easily write one. All that is needed is a set of memory pools with exponentially increasing block size. The new allocation function simply requests a large enough block from the appropriate pool.

In C++, the new operator essentially hides a call to malloc(), so rewriting this function addresses the issue. Another can be more efficient. When you instantiate a C++ class [for struct], the new operator is used to allocate space for its data. When you define a class, you can overload new to make it behave in a class-specific way. A good approach is to define a memory pool for each class, with blocks of precisely the required size. The size of the pools (i.e. number of blocks) depends on the usage of the class but can be determined quite straightforwardly.

So, maybe dynamic is not always bad …