diff --git a/src/nrncvode/hocevent.cpp b/src/nrncvode/hocevent.cpp index 5654ea0b7d..b1ecbb9360 100644 --- a/src/nrncvode/hocevent.cpp +++ b/src/nrncvode/hocevent.cpp @@ -1,10 +1,10 @@ #include -#include +#include #include #include #include -using HocEventPool = MutexPool; +using HocEventPool = MutexedPool; HocEventPool* HocEvent::hepool_; HocEvent::HocEvent() { @@ -27,11 +27,12 @@ HocEvent* HocEvent::alloc(const char* stmt, Object* ppobj, int reinit, Object* p if (!hepool_) { nrn_hoc_lock(); if (!hepool_) { - hepool_ = new HocEventPool(100, 1); + hepool_ = new HocEventPool(); + hepool_->set_function(&HocEvent::clear); } nrn_hoc_unlock(); } - HocEvent* he = hepool_->alloc(); + HocEvent* he = hepool_->allocate(); he->stmt_ = nullptr; he->ppobj_ = ppobj; he->reinit_ = reinit; @@ -48,7 +49,7 @@ void HocEvent::hefree() { delete stmt_; stmt_ = nullptr; } - hepool_->hpfree(this); + hepool_->deallocate(this); } void HocEvent::clear() { diff --git a/src/nrncvode/netcon.h b/src/nrncvode/netcon.h index efa9d68657..3063516856 100644 --- a/src/nrncvode/netcon.h +++ b/src/nrncvode/netcon.h @@ -6,7 +6,7 @@ #include "neuron/container/data_handle.hpp" #include "nrnmpi.h" #include "nrnneosm.h" -#include "pool.hpp" +#include "utils/mutexed_pool.hpp" #include "tqitem.hpp" #include @@ -28,7 +28,7 @@ class TQueue; struct NrnThread; class NetCvode; class HocEvent; -using HocEventPool = MutexPool; +using HocEventPool = MutexedPool; class HocCommand; struct STETransition; class IvocVect; diff --git a/src/nrncvode/netcvode.cpp b/src/nrncvode/netcvode.cpp index f0421a92da..e8a7246ce7 100644 --- a/src/nrncvode/netcvode.cpp +++ b/src/nrncvode/netcvode.cpp @@ -13,7 +13,7 @@ #include "parse.hpp" #include "cvodeobj.h" #include "hoclist.h" -#include "pool.hpp" +#include "utils/pool.hpp" #include "tqueue.hpp" #include "ocobserv.h" #include "nrnneosm.h" @@ -427,7 +427,6 @@ struct InterThreadEvent { }; typedef std::vector WatchList; -using SelfEventPool = MutexPool; typedef std::vector TQList; // allows marshalling of all items in the event queue that need to be @@ -1032,10 +1031,10 @@ Object** NetCvode::netconlist() { #define ITE_SIZE 10 NetCvodeThreadData::NetCvodeThreadData() { - tpool_ = new TQItemPool(1000, 1); + tpool_ = new TQItemPool(); // tqe_ accessed only by thread i so no locking tqe_ = new TQueue(tpool_, 0); - sepool_ = new SelfEventPool(1000, 1); + sepool_ = new SelfEventPool(); selfqueue_ = nullptr; psl_thr_ = nullptr; tq_ = nullptr; @@ -2295,7 +2294,7 @@ void nrn_net_send(Datum* v, double* weight, Point_process* pnt, double td, doubl STATISTICS(SelfEvent::selfevent_send_); NrnThread* nt = PP2NT(pnt); NetCvodeThreadData& p = net_cvode_instance->p[nt->id]; - SelfEvent* se = p.sepool_->alloc(); + SelfEvent* se = p.sepool_->allocate(); se->flag_ = flag; se->target_ = pnt; se->weight_ = weight; @@ -2331,7 +2330,7 @@ void artcell_net_send(Datum* v, double* weight, Point_process* pnt, double td, d STATISTICS(SelfEvent::selfevent_send_); NrnThread* nt = PP2NT(pnt); NetCvodeThreadData& p = net_cvode_instance->p[nt->id]; - SelfEvent* se = p.sepool_->alloc(); + SelfEvent* se = p.sepool_->allocate(); se->flag_ = flag; se->target_ = pnt; se->weight_ = weight; @@ -3366,7 +3365,7 @@ void SelfEvent::call_net_receive(NetCvode* ns) { } NetCvodeThreadData& nctd = ns->p[PP2NT(target_)->id]; --nctd.unreffed_event_cnt_; - nctd.sepool_->hpfree(this); + nctd.sepool_->deallocate(this); } void SelfEvent::pr(const char* s, double tt, NetCvode* ns) { diff --git a/src/nrncvode/netcvode.h b/src/nrncvode/netcvode.h index 6715eae018..ae6c08baf7 100644 --- a/src/nrncvode/netcvode.h +++ b/src/nrncvode/netcvode.h @@ -12,6 +12,8 @@ #include #include +#include + struct NrnThread; class PreSyn; class HocDataPaths; @@ -19,7 +21,7 @@ using PreSynTable = std::unordered_map, P class NetCon; class DiscreteEvent; class SelfEvent; -using SelfEventPool = MutexPool; +using SelfEventPool = MutexedPool; struct hoc_Item; class PlayRecord; class IvocVect; diff --git a/src/nrncvode/pool.hpp b/src/nrncvode/pool.hpp deleted file mode 100644 index 037224a3e5..0000000000 --- a/src/nrncvode/pool.hpp +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once - -// create and manage a vector of objects as a memory pool of those objects -// the object must have a void clear() method which takes care of any -// data the object contains which should be deleted upon free_all. -// clear() is NOT called on free, only on free_all. - -// the chain of Pool -// is only for extra items in a pool_ and no other fields are used. -// the pool doubles in size every time a chain Pool is added. -// maxget() tells the most number of pool items used at once. - -#include - -#include - -template -class MutexPool { - public: - MutexPool(long count, int mkmut = 0); - ~MutexPool(); - T* alloc(); - void hpfree(T*); - int maxget() { - return maxget_; - } - void free_all(); - - private: - void grow(); - T** items_{}; - T* pool_{}; - long pool_size_{}; - long count_{}; - long get_{}; - long put_{}; - long nget_{}; - long maxget_{}; - MutexPool* chain_{}; - MUTDEC -}; - -template -MutexPool::MutexPool(long count, int mkmut) { - count_ = count; - pool_ = new T[count_]; - pool_size_ = count; - items_ = new T*[count_]; - for (long i = 0; i < count_; ++i) { - items_[i] = pool_ + i; - } - MUTCONSTRUCT(mkmut) -} - -template -void MutexPool::grow() { - assert(get_ == put_); - MutexPool* p = new MutexPool(count_); - p->chain_ = chain_; - chain_ = p; - long newcnt = 2 * count_; - T** itms = new T*[newcnt]; - put_ += count_; - for (long i = 0; i < get_; ++i) { - itms[i] = items_[i]; - } - for (long i = get_, j = 0; j < count_; ++i, ++j) { - itms[i] = p->items_[j]; - } - for (long i = put_, j = get_; j < count_; ++i, ++j) { - itms[i] = items_[j]; - } - delete[] items_; - delete[] p->items_; - p->items_ = 0; - items_ = itms; - count_ = newcnt; -} - -template -MutexPool::~MutexPool() { - delete chain_; - delete[] pool_; - delete[] items_; - MUTDESTRUCT -} - -template -T* MutexPool::alloc() { - MUTLOCK - if (nget_ >= count_) { - grow(); - } - T* item = items_[get_]; - get_ = (get_ + 1) % count_; - ++nget_; - - maxget_ = std::max(nget_, maxget_); - - MUTUNLOCK - return item; -} - -template -void MutexPool::hpfree(T* item) { - MUTLOCK - assert(nget_ > 0); - items_[put_] = item; - put_ = (put_ + 1) % count_; - --nget_; - MUTUNLOCK -} - -template -void MutexPool::free_all() { - MUTLOCK - MutexPool* pp; - long i; - nget_ = 0; - get_ = 0; - put_ = 0; - for (pp = this; pp; pp = pp->chain_) { - for (i = 0; i < pp->pool_size_; ++i) { - items_[put_++] = pp->pool_ + i; - pp->pool_[i].clear(); - } - } - assert(put_ == count_); - put_ = 0; - MUTUNLOCK -} diff --git a/src/nrncvode/tqueue.cpp b/src/nrncvode/tqueue.cpp index 9010ae6c43..f1d87f0fc6 100644 --- a/src/nrncvode/tqueue.cpp +++ b/src/nrncvode/tqueue.cpp @@ -7,7 +7,6 @@ #include #include "tqueue.hpp" -#include "pool.hpp" #define PROFILE 0 #include "profile.h" @@ -80,7 +79,7 @@ TQueue::~TQueue() { } void TQueue::deleteitem(TQItem* i) { - tpool_->hpfree(i); + tpool_->deallocate(i); } void TQueue::print() { @@ -185,7 +184,7 @@ void TQueue::spike_stat(double* d) { TQItem* TQueue::insert(double t, void* d) { MUTLOCK STAT(ninsert); - TQItem* i = tpool_->alloc(); + TQItem* i = tpool_->allocate(); i->data_ = d; i->t_ = t; i->cnt_ = -1; @@ -204,7 +203,7 @@ TQItem* TQueue::insert(double t, void* d) { TQItem* TQueue::enqueue_bin(double td, void* d) { MUTLOCK STAT(ninsert); - TQItem* i = tpool_->alloc(); + TQItem* i = tpool_->allocate(); i->data_ = d; i->t_ = td; binq_->enqueue(td, i); @@ -214,7 +213,7 @@ TQItem* TQueue::enqueue_bin(double td, void* d) { void TQueue::release(TQItem* q) { // if lockable then the pool is internally handles locking - tpool_->hpfree(q); + tpool_->deallocate(q); } void TQueue::remove(TQItem* q) { @@ -232,7 +231,7 @@ void TQueue::remove(TQItem* q) { } else { sptree_->remove(q); } - tpool_->hpfree(q); + tpool_->deallocate(q); } MUTUNLOCK } @@ -400,7 +399,7 @@ SelfQueue::~SelfQueue() { } TQItem* SelfQueue::insert(void* d) { MUTLOCK - TQItem* q = tpool_->alloc(); + TQItem* q = tpool_->allocate(); q->left_ = nullptr; q->right_ = head_; if (head_) { @@ -422,14 +421,14 @@ void* SelfQueue::remove(TQItem* q) { if (q == head_) { head_ = q->right_; } - tpool_->hpfree(q); + tpool_->deallocate(q); MUTUNLOCK return q->data_; } void SelfQueue::remove_all() { MUTLOCK for (TQItem* q = first(); q; q = next(q)) { - tpool_->hpfree(q); + tpool_->deallocate(q); } head_ = nullptr; MUTUNLOCK diff --git a/src/nrncvode/tqueue.hpp b/src/nrncvode/tqueue.hpp index adf45e2464..12dd86e241 100644 --- a/src/nrncvode/tqueue.hpp +++ b/src/nrncvode/tqueue.hpp @@ -5,11 +5,11 @@ #include #include -#include +#include #include "tqitem.hpp" -using TQItemPool = MutexPool; +using TQItemPool = MutexedPool; // bin queue for the fixed step method for NetCons and PreSyns. Splay tree // for others. diff --git a/src/nrniv/arraypool.h b/src/nrniv/arraypool.h index 7b0051e29c..25a4902fc7 100644 --- a/src/nrniv/arraypool.h +++ b/src/nrniv/arraypool.h @@ -25,9 +25,6 @@ class ArrayPool { ~ArrayPool(); T* alloc(); void hpfree(T*); - int maxget() { - return maxget_; - } int size() { return count_; } @@ -35,12 +32,6 @@ class ArrayPool { T* pool() { return pool_; } - long get() { - return get_; - } - long put() { - return put_; - } long nget() { return nget_; } @@ -50,24 +41,13 @@ class ArrayPool { long d2() { return d2_; } - T* element(long i) { - return pool_ + i * d2_; - } - T** items() { - return items_; - } - void grow(long ninc); ArrayPool* chain() { return chain_; } - long chain_size() { - return pool_size_; - } private: void grow_(long ninc); - private: T** items_; T* pool_; long pool_size_; @@ -98,16 +78,10 @@ ArrayPool::ArrayPool(long count, long d2) { nget_ = 0; ntget_ = 0; maxget_ = 0; - chain_ = 0; + chain_ = nullptr; chainlast_ = this; } -template -void ArrayPool::grow(long ninc) { - grow_(ninc); - put_ = get_; -} - template void ArrayPool::grow_(long ninc) { assert(get_ == put_); diff --git a/src/nrniv/cxprop.cpp b/src/nrniv/cxprop.cpp index 08921d655b..1e915c4ea7 100644 --- a/src/nrniv/cxprop.cpp +++ b/src/nrniv/cxprop.cpp @@ -1,7 +1,7 @@ -#include "arraypool.h" // ArrayPool -#include "hocdec.h" // Datum -#include "section.h" // Section -#include "structpool.h" // Pool +#include "arraypool.h" // ArrayPool +#include "hocdec.h" // Datum +#include "section.h" // Section +#include "utils/pool.hpp" // Pool #include "../neuron/model_data.hpp" #include @@ -84,9 +84,9 @@ void nrn_delete_mechanism_prop_datum(int type) { Section* nrn_section_alloc() { if (!secpool_) { - secpool_ = new SectionPool(1000); + secpool_ = new SectionPool(); } - auto* const sec = secpool_->alloc(); + auto* const sec = secpool_->allocate(); // Call the Section constructor new (sec) Section(); return sec; @@ -95,14 +95,14 @@ Section* nrn_section_alloc() { void nrn_section_free(Section* s) { // Call the Section destructor s->~Section(); - secpool_->hpfree(s); + secpool_->deallocate(s); } int nrn_is_valid_section_ptr(void* v) { if (!secpool_) { return 0; } - return secpool_->is_valid_ptr(v); + return secpool_->is_valid_ptr(static_cast(v)); } void nrn_poolshrink(int shrink) { diff --git a/src/nrniv/multisend.cpp b/src/nrniv/multisend.cpp index 22bb8cc78b..731f91e813 100644 --- a/src/nrniv/multisend.cpp +++ b/src/nrniv/multisend.cpp @@ -43,6 +43,8 @@ of spikes sent is equal to the number of spikes sent. // setup time with a bgp.dma_send_ so as to pass on the spike to the // phase2 list of target hosts. +#include + // asm/msr.h no longer compiles on my machine. // only for basic testing of logic when not on blue gene/p #define USE_RDTSCL 0 @@ -94,7 +96,6 @@ struct Phase2Buffer { double spiketime; }; -#include using SpkPool = Pool; @@ -163,7 +164,7 @@ Multisend_ReceiveBuffer::Multisend_ReceiveBuffer() { count_ = 0; size_ = MULTISEND_RECEIVEBUFFER_SIZE; buffer_ = new NRNMPI_Spike*[size_]; - pool_ = new SpkPool(MULTISEND_RECEIVEBUFFER_SIZE); + pool_ = new SpkPool{}; psbuf_ = 0; #if ENQUEUE == 1 psbuf_ = new PreSyn*[size_]; @@ -173,9 +174,7 @@ Multisend_ReceiveBuffer::Multisend_ReceiveBuffer() { } Multisend_ReceiveBuffer::~Multisend_ReceiveBuffer() { assert(busy_ == 0); - for (int i = 0; i < count_; ++i) { - pool_->hpfree(buffer_[i]); - } + pool_->free_all(); delete[] buffer_; delete pool_; if (psbuf_) @@ -187,7 +186,7 @@ void Multisend_ReceiveBuffer::init(int index) { timebase_ = 0; nsend_cell_ = nsend_ = nrecv_ = busy_ = maxcount_ = 0; for (int i = 0; i < count_; ++i) { - pool_->hpfree(buffer_[i]); + pool_->deallocate(buffer_[i]); } count_ = 0; phase2_head_ = phase2_tail_ = 0; @@ -210,7 +209,7 @@ void Multisend_ReceiveBuffer::incoming(int gid, double spiketime) { psbuf_ = new PreSyn*[size_]; } } - NRNMPI_Spike* spk = pool_->alloc(); + NRNMPI_Spike* spk = pool_->allocate(); spk->gid = gid; spk->spiketime = spiketime; buffer_[count_++] = spk; @@ -241,7 +240,7 @@ void Multisend_ReceiveBuffer::enqueue() { pb.spiketime = spk->spiketime; } ps->send(spk->spiketime, net_cvode_instance, nrn_threads); - pool_->hpfree(spk); + pool_->deallocate(spk); } #endif count_ = 0; @@ -289,7 +288,7 @@ void Multisend_ReceiveBuffer::enqueue2() { NRNMPI_Spike* spk = buffer_[i]; PreSyn* ps = psbuf_[i]; ps->send(spk->spiketime, net_cvode_instance, nrn_threads); - pool_->hpfree(spk); + pool_->deallocate(spk); } count_ = 0; nrecv_ = 0; diff --git a/src/nrniv/structpool.h b/src/nrniv/structpool.h deleted file mode 100644 index ab8999af61..0000000000 --- a/src/nrniv/structpool.h +++ /dev/null @@ -1,148 +0,0 @@ -#pragma once - -// same as ../nrncvode/pool.h but items do not require a clear method. - -// create and manage a vector of objects as a memory pool of those objects -// the object must have a void clear() method which takes care of any -// data the object contains which should be deleted upon free_all. -// clear() is NOT called on free, only on free_all. - -// the chain of Pool -// is only for extra items in a pool_ and no other fields are used. -// the pool doubles in size every time a chain Pool is added. -// maxget() tells the most number of pool items used at once. - -template -class Pool { - public: - Pool(long count); - ~Pool(); - T* alloc(); - void hpfree(T*); - int maxget() { - return maxget_; - } - void free_all(); - int is_valid_ptr(void*); - - private: - void grow(); - - private: - T** items_; - T* pool_; - long pool_size_; - long count_; - long get_; - long put_; - long nget_; - long maxget_; - Pool* chain_; -}; - - -template -Pool::Pool(long count) { - count_ = count; - pool_ = new T[count_]; - pool_size_ = count; - items_ = new T*[count_]; - for (long i = 0; i < count_; ++i) - items_[i] = pool_ + i; - get_ = 0; - put_ = 0; - nget_ = 0; - maxget_ = 0; - chain_ = 0; -} - -template -void Pool::grow() { - assert(get_ == put_); - Pool* p = new Pool(count_); - p->chain_ = chain_; - chain_ = p; - long newcnt = 2 * count_; - T** itms = new T*[newcnt]; - long i, j; - put_ += count_; - for (i = 0; i < get_; ++i) { - itms[i] = items_[i]; - } - for (i = get_, j = 0; j < count_; ++i, ++j) { - itms[i] = p->items_[j]; - } - for (i = put_, j = get_; j < count_; ++i, ++j) { - itms[i] = items_[j]; - } - delete[] items_; - delete[] p->items_; - p->items_ = 0; - items_ = itms; - count_ = newcnt; -} - -template -Pool::~Pool() { - if (chain_) { - delete chain_; - } - delete[] pool_; - if (items_) { - delete[] items_; - } -} - -template -int Pool::is_valid_ptr(void* v) { - Pool* pp; - for (pp = this; pp; pp = pp->chain_) { - void* vp = (void*) (pp->pool_); - if (v >= vp && v < (void*) (pp->pool_ + pp->pool_size_)) { - if ((((char*) v - (char*) vp) % sizeof(T)) == 0) { - return 1; - } else { - return 0; - } - } - } - return 0; -} - -template -T* Pool::alloc() { - if (nget_ >= count_) { - grow(); - } - T* item = items_[get_]; - get_ = (get_ + 1) % count_; - ++nget_; - if (nget_ > maxget_) { - maxget_ = nget_; - } - return item; -} - -template -void Pool::hpfree(T* item) { - assert(nget_ > 0); - items_[put_] = item; - put_ = (put_ + 1) % count_; - --nget_; -} - -template -void Pool::free_all() { - Pool* pp; - long i; - nget_ = 0; - get_ = 0; - put_ = 0; - for (pp = this; pp; pp = pp->chain_) { - for (i = 0; i < pp->pool_size_; ++i) { - items_[put_++] = pp->pool_ + i; - } - } - assert(put_ == count_); - put_ = 0; -} diff --git a/src/utils/mutexed_pool.hpp b/src/utils/mutexed_pool.hpp new file mode 100644 index 0000000000..fe29b49bdd --- /dev/null +++ b/src/utils/mutexed_pool.hpp @@ -0,0 +1,141 @@ +#pragma once + +// create and manage a vector of objects as a memory pool of those objects +// the object must have a void clear() method which takes care of any +// data the object contains which should be deleted upon free_all. +// clear() is NOT called on deallocate, only on free_all. + +// the pool doubles in size every time a new pool is added. + +#include +#include +#include +#include + +template +class MutexedPool { + public: + using value_type = T; + MutexedPool(); + template + void set_function(F f) { + clear_ = f; + } + T* allocate(std::size_t = 1); + void deallocate(T*, std::size_t = 1); + void free_all(); + + // For now this pool only allow allocation of 1 byte at a time + std::size_t max_size() const { + return 1; + } + + bool is_valid_ptr(const T*) const; + + private: + // Add a new vector to the pools_ with the double of size of the previous one + // Reset put_ and get_ + void grow(); + // A ring-buffer covering all the pools_ + // put_ is the place where to put the next freed item + // get_ is the place where to get a new item + std::vector items_{}; + std::size_t get_{}; + std::size_t put_{}; + // Number of items currently allocated in the pools + // When this number equal the total number of elements it will be time to grow() + std::size_t nget_{}; + // A vector of all the pools_ + std::vector> pools_{}; + + std::function clear_{}; + +#if NRN_ENABLE_THREADS + MutexType mut_; +#endif +}; + +template +MutexedPool::MutexedPool() { + constexpr std::size_t count = 1000; + auto& ptr = pools_.emplace_back(count); + items_.resize(count); + std::transform(ptr.begin(), ptr.end(), items_.begin(), [](auto& it) { return ⁢ }); +} + +template +void MutexedPool::grow() { + const std::size_t total_size = items_.size(); + + // Everything is already allocated so reset put_ to the beginning of items + // and set get_ to the new space allocated pointing to the new pool + put_ = 0; + get_ = total_size; + + items_.resize(2 * total_size); + + auto& ptr = pools_.emplace_back(total_size); + std::transform(ptr.begin(), ptr.end(), items_.begin() + total_size, [](auto& it) { + return ⁢ + }); +} + +template +T* MutexedPool::allocate(std::size_t n) { +#if NRN_ENABLE_THREADS + std::lock_guard l(mut_); +#endif + if (n != 1) { + throw std::runtime_error("MutexedPool allocator can only allocate one object at a time"); + } + if (nget_ == items_.size()) { + grow(); + } + ++nget_; + T* item = items_[get_]; + get_ = (++get_) % items_.size(); + + return item; +} + +template +void MutexedPool::deallocate(T* item, std::size_t) { +#if NRN_ENABLE_THREADS + std::lock_guard l(mut_); +#endif + --nget_; + items_[put_] = item; + put_ = (++put_) % items_.size(); +} + +template +void MutexedPool::free_all() { +#if NRN_ENABLE_THREADS + std::lock_guard l(mut_); +#endif + get_ = 0; + put_ = 0; + nget_ = 0; + // Populate again items_ with real ones + auto it = items_.begin(); + for (auto& pool: pools_) { + std::transform(pool.begin(), pool.end(), it, [](auto& i) { return &i; }); + it += pool.size(); + } + if (clear_) { + for (auto& item: items_) { + std::invoke(clear_, item); + } + } +} + +template +bool MutexedPool::is_valid_ptr(const T* p) const { + for (const auto& pool: pools_) { + if (p >= &pool.front() && p <= &pool.back()) { + // Check that the pointer is on a value and not in the middle + return (p - &pool.front()) % sizeof(T) == 0; + } + } + return false; +} diff --git a/src/utils/pool.hpp b/src/utils/pool.hpp new file mode 100644 index 0000000000..034a550995 --- /dev/null +++ b/src/utils/pool.hpp @@ -0,0 +1,127 @@ +#pragma once + +// create and manage a vector of objects as a memory pool of those objects +// the object must have a void clear() method which takes care of any +// data the object contains which should be deleted upon free_all. +// clear() is NOT called on deallocate, only on free_all. + +// the pool doubles in size every time a new pool is added. + +#include +#include +#include + +template +class Pool { + public: + using value_type = T; + Pool(); + template + void set_function(F f) { + clear_ = f; + } + T* allocate(std::size_t = 1); + void deallocate(T*, std::size_t = 1); + void free_all(); + + // For now this pool only allow allocation of 1 byte at a time + std::size_t max_size() const { + return 1; + } + + bool is_valid_ptr(const T*) const; + + private: + // Add a new vector to the pools_ with the double of size of the previous one + // Reset put_ and get_ + void grow(); + // A ring-buffer covering all the pools_ + // put_ is the place where to put the next freed item + // get_ is the place where to get a new item + std::vector items_{}; + std::size_t get_{}; + std::size_t put_{}; + // Number of items currently allocated in the pools + // When this number equal the total number of elements it will be time to grow() + std::size_t nget_{}; + // A vector of all the pools_ + std::vector> pools_{}; + + std::function clear_{}; +}; + +template +Pool::Pool() { + constexpr std::size_t count = 1000; + auto& ptr = pools_.emplace_back(count); + items_.resize(count); + std::transform(ptr.begin(), ptr.end(), items_.begin(), [](auto& it) { return ⁢ }); +} + +template +void Pool::grow() { + const std::size_t total_size = items_.size(); + + // Everything is already allocated so reset put_ to the beginning of items + // and set get_ to the new space allocated pointing to the new pool + put_ = 0; + get_ = total_size; + + items_.resize(2 * total_size); + + auto& ptr = pools_.emplace_back(total_size); + std::transform(ptr.begin(), ptr.end(), items_.begin() + total_size, [](auto& it) { + return ⁢ + }); +} + +template +T* Pool::allocate(std::size_t n) { + if (n != 1) { + throw std::runtime_error("Pool allocator can only allocate one object at a time"); + } + if (nget_ == items_.size()) { + grow(); + } + ++nget_; + T* item = items_[get_]; + get_ = (++get_) % items_.size(); + + return item; +} + +template +void Pool::deallocate(T* item, std::size_t) { + --nget_; + items_[put_] = item; + put_ = (++put_) % items_.size(); +} + +template +void Pool::free_all() { + get_ = 0; + put_ = 0; + nget_ = 0; + // Populate again items_ with real ones + auto it = items_.begin(); + for (auto& pool: pools_) { + std::transform(pool.begin(), pool.end(), it, [](auto& i) { return &i; }); + it += pool.size(); + } + if (clear_) { + for (auto& item: items_) { + std::invoke(clear_, item); + } + } +} + +template +bool Pool::is_valid_ptr(const T* p) const { + for (const auto& pool: pools_) { + if (p >= &pool.front() && p <= &pool.back()) { + // Check that the pointer is on a value and not in the middle + return (p - &pool.front()) % sizeof(T) == 0; + } + } + return false; +}