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