YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/util/object_pool.h
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
//
18
// The following only applies to changes made to this file as part of YugaByte development.
19
//
20
// Portions Copyright (c) YugaByte, Inc.
21
//
22
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
23
// in compliance with the License.  You may obtain a copy of the License at
24
//
25
// http://www.apache.org/licenses/LICENSE-2.0
26
//
27
// Unless required by applicable law or agreed to in writing, software distributed under the License
28
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
29
// or implied.  See the License for the specific language governing permissions and limitations
30
// under the License.
31
//
32
// Simple pool/freelist for objects of the same type, typically used
33
// in local context.
34
#ifndef YB_UTIL_OBJECT_POOL_H
35
#define YB_UTIL_OBJECT_POOL_H
36
37
#include <stdint.h>
38
39
#include <functional>
40
41
#if defined(__APPLE__)
42
#include <thread>
43
#else
44
#include <sched.h>
45
#endif
46
47
#include <boost/container/stable_vector.hpp>
48
#include <boost/lockfree/stack.hpp>
49
50
#include "yb/gutil/manual_constructor.h"
51
#include "yb/gutil/sysinfo.h"
52
53
namespace yb {
54
55
using base::ManualConstructor;
56
57
template<class T>
58
class ReturnToPool;
59
60
// An object pool allocates and destroys a single class of objects
61
// off of a free-list.
62
//
63
// Upon destruction of the pool, any objects allocated from this pool are
64
// destroyed, regardless of whether they have been explicitly returned to the
65
// pool.
66
//
67
// This class is similar to the boost::pool::object_pool, except that the boost
68
// implementation seems to have O(n) deallocation performance and benchmarked
69
// really poorly.
70
//
71
// This class is not thread-safe.
72
template<typename T>
73
class ObjectPool {
74
 public:
75
  typedef ReturnToPool<T> deleter_type;
76
  typedef std::unique_ptr<T, deleter_type> scoped_ptr;
77
78
  ObjectPool() :
79
    free_list_head_(NULL),
80
    alloc_list_head_(NULL),
81
2
    deleter_(this) {
82
2
  }
83
84
2
  ~ObjectPool() {
85
    // Delete all objects ever allocated from this pool
86
2
    ListNode *node = alloc_list_head_;
87
5
    while (node != NULL) {
88
3
      ListNode *tmp = node;
89
3
      node = node->next_on_alloc_list;
90
3
      if (!tmp->is_on_freelist) {
91
        // Have to run the actual destructor if the user forgot to free it.
92
1
        tmp->Destroy();
93
1
      }
94
3
      delete tmp;
95
3
    }
96
2
  }
97
98
  // Construct a new object instance from the pool.
99
4
  T *Construct() {
100
4
    ManualConstructor<T> *obj = GetObject();
101
4
    obj->Init();
102
4
    return obj->get();
103
4
  }
104
105
  template<class Arg1>
106
  T *Construct(Arg1 arg1) {
107
    ManualConstructor<T> *obj = GetObject();
108
    obj->Init(arg1);
109
    return obj->get();
110
  }
111
112
  // Destroy an object, running its destructor and returning it to the
113
  // free-list.
114
3
  void Destroy(T *t) {
115
3
    CHECK_NOTNULL(t);
116
3
    ListNode *node = static_cast<ListNode *>(
117
3
      reinterpret_cast<ManualConstructor<T> *>(t));
118
119
3
    node->Destroy();
120
121
3
    DCHECK(!node->is_on_freelist);
122
3
    node->is_on_freelist = true;
123
3
    node->next_on_free_list = free_list_head_;
124
3
    free_list_head_ = node;
125
3
  }
126
127
  // Create a scoped_ptr wrapper around the given pointer which came from this
128
  // pool.
129
  // When the scoped_ptr goes out of scope, the object will get released back
130
  // to the pool.
131
1
  scoped_ptr make_scoped_ptr(T *ptr) {
132
1
    return scoped_ptr(ptr, deleter_);
133
1
  }
134
135
 private:
136
  class ListNode : ManualConstructor<T> {
137
    friend class ObjectPool<T>;
138
139
    ListNode *next_on_free_list;
140
    ListNode *next_on_alloc_list;
141
142
    bool is_on_freelist;
143
  };
144
145
146
4
  ManualConstructor<T> *GetObject() {
147
4
    if (free_list_head_ != NULL) {
148
1
      ListNode *tmp = free_list_head_;
149
1
      free_list_head_ = tmp->next_on_free_list;
150
1
      tmp->next_on_free_list = NULL;
151
1
      DCHECK(tmp->is_on_freelist);
152
1
      tmp->is_on_freelist = false;
153
154
1
      return static_cast<ManualConstructor<T> *>(tmp);
155
1
    }
156
3
    auto new_node = new ListNode();
157
3
    new_node->next_on_free_list = NULL;
158
3
    new_node->next_on_alloc_list = alloc_list_head_;
159
3
    new_node->is_on_freelist = false;
160
3
    alloc_list_head_ = new_node;
161
3
    return new_node;
162
3
  }
163
164
  // Keeps track of free objects in this pool.
165
  ListNode *free_list_head_;
166
167
  // Keeps track of all objects ever allocated by this pool.
168
  ListNode *alloc_list_head_;
169
170
  deleter_type deleter_;
171
};
172
173
// Functor which returns the passed objects to a specific object pool.
174
// This can be used in conjunction with scoped_ptr to automatically release
175
// an object back to a pool when it goes out of scope.
176
template<class T>
177
class ReturnToPool {
178
 public:
179
  explicit ReturnToPool(ObjectPool<T> *pool) :
180
2
    pool_(pool) {
181
2
  }
182
183
1
  inline void operator()(T *ptr) const {
184
1
    pool_->Destroy(ptr);
185
1
  }
186
187
 private:
188
  ObjectPool<T> *pool_;
189
};
190
191
template <class T>
192
struct DefaultFactory {
193
1
  T* operator()() const {
194
1
    return new T;
195
1
  }
196
};
197
198
template <class T>
199
class ThreadSafeObjectPool {
200
 public:
201
  typedef std::function<T*()> Factory;
202
  typedef std::function<void(T*)> Deleter;
203
204
  explicit ThreadSafeObjectPool(Factory factory = DefaultFactory<T>(),
205
                                Deleter deleter = std::default_delete<T>())
206
12.5k
      : factory_(std::move(factory)), deleter_(std::move(deleter)) {
207
    // Need the actual number of CPUs, so we do not use the Gflag value
208
12.5k
    size_t num_cpus = base::RawNumCPUs();
209
12.5k
    pools_.reserve(num_cpus);
210
137k
    while (pools_.size() != num_cpus) {
211
125k
      pools_.emplace_back(50);
212
125k
    }
213
12.5k
  }
_ZN2yb20ThreadSafeObjectPoolINS_2ql6ParserEEC2ENSt3__18functionIFPS2_vEEENS5_IFvS6_EEE
Line
Count
Source
206
10.6k
      : factory_(std::move(factory)), deleter_(std::move(deleter)) {
207
    // Need the actual number of CPUs, so we do not use the Gflag value
208
10.6k
    size_t num_cpus = base::RawNumCPUs();
209
10.6k
    pools_.reserve(num_cpus);
210
117k
    while (pools_.size() != num_cpus) {
211
106k
      pools_.emplace_back(50);
212
106k
    }
213
10.6k
  }
_ZN2yb20ThreadSafeObjectPoolINS_8internal9ArenaBaseINS1_21ThreadSafeArenaTraitsEEEEC2ENSt3__18functionIFPS4_vEEENS7_IFvS8_EEE
Line
Count
Source
206
1.87k
      : factory_(std::move(factory)), deleter_(std::move(deleter)) {
207
    // Need the actual number of CPUs, so we do not use the Gflag value
208
1.87k
    size_t num_cpus = base::RawNumCPUs();
209
1.87k
    pools_.reserve(num_cpus);
210
20.6k
    while (pools_.size() != num_cpus) {
211
18.7k
      pools_.emplace_back(50);
212
18.7k
    }
213
1.87k
  }
214
215
649
  ~ThreadSafeObjectPool() {
216
649
    T* object;
217
6.49k
    for (auto& pool : pools_) {
218
31.5k
      while (pool.pop(object)) {
219
25.0k
        deleter_(object);
220
25.0k
      }
221
6.49k
    }
222
649
  }
_ZN2yb20ThreadSafeObjectPoolINS_2ql6ParserEED2Ev
Line
Count
Source
215
288
  ~ThreadSafeObjectPool() {
216
288
    T* object;
217
2.88k
    for (auto& pool : pools_) {
218
2.88k
      while (pool.pop(object)) {
219
1
        deleter_(object);
220
1
      }
221
2.88k
    }
222
288
  }
_ZN2yb20ThreadSafeObjectPoolINS_8internal9ArenaBaseINS1_21ThreadSafeArenaTraitsEEEED2Ev
Line
Count
Source
215
361
  ~ThreadSafeObjectPool() {
216
361
    T* object;
217
3.61k
    for (auto& pool : pools_) {
218
28.6k
      while (pool.pop(object)) {
219
25.0k
        deleter_(object);
220
25.0k
      }
221
3.61k
    }
222
361
  }
223
224
8.65M
  T* Take() {
225
8.65M
    auto& pool = pools_[GetCPU()];
226
8.65M
    T* result;
227
8.65M
    if (pool.pop(result)) {
228
6.37M
      return result;
229
6.37M
    }
230
2.27M
    return factory_();
231
2.27M
  }
_ZN2yb20ThreadSafeObjectPoolINS_2ql6ParserEE4TakeEv
Line
Count
Source
224
336k
  T* Take() {
225
336k
    auto& pool = pools_[GetCPU()];
226
336k
    T* result;
227
336k
    if (pool.pop(result)) {
228
318k
      return result;
229
318k
    }
230
17.6k
    return factory_();
231
17.6k
  }
_ZN2yb20ThreadSafeObjectPoolINS_8internal9ArenaBaseINS1_21ThreadSafeArenaTraitsEEEE4TakeEv
Line
Count
Source
224
8.31M
  T* Take() {
225
8.31M
    auto& pool = pools_[GetCPU()];
226
8.31M
    T* result;
227
8.31M
    if (pool.pop(result)) {
228
6.05M
      return result;
229
6.05M
    }
230
2.25M
    return factory_();
231
2.25M
  }
232
233
8.64M
  void Release(T* value) {
234
8.64M
    auto& pool = pools_[GetCPU()];
235
8.64M
    if (!pool.bounded_push(value)) {
236
2.07M
      deleter_(value);
237
2.07M
    }
238
8.64M
  }
_ZN2yb20ThreadSafeObjectPoolINS_2ql6ParserEE7ReleaseEPS2_
Line
Count
Source
233
329k
  void Release(T* value) {
234
329k
    auto& pool = pools_[GetCPU()];
235
329k
    if (!pool.bounded_push(value)) {
236
0
      deleter_(value);
237
0
    }
238
329k
  }
_ZN2yb20ThreadSafeObjectPoolINS_8internal9ArenaBaseINS1_21ThreadSafeArenaTraitsEEEE7ReleaseEPS4_
Line
Count
Source
233
8.31M
  void Release(T* value) {
234
8.31M
    auto& pool = pools_[GetCPU()];
235
8.31M
    if (!pool.bounded_push(value)) {
236
2.07M
      deleter_(value);
237
2.07M
    }
238
8.31M
  }
239
240
 private:
241
17.2M
  size_t GetCPU() const {
242
17.2M
#if defined(__APPLE__)
243
    // OSX doesn't have a way to get the CPU, so we'll pick a random one.
244
17.2M
    return std::hash<std::thread::id>()(std::this_thread::get_id()) % pools_.size();
245
#else
246
    size_t cpu = sched_getcpu();
247
    DCHECK_LT(cpu, pools_.size());
248
    return cpu;
249
#endif // defined(__APPLE__)
250
17.2M
  }
_ZNK2yb20ThreadSafeObjectPoolINS_2ql6ParserEE6GetCPUEv
Line
Count
Source
241
670k
  size_t GetCPU() const {
242
670k
#if defined(__APPLE__)
243
    // OSX doesn't have a way to get the CPU, so we'll pick a random one.
244
670k
    return std::hash<std::thread::id>()(std::this_thread::get_id()) % pools_.size();
245
#else
246
    size_t cpu = sched_getcpu();
247
    DCHECK_LT(cpu, pools_.size());
248
    return cpu;
249
#endif // defined(__APPLE__)
250
670k
  }
_ZNK2yb20ThreadSafeObjectPoolINS_8internal9ArenaBaseINS1_21ThreadSafeArenaTraitsEEEE6GetCPUEv
Line
Count
Source
241
16.6M
  size_t GetCPU() const {
242
16.6M
#if defined(__APPLE__)
243
    // OSX doesn't have a way to get the CPU, so we'll pick a random one.
244
16.6M
    return std::hash<std::thread::id>()(std::this_thread::get_id()) % pools_.size();
245
#else
246
    size_t cpu = sched_getcpu();
247
    DCHECK_LT(cpu, pools_.size());
248
    return cpu;
249
#endif // defined(__APPLE__)
250
16.6M
  }
251
252
  typedef boost::lockfree::stack<T*> Pool;
253
254
  Factory factory_;
255
  Deleter deleter_;
256
  boost::container::stable_vector<Pool> pools_;
257
};
258
259
} // namespace yb
260
261
#endif // YB_UTIL_OBJECT_POOL_H