blob: 6e478b19e44d93e216ac720dda906a8c022b489f [file] [log] [blame]
/**
* Copyright 2016 Yahoo Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef LIB_OBJECTPOOL_H_
#define LIB_OBJECTPOOL_H_
#include <algorithm>
#include <boost/thread/locks.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/thread/tss.hpp>
namespace pulsar {
template<typename T, int MaxSize>
class Allocator {
public:
// Allocator must be stateless, so put everything in this static
class Impl {
public:
// cheap lock to acquire
static boost::mutex mutex_;
// note: use std::forward_list<> when switching to C++11 mode
struct Node {
Node* next;
explicit Node(Node* n) : next(n) {}
};
Node *head_;
int pushSize_;
struct GlobalPool {
Node *node_;
int nodeCount_;
GlobalPool *next_;
explicit GlobalPool(GlobalPool* n) : next_(n) {}
};
static struct GlobalPool *globalPool_;
static int globalNodeCount_;
Impl(const Impl&);
void operator=(const Impl&);
void* pop() {
if (!head_) {
// size = 0
boost::lock_guard<boost::mutex> lock(mutex_);
if (!globalPool_) {
return NULL;
}
GlobalPool *poolEntry = globalPool_;
head_ = globalPool_->node_;
pushSize_ += globalPool_->nodeCount_;
globalNodeCount_ -= globalPool_->nodeCount_;
globalPool_ = globalPool_->next_;
delete poolEntry;
}
void* result = head_;
if (result) {
head_ = head_->next;
pushSize_--;
}
return result;
}
bool push(void* p) {
// Once thread specific entries reaches 10% of max size, push them to GlobalPool
if (pushSize_ >= MaxSize*0.1) {
bool deleteList = true;
{
// Move the entries to global pool
boost::lock_guard<boost::mutex> lock(mutex_);
// If total node count reached max allowed cache limit,
// skip adding to global pool.
if ((globalNodeCount_ + pushSize_) <= MaxSize) {
deleteList = false;
globalPool_ = new GlobalPool(globalPool_);
globalPool_->node_ = head_;
globalPool_->nodeCount_ = pushSize_;
globalNodeCount_ += pushSize_;
}
}
if (deleteList) {
pushSize_ = 0;
deleteLinkedList(head_);
}
head_ = new(p) Node(0);
pushSize_ = 1;
return true;
}
head_ = new(p) Node(head_);
pushSize_++;
return true;
}
static void deleteLinkedList(Node* head) {
Node* n = head;
while (n) {
void* p = n;
n = n->next;
::operator delete(p);
}
}
public:
Impl() {
pushSize_ = 0;
head_ = 0;
}
~Impl() {
// No need for mutex for pop
deleteLinkedList(head_);
}
void* allocate() {
void* result = pop();
if (!result) {
result = ::operator new(std::max(sizeof(T), sizeof(Node)));
}
return result;
}
void deallocate(void* p) {
if (!push(p)) {
::operator delete(p);
}
}
};
static boost::thread_specific_ptr<Impl> implPtr_;
typedef size_t size_type;
typedef T* pointer;
typedef const void* const_pointer;
Allocator() {
}
Allocator(const Allocator& /*other*/) {
}
template<typename Other, int OtherSize>
Allocator(const Allocator<Other, OtherSize>& /*other*/) {
}
pointer allocate(size_type n, const void * /*hint*/ = 0) {
Impl *impl = implPtr_.get();
if (!impl) {
implPtr_.reset(new Impl);
impl = implPtr_.get();
}
void* p = (n == 1)
? impl->allocate()
: operator new(n * sizeof(T));
return static_cast<T*>(p);
}
void deallocate(pointer ptr, size_type n) {
Impl *impl = implPtr_.get();
if (!impl) {
implPtr_.reset(new Impl);
impl = implPtr_.get();
}
if (n == 1)
impl->deallocate(ptr);
else
::operator delete(ptr);
}
template<typename Other> struct rebind {
typedef Allocator<Other, MaxSize> other;
};
};
// typename Allocator<Type,MaxSize>::Impl is important else the compiler
// doesn't understand that it is a type
template <typename Type, int MaxSize>
boost::thread_specific_ptr<typename Allocator<Type,MaxSize>::Impl> Allocator<Type,MaxSize>::implPtr_;
template <typename Type, int MaxSize>
boost::mutex Allocator<Type,MaxSize>::Impl::mutex_;
template <typename Type, int MaxSize>
struct Allocator<Type,MaxSize>::Impl::GlobalPool *Allocator<Type,MaxSize>::Impl::globalPool_;
template <typename Type, int MaxSize>
int Allocator<Type,MaxSize>::Impl::globalNodeCount_;
template<typename Type, int MaxSize>
class ObjectPool {
typedef boost::shared_ptr<Type> TypeSharedPtr;
Allocator<Type, MaxSize> allocator_;
public:
ObjectPool() {
}
TypeSharedPtr create() {
return boost::allocate_shared<Type>(allocator_);
}
~ObjectPool() {
struct Allocator<Type,MaxSize>::Impl::GlobalPool *poolEntry = Allocator<Type,MaxSize>::Impl::globalPool_;
while(poolEntry) {
Allocator<Type, MaxSize>::Impl::deleteLinkedList(poolEntry->node_);
struct Allocator<Type,MaxSize>::Impl::GlobalPool *delEntry = poolEntry;
poolEntry = poolEntry->next_;
::operator delete(delEntry);
}
}
private:
ObjectPool<Type, MaxSize>(const ObjectPool<Type, MaxSize>&);
ObjectPool<Type, MaxSize>& operator=(const ObjectPool<Type, MaxSize>&);
};
}
#endif /* LIB_OBJECTPOOL_H_ */