lib/ObjectPool.h (166 lines of code) (raw):

/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 <memory> #include <mutex> 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 std::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 std::lock_guard<std::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 std::lock_guard<std::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 thread_local std::unique_ptr<Impl> implPtr_; typedef T value_type; 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> thread_local std::unique_ptr<typename Allocator<Type, MaxSize>::Impl> Allocator<Type, MaxSize>::implPtr_; template <typename Type, int MaxSize> std::mutex Allocator<Type, MaxSize>::Impl::mutex_; template <typename Type, int MaxSize> typename 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 std::shared_ptr<Type> TypeSharedPtr; Allocator<Type, MaxSize> allocator_; public: ObjectPool() {} TypeSharedPtr create() { return std::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(const ObjectPool<Type, MaxSize>&); ObjectPool& operator=(const ObjectPool<Type, MaxSize>&); }; } // namespace pulsar #endif /* LIB_OBJECTPOOL_H_ */