zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

Allocator.h (6506B)


      1 /*
      2   ZynAddSubFX - a software synthesizer
      3 
      4   Allocator.h - RT-Safe Memory Allocator
      5   Copyright (C) 2016 Mark McCurry
      6 
      7   This program is free software; you can redistribute it and/or
      8   modify it under the terms of the GNU General Public License
      9   as published by the Free Software Foundation; either version 2
     10   of the License, or (at your option) any later version.
     11 */
     12 #pragma once
     13 #include <cstdlib>
     14 #include <utility>
     15 #include <new>
     16 
     17 namespace zyn {
     18 
     19 //! Allocator Base class
     20 //! subclasses must specify allocation and deallocation
     21 class Allocator
     22 {
     23     public:
     24         Allocator(void);
     25         Allocator(const Allocator&) = delete;
     26         virtual ~Allocator(void);
     27 
     28         virtual void *alloc_mem(size_t mem_size) = 0;
     29         virtual void dealloc_mem(void *memory) = 0;
     30 
     31         /**
     32          * High level allocator method, which return a pointer to a class
     33          * or struct
     34          * allocated with the specialized subclass strategy
     35          * @param ts argument(s) for the constructor of the type T
     36          * @return a non null pointer to a new object of type T
     37          * @throw std::bad_alloc is no memory could be allocated
     38          */
     39         template <typename T, typename... Ts>
     40         T *alloc(Ts&&... ts)
     41         {
     42             void *data = alloc_mem(sizeof(T));
     43             if(!data) {
     44                 rollbackTransaction();
     45                 throw std::bad_alloc();
     46             }
     47             append_alloc_to_memory_transaction(data);
     48             return new (data) T(std::forward<Ts>(ts)...);
     49         }
     50 
     51         /**
     52          * High level allocator method, which return a pointer to an array of
     53          * class or struct
     54          * allocated with the specialized subclass strategy
     55          * @param len the array length
     56          * @param ts argument(s) for the constructor of the type T
     57          * @return a non null pointer to an array of new object(s) of type T
     58          * @throw std::bad_alloc is no memory could be allocated
     59          */
     60         template <typename T, typename... Ts>
     61         T *valloc(size_t len, Ts&&... ts)
     62         {
     63             T *data = (T*)alloc_mem(len*sizeof(T));
     64             if(!data && len != 0) {
     65                 rollbackTransaction();
     66                 throw std::bad_alloc();
     67             }
     68             append_alloc_to_memory_transaction(data);
     69             for(unsigned i=0; i<len; ++i)
     70                 new ((void*)&data[i]) T(std::forward<Ts>(ts)...);
     71 
     72             return data;
     73         }
     74 
     75         template <typename T>
     76         void dealloc(T*&t)
     77         {
     78             if(t) {
     79                 t->~T();
     80                 dealloc_mem((void*)t);
     81                 t = nullptr;
     82             }
     83         }
     84 
     85         //Destructor Free Version
     86         template <typename T>
     87         void devalloc(T*&t)
     88         {
     89             if(t) {
     90                 dealloc_mem(t);
     91                 t = nullptr;
     92             }
     93         }
     94 
     95         template <typename T>
     96         void devalloc(size_t elms, T*&t)
     97         {
     98             if(t) {
     99                 for(size_t i=0; i<elms; ++i)
    100                     (t+i)->~T();
    101 
    102                 dealloc_mem(t);
    103                 t = nullptr;
    104             }
    105         }
    106 
    107     void beginTransaction();
    108     void endTransaction();
    109 
    110     virtual void addMemory(void *, size_t mem_size) = 0;
    111 
    112     //Return true if the current pool cannot allocate n chunks of chunk_size
    113     virtual bool lowMemory(unsigned n, size_t chunk_size) const = 0;
    114     bool memFree(void *pool) const;
    115 
    116     //returns number of pools
    117     int memPools() const;
    118 
    119     int freePools() const;
    120 
    121     unsigned long long totalAlloced() const;
    122 
    123     struct AllocatorImpl *impl;
    124 
    125 private:
    126     const static size_t max_transaction_length = 256;
    127 
    128     void* transaction_alloc_content[max_transaction_length];
    129     size_t transaction_alloc_index;
    130     bool transaction_active;
    131 
    132     void rollbackTransaction();
    133 
    134     /**
    135      * Append memory block to the list of memory blocks allocated during this
    136      * transaction
    137      * @param new_memory pointer to the memory pointer to freshly allocated
    138      */
    139     void append_alloc_to_memory_transaction(void *new_memory) {
    140         if (transaction_active) {
    141             if (transaction_alloc_index < max_transaction_length) {
    142                 transaction_alloc_content[transaction_alloc_index++] = new_memory;
    143             }
    144             // TODO add log about transaction too long and memory transaction
    145             // safety net being disabled
    146         }
    147     }
    148 
    149 };
    150 
    151 //! the allocator for normal use
    152 class AllocatorClass : public Allocator
    153 {
    154     public:
    155         void *alloc_mem(size_t mem_size);
    156         void dealloc_mem(void *memory);
    157         void addMemory(void *, size_t mem_size);
    158         bool lowMemory(unsigned n, size_t chunk_size) const;
    159         using Allocator::Allocator;
    160 };
    161 typedef AllocatorClass Alloc;
    162 
    163 //! the dummy allocator, which does not allow any allocation
    164 class DummyAllocator : public Allocator
    165 {
    166     [[ noreturn ]] void not_allowed() const {
    167         throw "(de)allocation forbidden"; // TODO: std exception
    168     }
    169 public:
    170     void *alloc_mem(size_t ) { not_allowed(); }
    171     void dealloc_mem(void* ) { not_allowed(); } // TODO: more functions?
    172     void addMemory(void *, size_t ) { not_allowed(); }
    173     bool lowMemory(unsigned , size_t ) const { not_allowed(); }
    174     using Allocator::Allocator;
    175 };
    176 
    177 extern DummyAllocator DummyAlloc;
    178 
    179 /**
    180  * General notes on Memory Allocation Within ZynAddSubFX
    181  * -----------------------------------------------------
    182  *
    183  *  - Parameter Objects Are never allocated within the realtime thread
    184  *  - Effects, notes and note subcomponents must be allocated with an allocator
    185  *  - 5M Chunks are used to give the allocator the memory it wants
    186  *  - If there are 3 chunks that are unused then 1 will be deallocated
    187  *  - The system will request more allocated space if 5x 1MB chunks cannot be
    188  *    allocated at any given time (this is likely huge overkill, but if this is
    189  *    satisfied, then a lot of note spamming would be needed to run out of
    190  *    space)
    191  *
    192  *   - Things will get a bit weird around the effects due to how pointer swaps
    193  *     occur
    194  *     * When a new Part instance is provided it may or may not come with some
    195  *       instrument effects
    196  *     * Merging blocks is an option, but one that is not going to likely be
    197  *       implemented too soon, thus all effects need to be reallocated when the
    198  *       pointer swap occurs
    199  *     * The old effect is extracted from the manager
    200  *     * A new one is constructed with a deep copy
    201  *     * The old one is returned to middleware for deallocation
    202  */
    203 
    204  }