1 /* 2 * Copyright (c) 2016-present, Facebook, Inc. 3 * All rights reserved. 4 * 5 * This source code is licensed under both the BSD-style license (found in the 6 * LICENSE file in the root directory of this source tree) and the GPLv2 (found 7 * in the COPYING file in the root directory of this source tree). 8 */ 9 #pragma once 10 11 #include <cassert> 12 #include <functional> 13 #include <memory> 14 #include <mutex> 15 #include <vector> 16 17 namespace pzstd { 18 19 /** 20 * An unbounded pool of resources. 21 * A `ResourcePool<T>` requires a factory function that takes allocates `T*` and 22 * a free function that frees a `T*`. 23 * Calling `ResourcePool::get()` will give you a new `ResourcePool::UniquePtr` 24 * to a `T`, and when it goes out of scope the resource will be returned to the 25 * pool. 26 * The `ResourcePool<T>` *must* survive longer than any resources it hands out. 27 * Remember that `ResourcePool<T>` hands out mutable `T`s, so make sure to clean 28 * up the resource before or after every use. 29 */ 30 template <typename T> 31 class ResourcePool { 32 public: 33 class Deleter; 34 using Factory = std::function<T*()>; 35 using Free = std::function<void(T*)>; 36 using UniquePtr = std::unique_ptr<T, Deleter>; 37 38 private: 39 std::mutex mutex_; 40 Factory factory_; 41 Free free_; 42 std::vector<T*> resources_; 43 unsigned inUse_; 44 45 public: 46 /** 47 * Creates a `ResourcePool`. 48 * 49 * @param factory The function to use to create new resources. 50 * @param free The function to use to free resources created by `factory`. 51 */ ResourcePool(Factory factory,Free free)52 ResourcePool(Factory factory, Free free) 53 : factory_(std::move(factory)), free_(std::move(free)), inUse_(0) {} 54 55 /** 56 * @returns A unique pointer to a resource. The resource is null iff 57 * there are no available resources and `factory()` returns null. 58 */ get()59 UniquePtr get() { 60 std::lock_guard<std::mutex> lock(mutex_); 61 if (!resources_.empty()) { 62 UniquePtr resource{resources_.back(), Deleter{*this}}; 63 resources_.pop_back(); 64 ++inUse_; 65 return resource; 66 } 67 UniquePtr resource{factory_(), Deleter{*this}}; 68 ++inUse_; 69 return resource; 70 } 71 ~ResourcePool()72 ~ResourcePool() noexcept { 73 assert(inUse_ == 0); 74 for (const auto resource : resources_) { 75 free_(resource); 76 } 77 } 78 79 class Deleter { 80 ResourcePool *pool_; 81 public: Deleter(ResourcePool & pool)82 explicit Deleter(ResourcePool &pool) : pool_(&pool) {} 83 operator()84 void operator() (T *resource) { 85 std::lock_guard<std::mutex> lock(pool_->mutex_); 86 // Make sure we don't put null resources into the pool 87 if (resource) { 88 pool_->resources_.push_back(resource); 89 } 90 assert(pool_->inUse_ > 0); 91 --pool_->inUse_; 92 } 93 }; 94 }; 95 96 } 97