#pragma once
#include "common.hpp"
#include "test.hpp"
#include "stats.hpp"
#include <new>
template <class T> class TPool {
protected:
typedef struct tpool_node_s {
T data; // at the beginning, s.t. it can be 1) externally 2) easily casted
struct tpool_node_s* next;
} tpool_node_t;
private:
tpool_node_t* head; ///< spare ones
unsigned size; ///< only needed for upper spare limit
public:
TPool();
~TPool();
T* pop(); ///< "allocate" new or cached one
void push(T*); ///< "free"
void purge();
};
template <class T> class TPoolTS: public TPool<T> {
private:
atomic_t lock;
public:
TPoolTS(): lock(0) {}
INLINE T* pop() { spin_lock(&lock); T* rv = TPool<T>::pop(); spin_unlock(&lock); return rv; }
INLINE void push(T* t) { spin_lock(&lock); TPool<T>::push(t); spin_unlock(&lock); }
INLINE void purge() { spin_lock(&lock); TPool<T>::purge(); spin_unlock(&lock); }
};
template <class T> class CPool: public TPool<T> {
public:
INLINE T* pop() { return ::new (TPool<T>::pop()) T(); }
INLINE void push(T* t) { t->~T(); TPool<T>::push(t); }
};
/** Provides interface with static pool and new/delete operators to inherit from */
template <class T> class PoolClass {
private:
static TPool<T> pool;
public:
static INLINE void* operator new(size_t nbytes) { assert(nbytes == sizeof(T)); return pool.pop(); }
static INLINE void operator delete(void* p) { pool.push((T*)p); }
};
template <class T> TPool<T> PoolClass<T>::pool;
/** When a rare thread-safe push is needed only, for sync producer and possibly async consumer scenarios. */
template <class T> class TPoolPTS: public TPool<T> {
private:
typedef typename TPool<T>::tpool_node_t node_t;
volatile node_t* pushed; // need volatile?
INLINE void cleanup() {
node_t* node;
do {
node = (node_t*)pushed;
if (!node) return;
} while (!atomic_swp(&pushed, node, NULL));
do {
node_t* tmp = node;
node = node->next;
TPool<T>::push((T*)tmp);
} while (node);
}
public:
TPoolPTS(): pushed(NULL) {}
~TPoolPTS() { cleanup(); }
INLINE T* pop() {
cleanup();
return TPool<T>::pop();
}
INLINE void push_ts(T* t) {
assert(!in_main_thread());
node_t* const node = (node_t*)t;
do {
node->next = (node_t*)pushed;
} while (!atomic_swp(&pushed, node->next, node));
}
INLINE void purge() {
cleanup();
TPool<T>::purge();
}
};
template <class T> class CPoolPTS: public TPoolPTS<T> {
public:
INLINE T* pop() { return new (TPoolPTS<T>::pop()) T(); }
INLINE void push(T* t) { t->~T(); TPoolPTS<T>::push(t); }
};
template <class T> class RefCount { // TODO: implement this by inheritance and delete operator overloading? TODO: what if on the stack? TODO: use smart pointers instead? TODO: use operator* and operator->?
private:
unsigned refcount;
~RefCount() {}
public:
INLINE RefCount(): refcount(1) {}
INLINE RefCount(const T& _val): refcount(1), val(_val) {}
INLINE void inc() { ++refcount; }
INLINE unsigned dec() { if (!--refcount) { delete this; return 0; } else { return refcount; } }
INLINE unsigned dec(TPool<RefCount<T> >* pool) { if (!--refcount) { pool->push(this); return 0; } else { return refcount; } }
INLINE unsigned get() const { return refcount; }
T val;
};
/** Not an actual slab-pool with pages atm, but a list of linked lists w/ free mem of a fixed size. */
class SlabPool {
private:
typedef struct slab_node_s {
struct slab_node_s* next;
int slot; // TODO: alignment?
/* actual data here */
} slab_node_t;
slab_node_t* slots[SLAB_POOL_NUM];
public:
SlabPool();
~SlabPool();
void* pop(size_t);
INLINE void* cpop(size_t l) { return memset(pop(l), 0, l); }
char* dup(const char*, size_t);
INLINE char* dup(const char* s) { return dup(s, strlen(s)); }
void push(void*);
};
extern SlabPool slab_pool; // TODO: some generic string class using this pool?
/** More a string container with automatic cleanup, does not check for lengths, may return NULL. */
class SlabString {
private:
size_t len;
char* str;
public:
SlabString(): len(0), str(NULL) {}
SlabString(const char* s): len(strlen(s)), str(slab_pool.dup(s, len)) {}
SlabString(const char* s, size_t l): len(l), str(slab_pool.dup(s, len)) {}
SlabString(size_t l): len(l), str((char*)slab_pool.pop(l)) { *str = '\0'; }
~SlabString() { if (str) slab_pool.push(str); }
void INLINE dup(const char* s) { if (str) slab_pool.push(str); len = strlen(s); str = slab_pool.dup(s, len); }
void INLINE dup(const char* s, size_t l) { if (str) slab_pool.push(str); len = l; str = slab_pool.dup(s, l); }
const INLINE size_t length() const { return len; }
const INLINE char* c_str() const { return str; }
INLINE char* c_str() { return str; }
};
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
template <class T> TPool<T>::TPool(): head(NULL), size(0) {
}
template <class T> TPool<T>::~TPool() {
purge();
}
template <class T> void TPool<T>::purge() {
tpool_node_t* node;
while ((node = head) != NULL) {
head = head->next;
free(node);
--size;
}
}
template <class T> void TPool<T>::push(T* data) {
assert(data != NULL);
#if MAX_POOL_SPARE == 0
#ifndef DEBUG
MESSAGE(pooling disabled)
#endif
free(data);
return;
#endif
tpool_node_t* node = (tpool_node_t*)data;
if (size >= MAX_POOL_SPARE/sizeof(T)) {
free(node);
} else {
MAKE_MEM_UNDEFINED(data, sizeof(T));
node->next = head;
head = node;
++size;
}
}
template <class T> T* TPool<T>::pop() {
stats_inc(pool_allocs);
#if MAX_POOL_SPARE == 0
return talloc(T);
#endif
tpool_node_t* node;
if (head) {
node = head;
head = head->next;
--size;
stats_inc(pool_hits);
} else {
node = talloc(tpool_node_t);
}
return &node->data;
}