A Brief Summary of ODA Memory Allocation

Sergey Sherstnev

December 26, 2018

A Brief Summary of ODA Memory Allocation

The ODA continually looks at ways to advance and improve its technology. One example is with how it handles memory allocation.

In the past, ODA software allocated memory based on overridden new/delete global operators which were defined in the TD_Alloc module and exported from this module to every ODA project. The reason was simple: to provide the ability to catch memory leaks and write custom memory management for ODA members. However, this way was incorrect for the reasons detailed below.

  1. Noncompliance with C++ standards. A draft version of C++17 says:

    3.7.4.2., page 65

    “The library provides default definitions for the global allocation and deallocation functions. Some global allocation and deallocation functions are replaceable (18.6.1). A C++ program shall provide at most one definition of a replaceable allocation or deallocation function. Any such function definition replaces the default version provided in the library (17.6.4.6).”

    17.6.4.6., page 460

    “A C++ program may provide the definition for any of twelve dynamic memory allocation function signatures declared in header (3.7.4, 18.6): [...] The program’s definitions are used instead of the default versions supplied by the implementation (18.6).”

    The standard defines a "program" as such:

    “A program consists of one or more translation units (Clause 2) linked together.”

    Considering this, each DLL is a "program" so each DLL should have its own internal version of memory allocation operators.

  2. ODA members reported problems and questions, such as “random crashes, mismatch in allocation and deletion.” For example, see our forum (login required). In addition, many members had problems using third-party memory leak detectors with ODA modules.
  3. Loading an ODA library while more than one version of the same global allocation or deallocation operator has been defined is undefined behavior and may cause other problems.
  4. Internally, we periodically saw problems while using our own memory leak detector. Because allocation operators were overridden globally, there was a strong dependency link-order requirement in ODA projects. TD_Alloc should follow before any other library in the module dependencies to manage overriding the standard allocation operators. There was even a special script in OdaProjectGenerator to check the link order. Also there was an issue when adding newly sized deallocation operators (C++ 14 standard) because simply adding the "export" calling convention to these operators didn’t work, and this is why a previous workaround using the .def file was needed.

Refactoring memory allocation

During memory allocation refactoring, there were several goals (except eliminating issues described in the previous paragraph):

  1. Keep our existing functionality.
  2. Make the changes transparent for ODA developers: avoid long time requirements for rewriting our existing modules, such as adding allocation operators to each module separately.
  3. Make it easy to write the further modules/extensions using our allocators for ODA developers and ODA members.

So the following changes were done:

  1. New allocation operators were defined in the OdAllocOp.h file and added through the macro ODRX_DEFINE_DYNAMIC_MODULE for oda_tx and oda_txv modules. So, when ODA developers and ODA members create a new extension for an ODA-based application, there is no need to remember these operators at all (but the TD_Alloc module, which provides odrxAlloc/odrxFree functions, still must be linked to the module).
  2. A new module type oda_tx_component was added for support of DLL modules, which also has an ODRX_DEFINE_DYNAMIC_MODULE macro (mostly for internal use).
  3. For other DLL modules (oda_components), operators were added in the OdAllocOp.cpp file, which includes the same in OdAllocOp.h. At the generation stage, CMake parses projects and adds this file into components that have TD_Alloc in the dependencies.
  4. ODA library modules now can be independent from TD_Alloc (if odrxAlloc/odrxFree isn’t called from there directly).
  5. All of the above was done for dynamic configurations. But for executable modules (oda_executables), OdAllocOp.cpp now is added by cmake for both dynamic and static configurations.

How it works now

It’s simple: according to C++ standards, new/delete operators were defined for each ODA module independently. TD_Alloc still provides odrxAlloc/odrxFree functions for memory leak and memory management purposes, but it does not override global new/delete operators anymore.

Now to create an extension module, nothing is needed that wasn’t needed before. Also, if TD_Alloc isn’t added to the module dependencies, there will be a build error.

For other modules, it is necessary to add TD_Alloc to the module dependencies, and if ODAProjectGenerator isn’t used, to add OdAllocOp.cpp from Extensions/alloc to the project (if it is used, the file is added automatically).