pool_allocator.hh

00001 // This file is a part of Aurelia.
00002 // Copyright (C) 2010  Valentin David
00003 // Copyright (C) 2010  University of Bergen
00004 //
00005 // This program is free software: you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation, either version 3 of the License, or
00008 // (at your option) any later version.
00009 //
00010 // This program is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 // GNU General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License
00016 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017 
00018 #ifndef __POOL_ALLOCATOR_HH
00019 # define __POOL_ALLOCATOR_HH
00020 
00021 # include <aurelia-config.hh>
00022 
00023 # include <atomic>
00024 # include "../lockfree/simple_stack.hh"
00025 
00026 # ifdef HAVE_VALGRIND_VALGRIND_H
00027 #  pragma GCC diagnostic push
00028 #  pragma GCC diagnostic ignored "-Wunused-but-set-variable"
00029 #  pragma GCC diagnostic ignored "-Wunused-value"
00030 #  include <valgrind/valgrind.h>
00031 #  include <valgrind/memcheck.h>
00032 # else
00033 #  define VALGRIND_CREATE_MEMPOOL(X, Y, Z)
00034 #  define VALGRIND_MAKE_MEM_NOACCESS(X, Y)
00035 #  define VALGRIND_MEMPOOL_ALLOC(X, Y, Z)
00036 #  define VALGRIND_MEMPOOL_FREE(X, Y)
00037 #  define VALGRIND_MAKE_MEM_DEFINED(X, Y)
00038 #  define VALGRIND_MAKE_MEM_UNDEFINED(X, Y)
00039 # endif
00040 
00041 # include <cstdlib>
00042 # include <memory>
00043 
00044 namespace aurelia {
00045 
00046   template <int SIZE>
00047   struct block_list {
00048   private:
00049     lockfree::simple_stack<void*> blocks;
00050 
00051   public:
00052     block_list() {}
00053     block_list(const block_list&) = delete;
00054 
00055     void* new_block() {
00056       void *p = malloc(SIZE);
00057       if (p == NULL)
00058         throw std::bad_alloc();
00059       blocks.push(p);
00060       return p;
00061     }
00062 
00063     ~block_list() {
00064       while (true) {
00065         lockfree::simple_stack<void*>::wrapped w = blocks.pop();
00066         if (w.end())
00067           return ;
00068         free(*w);
00069       }
00070     }
00071   };
00072 
00073   template <typename T,
00074             int POOL_SIZE = 4096>
00075   struct chunk_alloc {
00076   private:
00077     static const void* mempool() {
00078       static bool init = false;
00079       if (!init) {
00080         init = true;
00081         VALGRIND_CREATE_MEMPOOL(&typeid(chunk_alloc), 0, false);
00082       }
00083       return &typeid(chunk_alloc);
00084     }
00085 
00086     union block {
00087       typename std::aligned_storage<sizeof(T),
00088                                     std::alignment_of<T>::value>::type obj;
00089       block *next;
00090     };
00091 
00092     static block*& free() {
00093       thread_local block* _free = NULL;
00094       return _free;
00095     }
00096 
00097     static block_list<sizeof(block)*POOL_SIZE>& blocks() {
00098       static block_list<sizeof(block)*POOL_SIZE> _blocks;
00099       return _blocks;
00100     }
00101 
00102     static block* ensure_blocks() {
00103       block* f = free();
00104       if (f == NULL) {
00105         f = (block*)blocks().new_block();
00106         free() = f;
00107         for (int i = 0; i < (POOL_SIZE - 1); ++i) {
00108           f[i].next = f + i + 1;
00109         }
00110         f[POOL_SIZE-1].next = NULL;
00111         VALGRIND_MAKE_MEM_NOACCESS(f, sizeof(block)*POOL_SIZE);
00112       }
00113       return f;
00114     }
00115 
00116   public:
00117     static void *alloc() {
00118       block* n = ensure_blocks();
00119       VALGRIND_MEMPOOL_ALLOC(mempool(), n, sizeof(block));
00120       VALGRIND_MAKE_MEM_DEFINED(&(n->next), sizeof(block*));
00121       free() = n->next;
00122       VALGRIND_MAKE_MEM_UNDEFINED(&(n->next), sizeof(block*));
00123       return (void*)&(n->obj);
00124     }
00125 
00126     static size_t offset() {
00127       block n;
00128       return ((char*)&n.obj - (char*)&n);
00129     }
00130 
00131     static void del(void *p) {
00132       block *n = (block*)(((char*)p) - offset());
00133       n->next = free();
00134       VALGRIND_MEMPOOL_FREE(mempool(), n);
00135       free() = n;
00136     }
00137   };
00138 
00139   template <typename T,
00140             int POOL_SIZE = 4096,
00141             typename RealAlloc = chunk_alloc<T, POOL_SIZE> >
00142   struct pool_allocator {
00143     typedef size_t size_type;
00144     typedef T* pointer;
00145     typedef T& reference;
00146     typedef const T* const_pointer;
00147     typedef const T& const_reference;
00148     typedef T value_type;
00149 
00150     template <typename U>
00151     struct rebind {
00152       typedef pool_allocator<U, POOL_SIZE> other;
00153     };
00154 
00155     template <typename U, int OTHERSIZE>
00156     pool_allocator(const pool_allocator<U, OTHERSIZE>&) {}
00157     pool_allocator() {}
00158 
00159     pointer allocate(size_type n) {
00160       if (n == 1)
00161         return (pointer)RealAlloc::alloc();
00162       std::allocator<T> a;
00163       return a.allocate(n);
00164     }
00165 
00166     void deallocate(pointer p, size_type n) {
00167       if (n == 1)
00168         return RealAlloc::del(p);
00169       std::allocator<T> a;
00170       a.deallocate(p, n);
00171     }
00172 
00173     void destroy(pointer p) {
00174       p->~T();
00175     }
00176 
00177     template <typename... Args>
00178     void construct(pointer p, Args... args...) {
00179       new (p) T(std::forward<Args>(args)...);
00180     }
00181   };
00182 
00183 }
00184 
00185 # ifdef HAVE_VALGRIND_VALGRIND_H
00186 #  pragma GCC diagnostic pop
00187 # endif
00188 
00189 #endif