The ASN.1/C++ runtime makes heavy use of dynamic memory, both for representation class instances and for temporary storage. For most platforms and applications, the standard C dynamic memory management (malloc/free/realloc) is good enough. However, on some platforms the standard memory management is relatively heavyweight (e.g., because of the need of inter-thread synchronization) and may prevent achieving the needed performance. There may also be some other situations where the application programmer would prefer non-standard, customized memory management.
As stated in Memory Allocation, the runtime operates representation class instances using the asn1Malloc(), asn1Free(), and asn1Realloc() functions. The encoder/decoder also uses these functions to allocate and deallocate temporary storage. These functions invoke the methods of the current memory system object. A memory system is a special C++ object that specifies the memory management strategy that the ASN.1/C++ Runtime uses when dealing with representation objects and temporary storage. At any given moment, there is only one memory system (the current memory system) that processes all memory management requests from the ASN.1/C++ runtime. The default memory system, available when the application starts, simply uses the standard C memory management (malloc/free/realloc).
To change the memory management, you should create another memory system object. The constructor of the memory system object automatically makes it current; its destructor restores the previous current memory system. Therefore, you may create a stack of memory systems; however, we expect there is rarely a need to do so, if ever.
The current memory system pointer is global to all application threads. It is assumed that the custom memory system is set at the start of the application, before creating multiple threads. Access to the current memory system is not synchronized. If you plan to change the memory system when multiple threads are running, you should implement your own synchronization, however, we do not recommend changing memory systems in multi-threading mode.
If you allocate an object using a particular memory system, you must deallocate it using the same memory system. In particular, it means that if you initialize some representation objects before changing the memory system (e.g., statically), you should not deallocate them, change them, or use them in ownership-transferring functions until the new memory system is destroyed. A simpler rule is this: if you change the memory system, do so at the very beginning of the program, before starting multiple threads, and do not use static representation objects.
The ASN.1/C++ runtime provides two memory systems: the default memory system that you need not create explicitly and the memory pool-enabled memory system. If you are using the latter, you can take advantage of memory pools.
A memory pool is a collection of memory chunks, the size of each being equal or greater than some specified value, allocated for exclusive use by one application thread. A thread may use more than one memory pool, but only one pool is active at a time and that pool is used for the allocation of new objects by that thread. If all of the existing pool's memory is exhausted, an additional memory chunk is requested from the system using malloc. Memory allocated to a pool can be cleaned at once and then reused, but is never returned to the system except when the pool is destroyed.
What is the reason for such a mode of operation? On some platforms the standard malloc/free are relatively expensive operations, especially in a multithreaded application, when it should perform inter-thread synchronization to avoid race conditions between threads competing for memory resources. The application may achieve better performance by requesting a big chunk of memory for each thread and allocating smaller memory blocks by splitting the preallocated big chunk. Memory pools provide this optimization almost transparently for the application programmer.
If you are using several memory pools in one thread, you may safely mix memory allocated from different pools. For example, you may insert (using an ownership-transferring function) an object that was allocated in one pool into another object that was allocated in another pool. However, you cannot mix objects allocated from pools belonging to different threads. Doing so may result in memory corruption because different threads are competing for one memory pool.
The ASN.1/C++ runtime also supports the earlier method of customizing memory management - providing special implementation of the asn1Malloc(), asn1Free(), and asn1Realloc() functions to replace the standard implementation. In versions prior to 4.0.1, this was the only way to implement custom memory management. To use this method, you simply write the three functions as you wish and specify the object file in the linker command line before the ASN.1/C++ Runtime library, so the linker uses your object file instead of the version from the runtime library.
However, this approach has two important disadvantages:
The preferred way to customize memory management is to implement your own memory system. Then you will avoid those disadvantages.
The OssMemorySystem class is an abstract class that is a common ancestor of all memory systems.
class OssMemorySystem { protected: OssMemorySystem *previous; OssMemorySystem(); public: virtual ~OssMemorySystem(); static OssMemorySystem *get_current(); virtual void *base_malloc(size_t size); virtual void base_free(void *ptr); virtual void *base_realloc(void *ptr, size_t oldsize, size_t size); virtual void *asn1_malloc(size_t size) = 0; virtual void asn1_free(void *ptr) = 0; virtual void *asn1_realloc(void *ptr, size_t oldsize, size_t size) = 0; };
The OssDefaultMemorySystem class is the default memory system. It is available without any action by the application programmer. You don't need to create instances of OssDefaultMemorySystem.
class OssDefaultMemorySystem { public: OssDefaultMemorySystem(); public: virtual void *asn1_malloc(size_t size); virtual void asn1_free(void *ptr); virtual void *asn1_realloc(void *ptr, size_t oldsize, size_t size); };
The OssPoolMemorySystem class is the memory system that provides memory pools. To use memory pools, you simply create an instance of OssPoolMemorySystem in the beginning of the application, before creating additional threads:
OssPoolMemorySystem sys;
After the memory system has been created, you may start creating and using memory pool objects as needed.
When OssPoolMemorySystem is active, it tracks the usage of memory pools by different threads. If the current thread has some memory pool active, asn1Malloc uses this pool for memory allocation. If the current thread has no active memory pool, it uses simple malloc. The memory system also keeps track of the memory pool that an allocated memory block belongs to. When the block is deallocated, it is deallocated by the same memory pool it was allocated from, regardless of which pool is now active for the current thread. However, make sure that the memory pool belongs to the same thread!
NOTE: If you allocated an object in a memory pool, deallocate it in the same thread that allocated it.
Although OssPoolMemorySystem may be used without using memory pools, do not do so. You will get the same behavior as the default, except with larger memory requirements due to OssPoolMemorySystem's overhead.
class OssPoolMemorySystem { public: OssPoolMemorySystem(); public: virtual void *asn1_malloc(size_t size); virtual void asn1_free(void *ptr); virtual void *asn1_realloc(void *ptr, size_t oldsize, size_t size); };
The OssMemoryPool class is an abstract class that is a common ancestor of all memory pool classes. Currently, there is only one concrete memory pool class - OssSimpleMemoryPool. Upcoming versions may contain other implementations of the OssMemoryPool interface.
class OssMemoryPool { public: THREADID get_thread(); int activate(); int deactivate(); virtual void *alloc(size_t size) = 0; virtual void clean() = 0; virtual void dealloc(void *ptr) = 0; virtual ~OssMemoryPool(); };
The OssSimpleMemoryPool class is an concrete memory pool class that is tied to a single thread during its lifecycle. Currently, it is the only concrete memory pool class, but upcoming versions may contain other implementations of the OssMemoryPool interface.
class OssSimpleMemoryPool { public: OssSimpleMemoryPool(size_t chunk_size); virtual void *alloc(size_t size); virtual void clean(); virtual void dealloc(void *ptr); };
This documentation applies to release 7.3 and later of the OSS® ASN.1 Tools for C++.
Copyright © 2024 OSS Nokalva, Inc. All rights reserved.
No part of this publication may be reproduced, stored in a retrieval system, or transmitted in any form or by any means electronic, mechanical, photocopying, recording or otherwise, without the prior permission of OSS Nokalva, Inc.
Every distributed copy of the OSS® ASN.1 Tools for C++ is associated with a specific license and related unique license number. That license determines, among other things, what functions of the OSS ASN.1 Tools for C++ are available to you.