type_pool  1.2
All Classes Files Functions Typedefs
include/type_pool.h
Go to the documentation of this file.
00001 /*
00002     (C) Copyright Thierry Seegers 2012. Distributed under the following license:
00003 
00004     Boost Software License - Version 1.0 - August 17th, 2003
00005 
00006     Permission is hereby granted, free of charge, to any person or organization
00007     obtaining a copy of the software and accompanying documentation covered by
00008     this license (the "Software") to use, reproduce, display, distribute,
00009     execute, and transmit the Software, and to prepare derivative works of the
00010     Software, and to permit third-parties to whom the Software is furnished to
00011     do so, all subject to the following:
00012 
00013     The copyright notices in the Software and this entire statement, including
00014     the above license grant, this restriction and the following disclaimer,
00015     must be included in all copies of the Software, in whole or in part, and
00016     all derivative works of the Software, unless such copies or derivative
00017     works are solely in the form of machine-executable object code generated by
00018     a source language processor.
00019 
00020     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00021     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00022     FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
00023     SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
00024     FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
00025     ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00026     DEALINGS IN THE SOFTWARE.
00027 */
00028 
00029 #if !defined(TYPEPOOL_H)
00030      #define TYPEPOOL_H
00031 
00032 #include <algorithm>
00033 #include <atomic>
00034 #include <functional>
00035 #include <future>
00036 #include <list>
00037 #include <memory>
00038 #include <mutex>
00039 #include <utility>
00040 
00042 #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
00043 
00044 
00052 template<size_t S, class Allocator = std::allocator<unsigned char[S]>>
00053 class chunk_pool
00054 {
00055     Allocator allocator;
00056     size_t cap;
00057     std::atomic<bool> empty;
00058     std::mutex m;
00059     std::future<void> preallocation;
00060 
00061     typedef std::list<typename Allocator::pointer> store_t;
00062     store_t store;
00063     
00064     // Disabled copy construction.
00065     chunk_pool(const chunk_pool&);
00066     chunk_pool(chunk_pool&&);
00067 
00068     // Disabled assignment.
00069     chunk_pool& operator=(const chunk_pool&);
00070     chunk_pool& operator=(chunk_pool&&);
00071 
00072 protected:
00074     static size_t max_size()
00075     {
00076         store_t s;
00077         return s.max_size();
00078     }
00079 
00080 public:
00082     typedef Allocator allocator_type;
00083 
00085     typedef typename allocator_type::pointer pointer;
00086 
00094     chunk_pool(size_t preallocate = 0, size_t cap = max_size()) : cap(std::min(cap, max_size()))
00095     {
00096         preallocate = std::min(preallocate, cap);
00097 
00098         empty = (preallocate == 0);
00099 
00100         preallocation = std::async([preallocate, this]() mutable -> void
00101         {
00102             try
00103             {
00104                 while(preallocate--)
00105                 {
00106                     store.push_back(this->allocator.allocate(1));
00107                 }
00108             }
00109             catch(...) {}
00110         });
00111     }
00112 
00116     virtual ~chunk_pool()
00117     {
00118         preallocation.wait();
00119         std::for_each(store.begin(), store.end(), std::bind(&allocator_type::deallocate, allocator, std::placeholders::_1, S));
00120     }
00121 
00126     pointer allocate()
00127     {
00128         if(empty)
00129         {
00130             return allocator.allocate(1);
00131         }
00132         else
00133         {
00134             m.lock();
00135 
00136             preallocation.wait();
00137             empty = store.empty();
00138             
00139             if(!empty)
00140             {
00141                 pointer p = store.back();
00142                 store.pop_back();
00143                 empty = store.empty();
00144 
00145                 m.unlock();
00146 
00147                 return p;
00148             }
00149             else
00150             {
00151                 m.unlock();
00152 
00153                 return allocator.allocate(1);
00154             }
00155         }
00156     }
00157 
00163    void release(typename Allocator::pointer p)
00164     {
00165         m.lock();
00166 
00167         if(store.size() < cap)
00168         {
00169             store.push_back(p);
00170             empty = false;
00171         }
00172         else
00173         {
00174             allocator.deallocate(p, S);
00175         }
00176 
00177         m.unlock();
00178     }
00179 };
00180 
00200 template<typename T, class Allocator = typename chunk_pool<sizeof(T)>::allocator_type>
00201 class type_pool : public chunk_pool<sizeof(T), Allocator>
00202 {
00203     void release(T* p)
00204     {
00205         p->~T();
00206 
00207         chunk_pool<sizeof(T), Allocator>::release(reinterpret_cast<typename chunk_pool<sizeof(T), Allocator>::pointer>(p));
00208     }
00209 
00210 public:
00212     typedef typename std::unique_ptr<T, std::function<void (T*)>> unique_ptr;
00213 
00218     type_pool(size_t preallocate = 0, size_t cap = chunk_pool<sizeof(T), Allocator>::max_size()) : chunk_pool<sizeof(T), Allocator>(preallocate, cap)
00219     {}
00220 
00225     template<typename U>
00226     typename std::enable_if<std::is_same<T, U>::value, T*>::type get_ptr(U&& t)
00227     {
00228         return new(chunk_pool<sizeof(T), Allocator>::allocate()) T(std::move(t));
00229     }
00230 
00231 #if (defined(__GNUG__) && (GCC_VERSION >= 40400))
00232 
00233 
00234 
00235 
00236 
00237 
00238     template<typename U, typename... Args>
00239     typename std::enable_if<std::is_same<T, U>::value, T*>::type get_ptr(Args&&... args)
00240     {
00241         return new(chunk_pool<sizeof(T), Allocator>::allocate()) T(std::forward<Args>(args)...);
00242     }
00243 #endif
00244 
00248     template<typename U>
00249     typename std::enable_if<std::is_same<T, U>::value, std::shared_ptr<T>>::type get_shared(U&& t)
00250     {
00251         return std::shared_ptr<T>(get_ptr(std::move(t)), std::bind1st(std::mem_fun(&type_pool<T, Allocator>::release), this));
00252     }
00253 
00254 #if (defined(__GNUG__) && (GCC_VERSION >= 40400))
00255 
00256 
00257 
00258 
00259 
00260     template<typename U, typename... Args>
00261     typename std::enable_if<std::is_same<T, U>::value, std::shared_ptr<T>>::type get_shared(Args&&... args)
00262     {
00263         return std::shared_ptr<T>(get_ptr<T>(std::forward<Args>(args)...), std::bind1st(std::mem_fun(&type_pool<T, Allocator>::release), this));
00264     }
00265 #endif
00266 
00271     template<typename U>
00272     typename std::enable_if<std::is_same<T, U>::value, typename type_pool<T, Allocator>::unique_ptr>::type get_unique(U&& t)
00273     {
00274         return typename type_pool<T, Allocator>::unique_ptr(get_ptr(std::move(t)), std::bind1st(std::mem_fun(&type_pool<T, Allocator>::release), this));
00275     }
00276 
00277 #if (defined(__GNUG__) && (GCC_VERSION >= 40400))
00278 
00279 
00280 
00281 
00282 
00283 
00284     template<typename U, typename... Args>
00285     typename std::enable_if<std::is_same<T, U>::value, typename type_pool<T, Allocator>::unique_ptr>::type get_unique(Args&&... args)
00286     {
00287         return typename type_pool<T, Allocator>::unique_ptr(get_ptr<T>(std::forward<Args>(args)...), std::bind1st(std::mem_fun(&type_pool<T, Allocator>::release), this));
00288     }
00289 #endif
00290 };
00291 
00292 #endif
00293