aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/clickhouse/src/Common/ObjectPool.h
blob: 801a37d0dfba84007432968ec0b9a06650edde82 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#pragma once

#include <map>
#include <memory>
#include <stack>
#include <mutex>


namespace DB
{


/** Pool for objects that cannot be used from different threads simultaneously.
  * Allows to create an object for each thread.
  * Pool has unbounded size and objects are not destroyed before destruction of pool.
  *
  * Use it in cases when thread local storage is not appropriate
  *  (when maximum number of simultaneously used objects is less
  *   than number of running/sleeping threads, that has ever used object,
  *   and creation/destruction of objects is expensive).
  */
template <typename T>
class SimpleObjectPool
{
protected:

    /// Hold all available objects in stack.
    std::mutex mutex;
    std::stack<std::unique_ptr<T>> stack;

    /// Specialized deleter for std::unique_ptr.
    /// Returns underlying pointer back to stack thus reclaiming its ownership.
    struct Deleter
    {
        SimpleObjectPool<T> * parent;

        Deleter(SimpleObjectPool<T> * parent_ = nullptr) : parent{parent_} {} /// NOLINT

        void operator()(T * owning_ptr) const
        {
            std::lock_guard lock{parent->mutex};
            parent->stack.emplace(owning_ptr);
        }
    };

public:
    using Pointer = std::unique_ptr<T, Deleter>;

    /// Extracts and returns a pointer from the stack if it's not empty,
    ///  creates a new one by calling provided f() otherwise.
    template <typename Factory>
    Pointer get(Factory && f)
    {
        std::unique_lock lock(mutex);

        if (stack.empty())
        {
            lock.unlock();
            return { f(), this };
        }

        auto object = stack.top().release();
        stack.pop();

        return { object, this };
    }

    /// Like get(), but creates object using default constructor.
    Pointer getDefault()
    {
        return get([] { return new T; });
    }
};


/// Like SimpleObjectPool, but additionally allows store different kind of objects that are identified by Key
template <typename T, typename Key>
class ObjectPoolMap
{
private:

    using Object = SimpleObjectPool<T>;

    /// Key -> objects
    using Container = std::map<Key, std::unique_ptr<Object>>;

    Container container;
    std::mutex mutex;

public:

    using Pointer = typename Object::Pointer;

    template <typename Factory>
    Pointer get(const Key & key, Factory && f)
    {
        std::lock_guard lock(mutex);

        auto it = container.find(key);
        if (container.end() == it)
            it = container.emplace(key, std::make_unique<Object>()).first;

        return it->second->get(std::forward<Factory>(f));
    }
};


}