The eXtremeSQL Memory Allocator in C++

For class McoSqlEngine, the class StdAllocator is used for all memory allocation. Applications that need to replace the built-in malloc()/free()-based allocator can explicitly create an allocator. Such a custom allocator must instantiate an object of the StdAllocator class passing the custom alloc()/free() functions to its constructor (or alternatively, one may subclass the SystemAllocator class). This must be done after the static construction of the default StdAllocator is finished.

Note that eXtremeSQL versions prior to 6.5 required the instantiation of a DynamicAllocator object to substitute a custom memory allocator. Version 6.5 and later do not contain this class. So applications built with a previous version of eXtremeSQL will need to be modified. For example, instead of

     
    DynamicAllocator allocator(mem_alloc, mem_free,
                    ALLOC_QUANTUM, ALLOC_RETAIN);
    engine.setAllocator(&allocator);
     

the custom allocator is simply instantiated in main() as follows:

     
    int main(int argc, char** argv)
    {
        ...
        StdAllocator allocator( mem_alloc, mem_free );
    …
    }
     

It is possible to limit the amount of memory that the SQL engine can use during the execution of a query, preventing the engine from drawing all available memory from the machine running the query.

The abstract class SystemAllocator used by all SQL allocators to allocate memory blocks, is defined as follows:

     
    class SystemAllocator
    {
        public:
        virtual void* allocate(size_t size) = 0;
        virtual void free(void* ptr) = 0;
        virtual size_t allocated() 
        {
            return 0;
        }
     
        SystemAllocator() 
        {
            implementation = this;
        }
        static SystemAllocator* implementation;
    };
     

The default implementation of the abstract class, StdAllocator, allows specifying the alloc() and free() interfaces and otherwise utilizes the standard C runtime malloc()/free() API. A new QuotaSysAllocator class extends the StdAllocator by keeping track of the allocated space. This class can be used to limit the amount of allocated memory, by use of the constructor as follows:

     
    QuotaSysAllocator(size_t quota, malloc_t malloc, free_t free) :
    StdAllocator(malloc,free), limit(quota), total(0){}
     

Here the quota is the size in bytes limiting the amount of allocated memory. If the limit is reached, the allocator's malloc() returns null. The second parameter is the function to get memory from the system and the third is the function to release the memory block. The signatures of these functions are:

     
    typedef void* (*malloc_t)(size_t size);
    typedef void  (*free_t)(void* ptr);
     

In order to redefine the system allocator the application creates an instance of QuotaSysAllocator. For example:

     
    main() 
    {
        new QuotaSysAllocator(10*1024*1024, malloc, free);
        ....
    }
     

Note that only one instance of the system allocator can be used by the application. If / when a query exceeds the memory limit, the following happens:

  1. QuotaSysAllocator::allocate() returns NULL;
  2. The engine's allocator detects the NULL and throws a NotEnoughMemory exception;
  3. The exception breaks the current SQL statement and the current transaction is rolled back (freeing all memory allocated by the transaction).

In addition to limiting the amount of memory used by the query, the QuotaSysAllocator can also be used by applications to request the used-memory statistics through the get_total_heap_size() SQL function.

Note that the QuotaSysAllocator is used implicitly when the sql_workspace_limit parameter is set in an xSQL configuration file (by default the limit is set to 0 indicating that there is no limit).

The QuotaSysAllocator functionality is not currently available as a part of the Java, C# or Python APIs.