/src/brpc/src/butil/resource_pool_inl.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 | | // bthread - An M:N threading library to make applications more concurrent. |
19 | | |
20 | | // Date: Sun Jul 13 15:04:18 CST 2014 |
21 | | |
22 | | #ifndef BUTIL_RESOURCE_POOL_INL_H |
23 | | #define BUTIL_RESOURCE_POOL_INL_H |
24 | | |
25 | | #include <iostream> // std::ostream |
26 | | #include <pthread.h> // pthread_mutex_t |
27 | | #include <algorithm> // std::max, std::min |
28 | | #include "butil/atomicops.h" // butil::atomic |
29 | | #include "butil/macros.h" // BAIDU_CACHELINE_ALIGNMENT |
30 | | #include "butil/scoped_lock.h" // BAIDU_SCOPED_LOCK |
31 | | #include "butil/thread_local.h" // thread_atexit |
32 | | #include "butil/memory/aligned_memory.h" // butil::AlignedMemory |
33 | | #include <vector> |
34 | | |
35 | | #ifdef BUTIL_RESOURCE_POOL_NEED_FREE_ITEM_NUM |
36 | | #define BAIDU_RESOURCE_POOL_FREE_ITEM_NUM_ADD1 \ |
37 | | (_global_nfree.fetch_add(1, butil::memory_order_relaxed)) |
38 | | #define BAIDU_RESOURCE_POOL_FREE_ITEM_NUM_SUB1 \ |
39 | | (_global_nfree.fetch_sub(1, butil::memory_order_relaxed)) |
40 | | #else |
41 | | #define BAIDU_RESOURCE_POOL_FREE_ITEM_NUM_ADD1 |
42 | | #define BAIDU_RESOURCE_POOL_FREE_ITEM_NUM_SUB1 |
43 | | #endif |
44 | | |
45 | | namespace butil { |
46 | | |
47 | | template <typename T> |
48 | | struct ResourceId { |
49 | | uint64_t value; |
50 | | |
51 | | operator uint64_t() const { |
52 | | return value; |
53 | | } |
54 | | |
55 | | template <typename T2> |
56 | | ResourceId<T2> cast() const { |
57 | | ResourceId<T2> id = { value }; |
58 | | return id; |
59 | | } |
60 | | }; |
61 | | |
62 | | template <typename T, size_t NITEM> |
63 | | struct ResourcePoolFreeChunk { |
64 | | size_t nfree; |
65 | | ResourceId<T> ids[NITEM]; |
66 | | }; |
67 | | // for gcc 3.4.5 |
68 | | template <typename T> |
69 | | struct ResourcePoolFreeChunk<T, 0> { |
70 | | size_t nfree; |
71 | | ResourceId<T> ids[0]; |
72 | | }; |
73 | | |
74 | | struct ResourcePoolInfo { |
75 | | size_t local_pool_num; |
76 | | size_t block_group_num; |
77 | | size_t block_num; |
78 | | size_t item_num; |
79 | | size_t block_item_num; |
80 | | size_t free_chunk_item_num; |
81 | | size_t total_size; |
82 | | #ifdef BUTIL_RESOURCE_POOL_NEED_FREE_ITEM_NUM |
83 | | size_t free_item_num; |
84 | | #endif |
85 | | }; |
86 | | |
87 | | static const size_t RP_MAX_BLOCK_NGROUP = 65536; |
88 | | static const size_t RP_GROUP_NBLOCK_NBIT = 16; |
89 | | static const size_t RP_GROUP_NBLOCK = (1UL << RP_GROUP_NBLOCK_NBIT); |
90 | | static const size_t RP_INITIAL_FREE_LIST_SIZE = 1024; |
91 | | |
92 | | template <typename T> |
93 | | class ResourcePoolBlockItemNum { |
94 | | static const size_t N1 = ResourcePoolBlockMaxSize<T>::value / sizeof(T); |
95 | | static const size_t N2 = (N1 < 1 ? 1 : N1); |
96 | | public: |
97 | | static const size_t value = (N2 > ResourcePoolBlockMaxItem<T>::value ? |
98 | | ResourcePoolBlockMaxItem<T>::value : N2); |
99 | | }; |
100 | | |
101 | | |
102 | | template <typename T> |
103 | | class BAIDU_CACHELINE_ALIGNMENT ResourcePool { |
104 | | public: |
105 | | static const size_t BLOCK_NITEM = ResourcePoolBlockItemNum<T>::value; |
106 | | static const size_t FREE_CHUNK_NITEM = BLOCK_NITEM; |
107 | | |
108 | | // Free identifiers are batched in a FreeChunk before they're added to |
109 | | // global list(_free_chunks). |
110 | | typedef ResourcePoolFreeChunk<T, FREE_CHUNK_NITEM> FreeChunk; |
111 | | typedef ResourcePoolFreeChunk<T, 0> DynamicFreeChunk; |
112 | | |
113 | | typedef AlignedMemory<sizeof(T), __alignof__(T)> BlockItem; |
114 | | // When a thread needs memory, it allocates a Block. To improve locality, |
115 | | // items in the Block are only used by the thread. |
116 | | // To support cache-aligned objects, align Block.items by cacheline. |
117 | | struct BAIDU_CACHELINE_ALIGNMENT Block { |
118 | | BlockItem items[BLOCK_NITEM]; |
119 | | size_t nitem; |
120 | | |
121 | 0 | Block() : nitem(0) {}Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::Block::Block() Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::Block::Block() Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::Block::Block() Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::Block::Block() Unexecuted instantiation: butil::ResourcePool<brpc::IOEventData>::Block::Block() Unexecuted instantiation: butil::ResourcePool<bthread::ExecutionQueueBase>::Block::Block() Unexecuted instantiation: butil::ResourcePool<bthread::Id>::Block::Block() |
122 | | }; |
123 | | |
124 | | // A Resource addresses at most RP_MAX_BLOCK_NGROUP BlockGroups, |
125 | | // each BlockGroup addresses at most RP_GROUP_NBLOCK blocks. So a |
126 | | // resource addresses at most RP_MAX_BLOCK_NGROUP * RP_GROUP_NBLOCK Blocks. |
127 | | struct BlockGroup { |
128 | | butil::atomic<size_t> nblock; |
129 | | butil::atomic<Block*> blocks[RP_GROUP_NBLOCK]; |
130 | | |
131 | 0 | BlockGroup() : nblock(0) { |
132 | | // We fetch_add nblock in add_block() before setting the entry, |
133 | | // thus address_resource() may sees the unset entry. Initialize |
134 | | // all entries to NULL makes such address_resource() return NULL. |
135 | 0 | memset(static_cast<void*>(blocks), 0, sizeof(butil::atomic<Block*>) * RP_GROUP_NBLOCK); |
136 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::BlockGroup::BlockGroup() Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::BlockGroup::BlockGroup() Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::BlockGroup::BlockGroup() Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::BlockGroup::BlockGroup() Unexecuted instantiation: butil::ResourcePool<brpc::IOEventData>::BlockGroup::BlockGroup() Unexecuted instantiation: butil::ResourcePool<bthread::ExecutionQueueBase>::BlockGroup::BlockGroup() Unexecuted instantiation: butil::ResourcePool<bthread::Id>::BlockGroup::BlockGroup() |
137 | | }; |
138 | | |
139 | | |
140 | | // Each thread has an instance of this class. |
141 | | class BAIDU_CACHELINE_ALIGNMENT LocalPool { |
142 | | public: |
143 | | explicit LocalPool(ResourcePool* pool) |
144 | 0 | : _pool(pool) |
145 | 0 | , _cur_block(NULL) |
146 | 0 | , _cur_block_index(0) { |
147 | 0 | _cur_free.nfree = 0; |
148 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::LocalPool::LocalPool(butil::ResourcePool<bthread::TaskMeta>*) Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::LocalPool::LocalPool(butil::ResourcePool<bthread::TimerThread::Task>*) Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::LocalPool::LocalPool(butil::ResourcePool<butil::details::ExtendedEndPoint>*) Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::LocalPool::LocalPool(butil::ResourcePool<brpc::Socket>*) Unexecuted instantiation: butil::ResourcePool<brpc::IOEventData>::LocalPool::LocalPool(butil::ResourcePool<brpc::IOEventData>*) Unexecuted instantiation: butil::ResourcePool<bthread::ExecutionQueueBase>::LocalPool::LocalPool(butil::ResourcePool<bthread::ExecutionQueueBase>*) Unexecuted instantiation: butil::ResourcePool<bthread::Id>::LocalPool::LocalPool(butil::ResourcePool<bthread::Id>*) |
149 | | |
150 | 0 | ~LocalPool() { |
151 | | // Add to global _free_chunks if there're some free resources |
152 | 0 | if (_cur_free.nfree) { |
153 | 0 | _pool->push_free_chunk(_cur_free); |
154 | 0 | } |
155 | |
|
156 | 0 | _pool->clear_from_destructor_of_local_pool(); |
157 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::LocalPool::~LocalPool() Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::LocalPool::~LocalPool() Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::LocalPool::~LocalPool() Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::LocalPool::~LocalPool() Unexecuted instantiation: butil::ResourcePool<brpc::IOEventData>::LocalPool::~LocalPool() Unexecuted instantiation: butil::ResourcePool<bthread::ExecutionQueueBase>::LocalPool::~LocalPool() Unexecuted instantiation: butil::ResourcePool<bthread::Id>::LocalPool::~LocalPool() |
158 | | |
159 | 0 | static void delete_local_pool(void* arg) { |
160 | 0 | delete(LocalPool*)arg; |
161 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::LocalPool::delete_local_pool(void*) Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::LocalPool::delete_local_pool(void*) Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::LocalPool::delete_local_pool(void*) Unexecuted instantiation: butil::ResourcePool<brpc::IOEventData>::LocalPool::delete_local_pool(void*) Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::LocalPool::delete_local_pool(void*) Unexecuted instantiation: butil::ResourcePool<bthread::ExecutionQueueBase>::LocalPool::delete_local_pool(void*) Unexecuted instantiation: butil::ResourcePool<bthread::Id>::LocalPool::delete_local_pool(void*) |
162 | | |
163 | | // We need following macro to construct T with different CTOR_ARGS |
164 | | // which may include parenthesis because when T is POD, "new T()" |
165 | | // and "new T" are different: former one sets all fields to 0 which |
166 | | // we don't want. |
167 | | #define BAIDU_RESOURCE_POOL_GET(CTOR_ARGS) \ |
168 | | /* Fetch local free id */ \ |
169 | 0 | if (_cur_free.nfree) { \ |
170 | 0 | const ResourceId<T> free_id = _cur_free.ids[--_cur_free.nfree]; \ |
171 | 0 | *id = free_id; \ |
172 | 0 | BAIDU_RESOURCE_POOL_FREE_ITEM_NUM_SUB1; \ |
173 | 0 | return unsafe_address_resource(free_id); \ |
174 | 0 | } \ |
175 | 0 | /* Fetch a FreeChunk from global. \ |
176 | 0 | TODO: Popping from _free needs to copy a FreeChunk which is \ |
177 | 0 | costly, but hardly impacts amortized performance. */ \ |
178 | 0 | if (_pool->pop_free_chunk(_cur_free)) { \ |
179 | 0 | --_cur_free.nfree; \ |
180 | 0 | const ResourceId<T> free_id = _cur_free.ids[_cur_free.nfree]; \ |
181 | 0 | *id = free_id; \ |
182 | 0 | BAIDU_RESOURCE_POOL_FREE_ITEM_NUM_SUB1; \ |
183 | 0 | return unsafe_address_resource(free_id); \ |
184 | 0 | } \ |
185 | 0 | T* p = NULL; \ |
186 | 0 | /* Fetch memory from local block */ \ |
187 | 0 | if (_cur_block && _cur_block->nitem < BLOCK_NITEM) { \ |
188 | 0 | id->value = _cur_block_index * BLOCK_NITEM + _cur_block->nitem; \ |
189 | 0 | auto item = _cur_block->items + _cur_block->nitem; \ |
190 | 0 | p = new (item->void_data()) T CTOR_ARGS; \ |
191 | 0 | if (!ResourcePoolValidator<T>::validate(p)) { \ |
192 | 0 | p->~T(); \ |
193 | 0 | return NULL; \ |
194 | 0 | } \ |
195 | 0 | ++_cur_block->nitem; \ |
196 | 0 | return p; \ |
197 | 0 | } \ |
198 | 0 | /* Fetch a Block from global */ \ |
199 | 0 | _cur_block = add_block(&_cur_block_index); \ |
200 | 0 | if (_cur_block != NULL) { \ |
201 | 0 | id->value = _cur_block_index * BLOCK_NITEM + _cur_block->nitem; \ |
202 | 0 | auto item = _cur_block->items + _cur_block->nitem; \ |
203 | 0 | p = new (item->void_data()) T CTOR_ARGS; \ |
204 | 0 | if (!ResourcePoolValidator<T>::validate(p)) { \ |
205 | 0 | p->~T(); \ |
206 | 0 | return NULL; \ |
207 | 0 | } \ |
208 | 0 | ++_cur_block->nitem; \ |
209 | 0 | return p; \ |
210 | 0 | } \ |
211 | 0 | return NULL; \ |
212 | | |
213 | | |
214 | 0 | inline T* get(ResourceId<T>* id) { |
215 | 0 | BAIDU_RESOURCE_POOL_GET(); |
216 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::LocalPool::get(butil::ResourceId<bthread::TaskMeta>*) Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::LocalPool::get(butil::ResourceId<bthread::TimerThread::Task>*) Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::LocalPool::get(butil::ResourceId<butil::details::ExtendedEndPoint>*) Unexecuted instantiation: butil::ResourcePool<bthread::Id>::LocalPool::get(butil::ResourceId<bthread::Id>*) |
217 | | |
218 | | template<typename... Args> |
219 | 0 | inline T* get(ResourceId<T>* id, Args&&... args) { |
220 | 0 | BAIDU_RESOURCE_POOL_GET((std::forward<Args>(args)...)); |
221 | 0 | } Unexecuted instantiation: brpc::Socket* butil::ResourcePool<brpc::Socket>::LocalPool::get<brpc::VersionedRefWithId<brpc::Socket>::Forbidden>(butil::ResourceId<brpc::Socket>*, brpc::VersionedRefWithId<brpc::Socket>::Forbidden&&) Unexecuted instantiation: brpc::IOEventData* butil::ResourcePool<brpc::IOEventData>::LocalPool::get<brpc::VersionedRefWithId<brpc::IOEventData>::Forbidden>(butil::ResourceId<brpc::IOEventData>*, brpc::VersionedRefWithId<brpc::IOEventData>::Forbidden&&) Unexecuted instantiation: bthread::ExecutionQueueBase* butil::ResourcePool<bthread::ExecutionQueueBase>::LocalPool::get<bthread::ExecutionQueueBase::Forbidden>(butil::ResourceId<bthread::ExecutionQueueBase>*, bthread::ExecutionQueueBase::Forbidden&&) |
222 | | |
223 | | #undef BAIDU_RESOURCE_POOL_GET |
224 | | |
225 | 0 | inline int return_resource(ResourceId<T> id) { |
226 | | // Return to local free list |
227 | 0 | if (_cur_free.nfree < ResourcePool::free_chunk_nitem()) { |
228 | 0 | _cur_free.ids[_cur_free.nfree++] = id; |
229 | 0 | BAIDU_RESOURCE_POOL_FREE_ITEM_NUM_ADD1; |
230 | 0 | return 0; |
231 | 0 | } |
232 | | // Local free list is full, return it to global. |
233 | | // For copying issue, check comment in upper get() |
234 | 0 | if (_pool->push_free_chunk(_cur_free)) { |
235 | 0 | _cur_free.nfree = 1; |
236 | 0 | _cur_free.ids[0] = id; |
237 | 0 | BAIDU_RESOURCE_POOL_FREE_ITEM_NUM_ADD1; |
238 | 0 | return 0; |
239 | 0 | } |
240 | 0 | return -1; |
241 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::LocalPool::return_resource(butil::ResourceId<bthread::TaskMeta>) Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::LocalPool::return_resource(butil::ResourceId<bthread::TimerThread::Task>) Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::LocalPool::return_resource(butil::ResourceId<butil::details::ExtendedEndPoint>) Unexecuted instantiation: butil::ResourcePool<brpc::IOEventData>::LocalPool::return_resource(butil::ResourceId<brpc::IOEventData>) Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::LocalPool::return_resource(butil::ResourceId<brpc::Socket>) Unexecuted instantiation: butil::ResourcePool<bthread::ExecutionQueueBase>::LocalPool::return_resource(butil::ResourceId<bthread::ExecutionQueueBase>) Unexecuted instantiation: butil::ResourcePool<bthread::Id>::LocalPool::return_resource(butil::ResourceId<bthread::Id>) |
242 | | |
243 | | private: |
244 | | ResourcePool* _pool; |
245 | | Block* _cur_block; |
246 | | size_t _cur_block_index; |
247 | | FreeChunk _cur_free; |
248 | | }; |
249 | | |
250 | 0 | static inline T* unsafe_address_resource(ResourceId<T> id) { |
251 | 0 | const size_t block_index = id.value / BLOCK_NITEM; |
252 | 0 | return (T*)(_block_groups[(block_index >> RP_GROUP_NBLOCK_NBIT)] |
253 | 0 | .load(butil::memory_order_consume) |
254 | 0 | ->blocks[(block_index & (RP_GROUP_NBLOCK - 1))] |
255 | 0 | .load(butil::memory_order_consume)->items) + |
256 | 0 | id.value - block_index * BLOCK_NITEM; |
257 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::unsafe_address_resource(butil::ResourceId<bthread::TaskMeta>) Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::unsafe_address_resource(butil::ResourceId<bthread::TimerThread::Task>) Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::unsafe_address_resource(butil::ResourceId<butil::details::ExtendedEndPoint>) Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::unsafe_address_resource(butil::ResourceId<brpc::Socket>) Unexecuted instantiation: butil::ResourcePool<brpc::IOEventData>::unsafe_address_resource(butil::ResourceId<brpc::IOEventData>) Unexecuted instantiation: butil::ResourcePool<bthread::ExecutionQueueBase>::unsafe_address_resource(butil::ResourceId<bthread::ExecutionQueueBase>) Unexecuted instantiation: butil::ResourcePool<bthread::Id>::unsafe_address_resource(butil::ResourceId<bthread::Id>) |
258 | | |
259 | 0 | static inline T* address_resource(ResourceId<T> id) { |
260 | 0 | const size_t block_index = id.value / BLOCK_NITEM; |
261 | 0 | const size_t group_index = (block_index >> RP_GROUP_NBLOCK_NBIT); |
262 | 0 | if (__builtin_expect(group_index < RP_MAX_BLOCK_NGROUP, 1)) { |
263 | 0 | BlockGroup* bg = |
264 | 0 | _block_groups[group_index].load(butil::memory_order_consume); |
265 | 0 | if (__builtin_expect(bg != NULL, 1)) { |
266 | 0 | Block* b = bg->blocks[block_index & (RP_GROUP_NBLOCK - 1)] |
267 | 0 | .load(butil::memory_order_consume); |
268 | 0 | if (__builtin_expect(b != NULL, 1)) { |
269 | 0 | const size_t offset = id.value - block_index * BLOCK_NITEM; |
270 | 0 | if (__builtin_expect(offset < b->nitem, 1)) { |
271 | 0 | return (T*)b->items + offset; |
272 | 0 | } |
273 | 0 | } |
274 | 0 | } |
275 | 0 | } |
276 | | |
277 | 0 | return NULL; |
278 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::address_resource(butil::ResourceId<bthread::TaskMeta>) Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::address_resource(butil::ResourceId<bthread::TimerThread::Task>) Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::address_resource(butil::ResourceId<butil::details::ExtendedEndPoint>) Unexecuted instantiation: butil::ResourcePool<brpc::IOEventData>::address_resource(butil::ResourceId<brpc::IOEventData>) Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::address_resource(butil::ResourceId<brpc::Socket>) Unexecuted instantiation: butil::ResourcePool<bthread::ExecutionQueueBase>::address_resource(butil::ResourceId<bthread::ExecutionQueueBase>) Unexecuted instantiation: butil::ResourcePool<bthread::Id>::address_resource(butil::ResourceId<bthread::Id>) |
279 | | |
280 | | template<typename... Args> |
281 | 0 | inline T* get_resource(ResourceId<T>* id, Args&&... args) { |
282 | 0 | LocalPool* lp = get_or_new_local_pool(); |
283 | 0 | if (__builtin_expect(lp != NULL, 1)) { |
284 | 0 | return lp->get(id, std::forward<Args>(args)...); |
285 | 0 | } |
286 | 0 | return NULL; |
287 | 0 | } Unexecuted instantiation: bthread::TaskMeta* butil::ResourcePool<bthread::TaskMeta>::get_resource<>(butil::ResourceId<bthread::TaskMeta>*) Unexecuted instantiation: bthread::TimerThread::Task* butil::ResourcePool<bthread::TimerThread::Task>::get_resource<>(butil::ResourceId<bthread::TimerThread::Task>*) Unexecuted instantiation: butil::details::ExtendedEndPoint* butil::ResourcePool<butil::details::ExtendedEndPoint>::get_resource<>(butil::ResourceId<butil::details::ExtendedEndPoint>*) Unexecuted instantiation: brpc::Socket* butil::ResourcePool<brpc::Socket>::get_resource<brpc::VersionedRefWithId<brpc::Socket>::Forbidden>(butil::ResourceId<brpc::Socket>*, brpc::VersionedRefWithId<brpc::Socket>::Forbidden&&) Unexecuted instantiation: brpc::IOEventData* butil::ResourcePool<brpc::IOEventData>::get_resource<brpc::VersionedRefWithId<brpc::IOEventData>::Forbidden>(butil::ResourceId<brpc::IOEventData>*, brpc::VersionedRefWithId<brpc::IOEventData>::Forbidden&&) Unexecuted instantiation: bthread::ExecutionQueueBase* butil::ResourcePool<bthread::ExecutionQueueBase>::get_resource<bthread::ExecutionQueueBase::Forbidden>(butil::ResourceId<bthread::ExecutionQueueBase>*, bthread::ExecutionQueueBase::Forbidden&&) Unexecuted instantiation: bthread::Id* butil::ResourcePool<bthread::Id>::get_resource<>(butil::ResourceId<bthread::Id>*) |
288 | | |
289 | 0 | inline int return_resource(ResourceId<T> id) { |
290 | 0 | LocalPool* lp = get_or_new_local_pool(); |
291 | 0 | if (__builtin_expect(lp != NULL, 1)) { |
292 | 0 | return lp->return_resource(id); |
293 | 0 | } |
294 | 0 | return -1; |
295 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::return_resource(butil::ResourceId<bthread::TaskMeta>) Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::return_resource(butil::ResourceId<bthread::TimerThread::Task>) Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::return_resource(butil::ResourceId<butil::details::ExtendedEndPoint>) Unexecuted instantiation: butil::ResourcePool<brpc::IOEventData>::return_resource(butil::ResourceId<brpc::IOEventData>) Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::return_resource(butil::ResourceId<brpc::Socket>) Unexecuted instantiation: butil::ResourcePool<bthread::ExecutionQueueBase>::return_resource(butil::ResourceId<bthread::ExecutionQueueBase>) Unexecuted instantiation: butil::ResourcePool<bthread::Id>::return_resource(butil::ResourceId<bthread::Id>) |
296 | | |
297 | | void clear_resources() { |
298 | | LocalPool* lp = _local_pool; |
299 | | if (lp) { |
300 | | _local_pool = NULL; |
301 | | butil::thread_atexit_cancel(LocalPool::delete_local_pool, lp); |
302 | | delete lp; |
303 | | } |
304 | | } |
305 | | |
306 | 0 | static inline size_t free_chunk_nitem() { |
307 | 0 | const size_t n = ResourcePoolFreeChunkMaxItem<T>::value(); |
308 | 0 | return n < FREE_CHUNK_NITEM ? n : FREE_CHUNK_NITEM; |
309 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::free_chunk_nitem() Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::free_chunk_nitem() Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::free_chunk_nitem() Unexecuted instantiation: butil::ResourcePool<brpc::IOEventData>::free_chunk_nitem() Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::free_chunk_nitem() Unexecuted instantiation: butil::ResourcePool<bthread::ExecutionQueueBase>::free_chunk_nitem() Unexecuted instantiation: butil::ResourcePool<bthread::Id>::free_chunk_nitem() |
310 | | |
311 | | template <typename F> |
312 | 0 | void for_each_resource(F const& f) { |
313 | 0 | for (size_t i = 0; i < _ngroup.load(butil::memory_order_acquire); ++i) { |
314 | 0 | BlockGroup* bg = _block_groups[i].load(butil::memory_order_consume); |
315 | 0 | if (NULL == bg) { |
316 | 0 | break; |
317 | 0 | } |
318 | 0 | size_t nblock = std::min(bg->nblock.load(butil::memory_order_relaxed), |
319 | 0 | RP_GROUP_NBLOCK); |
320 | 0 | for (size_t j = 0; j < nblock; ++j) { |
321 | 0 | Block* b = bg->blocks[j].load(butil::memory_order_consume); |
322 | 0 | if (NULL != b) { |
323 | 0 | for (size_t k = 0; k < b->nitem; ++k) { |
324 | 0 | auto item = b->items + k; |
325 | 0 | T* obj = (T*)item->void_data(); |
326 | 0 | f(obj); |
327 | 0 | } |
328 | 0 | } |
329 | 0 | } |
330 | 0 | } |
331 | 0 | } |
332 | | |
333 | | // Number of all allocated objects, including being used and free. |
334 | 0 | ResourcePoolInfo describe_resources() const { |
335 | 0 | ResourcePoolInfo info; |
336 | 0 | info.local_pool_num = _nlocal.load(butil::memory_order_relaxed); |
337 | 0 | info.block_group_num = _ngroup.load(butil::memory_order_acquire); |
338 | 0 | info.block_num = 0; |
339 | 0 | info.item_num = 0; |
340 | 0 | info.free_chunk_item_num = free_chunk_nitem(); |
341 | 0 | info.block_item_num = BLOCK_NITEM; |
342 | | #ifdef BUTIL_RESOURCE_POOL_NEED_FREE_ITEM_NUM |
343 | | info.free_item_num = _global_nfree.load(butil::memory_order_relaxed); |
344 | | #endif |
345 | |
|
346 | 0 | for (size_t i = 0; i < info.block_group_num; ++i) { |
347 | 0 | BlockGroup* bg = _block_groups[i].load(butil::memory_order_consume); |
348 | 0 | if (NULL == bg) { |
349 | 0 | break; |
350 | 0 | } |
351 | 0 | size_t nblock = std::min(bg->nblock.load(butil::memory_order_relaxed), |
352 | 0 | RP_GROUP_NBLOCK); |
353 | 0 | info.block_num += nblock; |
354 | 0 | for (size_t j = 0; j < nblock; ++j) { |
355 | 0 | Block* b = bg->blocks[j].load(butil::memory_order_consume); |
356 | 0 | if (NULL != b) { |
357 | 0 | info.item_num += b->nitem; |
358 | 0 | } |
359 | 0 | } |
360 | 0 | } |
361 | 0 | info.total_size = info.block_num * info.block_item_num * sizeof(T); |
362 | 0 | return info; |
363 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::Id>::describe_resources() const Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::describe_resources() const |
364 | | |
365 | 0 | static inline ResourcePool* singleton() { |
366 | 0 | ResourcePool* p = _singleton.load(butil::memory_order_consume); |
367 | 0 | if (p) { |
368 | 0 | return p; |
369 | 0 | } |
370 | 0 | pthread_mutex_lock(&_singleton_mutex); |
371 | 0 | p = _singleton.load(butil::memory_order_consume); |
372 | 0 | if (!p) { |
373 | 0 | p = new ResourcePool(); |
374 | 0 | _singleton.store(p, butil::memory_order_release); |
375 | 0 | } |
376 | 0 | pthread_mutex_unlock(&_singleton_mutex); |
377 | 0 | return p; |
378 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::singleton() Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::singleton() Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::singleton() Unexecuted instantiation: butil::ResourcePool<brpc::IOEventData>::singleton() Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::singleton() Unexecuted instantiation: butil::ResourcePool<bthread::ExecutionQueueBase>::singleton() Unexecuted instantiation: butil::ResourcePool<bthread::Id>::singleton() |
379 | | |
380 | | private: |
381 | 0 | ResourcePool() { |
382 | 0 | _free_chunks.reserve(RP_INITIAL_FREE_LIST_SIZE); |
383 | 0 | pthread_mutex_init(&_free_chunks_mutex, NULL); |
384 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::ResourcePool() Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::ResourcePool() Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::ResourcePool() Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::ResourcePool() Unexecuted instantiation: butil::ResourcePool<brpc::IOEventData>::ResourcePool() Unexecuted instantiation: butil::ResourcePool<bthread::ExecutionQueueBase>::ResourcePool() Unexecuted instantiation: butil::ResourcePool<bthread::Id>::ResourcePool() |
385 | | |
386 | | ~ResourcePool() { |
387 | | pthread_mutex_destroy(&_free_chunks_mutex); |
388 | | } |
389 | | |
390 | | // Create a Block and append it to right-most BlockGroup. |
391 | 0 | static Block* add_block(size_t* index) { |
392 | 0 | Block* const new_block = new (std::nothrow) Block; |
393 | 0 | if (NULL == new_block) { |
394 | 0 | return NULL; |
395 | 0 | } |
396 | | |
397 | 0 | size_t ngroup; |
398 | 0 | do { |
399 | 0 | ngroup = _ngroup.load(butil::memory_order_acquire); |
400 | 0 | if (ngroup >= 1) { |
401 | 0 | BlockGroup* const g = |
402 | 0 | _block_groups[ngroup - 1].load(butil::memory_order_consume); |
403 | 0 | const size_t block_index = |
404 | 0 | g->nblock.fetch_add(1, butil::memory_order_relaxed); |
405 | 0 | if (block_index < RP_GROUP_NBLOCK) { |
406 | 0 | g->blocks[block_index].store( |
407 | 0 | new_block, butil::memory_order_release); |
408 | 0 | *index = (ngroup - 1) * RP_GROUP_NBLOCK + block_index; |
409 | 0 | return new_block; |
410 | 0 | } |
411 | 0 | g->nblock.fetch_sub(1, butil::memory_order_relaxed); |
412 | 0 | } |
413 | 0 | } while (add_block_group(ngroup)); |
414 | | |
415 | | // Fail to add_block_group. |
416 | 0 | delete new_block; |
417 | 0 | return NULL; |
418 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::add_block(unsigned long*) Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::add_block(unsigned long*) Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::add_block(unsigned long*) Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::add_block(unsigned long*) Unexecuted instantiation: butil::ResourcePool<brpc::IOEventData>::add_block(unsigned long*) Unexecuted instantiation: butil::ResourcePool<bthread::ExecutionQueueBase>::add_block(unsigned long*) Unexecuted instantiation: butil::ResourcePool<bthread::Id>::add_block(unsigned long*) |
419 | | |
420 | | // Create a BlockGroup and append it to _block_groups. |
421 | | // Shall be called infrequently because a BlockGroup is pretty big. |
422 | 0 | static bool add_block_group(size_t old_ngroup) { |
423 | 0 | BlockGroup* bg = NULL; |
424 | 0 | BAIDU_SCOPED_LOCK(_block_group_mutex); |
425 | 0 | const size_t ngroup = _ngroup.load(butil::memory_order_acquire); |
426 | 0 | if (ngroup != old_ngroup) { |
427 | | // Other thread got lock and added group before this thread. |
428 | 0 | return true; |
429 | 0 | } |
430 | 0 | if (ngroup < RP_MAX_BLOCK_NGROUP) { |
431 | 0 | bg = new(std::nothrow) BlockGroup; |
432 | 0 | if (NULL != bg) { |
433 | | // Release fence is paired with consume fence in address() and |
434 | | // add_block() to avoid un-constructed bg to be seen by other |
435 | | // threads. |
436 | 0 | _block_groups[ngroup].store(bg, butil::memory_order_release); |
437 | 0 | _ngroup.store(ngroup + 1, butil::memory_order_release); |
438 | 0 | } |
439 | 0 | } |
440 | 0 | return bg != NULL; |
441 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::add_block_group(unsigned long) Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::add_block_group(unsigned long) Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::add_block_group(unsigned long) Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::add_block_group(unsigned long) Unexecuted instantiation: butil::ResourcePool<brpc::IOEventData>::add_block_group(unsigned long) Unexecuted instantiation: butil::ResourcePool<bthread::ExecutionQueueBase>::add_block_group(unsigned long) Unexecuted instantiation: butil::ResourcePool<bthread::Id>::add_block_group(unsigned long) |
442 | | |
443 | 0 | inline LocalPool* get_or_new_local_pool() { |
444 | 0 | LocalPool* lp = BAIDU_GET_VOLATILE_THREAD_LOCAL(_local_pool); |
445 | 0 | if (lp != NULL) { |
446 | 0 | return lp; |
447 | 0 | } |
448 | 0 | lp = new(std::nothrow) LocalPool(this); |
449 | 0 | if (NULL == lp) { |
450 | 0 | return NULL; |
451 | 0 | } |
452 | 0 | BAIDU_SCOPED_LOCK(_change_thread_mutex); //avoid race with clear() |
453 | 0 | BAIDU_SET_VOLATILE_THREAD_LOCAL(_local_pool, lp); |
454 | 0 | butil::thread_atexit(LocalPool::delete_local_pool, lp); |
455 | 0 | _nlocal.fetch_add(1, butil::memory_order_relaxed); |
456 | 0 | return lp; |
457 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::get_or_new_local_pool() Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::get_or_new_local_pool() Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::get_or_new_local_pool() Unexecuted instantiation: butil::ResourcePool<brpc::IOEventData>::get_or_new_local_pool() Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::get_or_new_local_pool() Unexecuted instantiation: butil::ResourcePool<bthread::ExecutionQueueBase>::get_or_new_local_pool() Unexecuted instantiation: butil::ResourcePool<bthread::Id>::get_or_new_local_pool() |
458 | | |
459 | 0 | void clear_from_destructor_of_local_pool() { |
460 | | // Remove tls |
461 | 0 | _local_pool = NULL; |
462 | |
|
463 | 0 | if (_nlocal.fetch_sub(1, butil::memory_order_relaxed) != 1) { |
464 | 0 | return; |
465 | 0 | } |
466 | | |
467 | | // Can't delete global even if all threads(called ResourcePool |
468 | | // functions) quit because the memory may still be referenced by |
469 | | // other threads. But we need to validate that all memory can |
470 | | // be deallocated correctly in tests, so wrap the function with |
471 | | // a macro which is only defined in unittests. |
472 | | #ifdef BAIDU_CLEAR_RESOURCE_POOL_AFTER_ALL_THREADS_QUIT |
473 | | BAIDU_SCOPED_LOCK(_change_thread_mutex); // including acquire fence. |
474 | | // Do nothing if there're active threads. |
475 | | if (_nlocal.load(butil::memory_order_relaxed) != 0) { |
476 | | return; |
477 | | } |
478 | | // All threads exited and we're holding _change_thread_mutex to avoid |
479 | | // racing with new threads calling get_resource(). |
480 | | |
481 | | // Clear global free list. |
482 | | FreeChunk dummy; |
483 | | while (pop_free_chunk(dummy)); |
484 | | |
485 | | // Delete all memory |
486 | | const size_t ngroup = _ngroup.exchange(0, butil::memory_order_relaxed); |
487 | | for (size_t i = 0; i < ngroup; ++i) { |
488 | | BlockGroup* bg = _block_groups[i].load(butil::memory_order_relaxed); |
489 | | if (NULL == bg) { |
490 | | break; |
491 | | } |
492 | | size_t nblock = std::min(bg->nblock.load(butil::memory_order_relaxed), |
493 | | RP_GROUP_NBLOCK); |
494 | | for (size_t j = 0; j < nblock; ++j) { |
495 | | Block* b = bg->blocks[j].load(butil::memory_order_relaxed); |
496 | | if (NULL == b) { |
497 | | continue; |
498 | | } |
499 | | for (size_t k = 0; k < b->nitem; ++k) { |
500 | | T* const objs = (T*)b->items; |
501 | | objs[k].~T(); |
502 | | } |
503 | | delete b; |
504 | | } |
505 | | delete bg; |
506 | | } |
507 | | |
508 | | memset(_block_groups, 0, sizeof(BlockGroup*) * RP_MAX_BLOCK_NGROUP); |
509 | | #endif |
510 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::clear_from_destructor_of_local_pool() Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::clear_from_destructor_of_local_pool() Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::clear_from_destructor_of_local_pool() Unexecuted instantiation: butil::ResourcePool<brpc::IOEventData>::clear_from_destructor_of_local_pool() Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::clear_from_destructor_of_local_pool() Unexecuted instantiation: butil::ResourcePool<bthread::ExecutionQueueBase>::clear_from_destructor_of_local_pool() Unexecuted instantiation: butil::ResourcePool<bthread::Id>::clear_from_destructor_of_local_pool() |
511 | | |
512 | | private: |
513 | 0 | bool pop_free_chunk(FreeChunk& c) { |
514 | | // Critical for the case that most return_object are called in |
515 | | // different threads of get_object. |
516 | 0 | if (_free_chunks.empty()) { |
517 | 0 | return false; |
518 | 0 | } |
519 | 0 | pthread_mutex_lock(&_free_chunks_mutex); |
520 | 0 | if (_free_chunks.empty()) { |
521 | 0 | pthread_mutex_unlock(&_free_chunks_mutex); |
522 | 0 | return false; |
523 | 0 | } |
524 | 0 | DynamicFreeChunk* p = _free_chunks.back(); |
525 | 0 | _free_chunks.pop_back(); |
526 | 0 | pthread_mutex_unlock(&_free_chunks_mutex); |
527 | 0 | c.nfree = p->nfree; |
528 | 0 | memcpy(c.ids, p->ids, sizeof(*p->ids) * p->nfree); |
529 | 0 | free(p); |
530 | 0 | return true; |
531 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::pop_free_chunk(butil::ResourcePoolFreeChunk<bthread::TaskMeta, 256ul>&) Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::pop_free_chunk(butil::ResourcePoolFreeChunk<bthread::TimerThread::Task, 256ul>&) Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::pop_free_chunk(butil::ResourcePoolFreeChunk<butil::details::ExtendedEndPoint, 256ul>&) Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::pop_free_chunk(butil::ResourcePoolFreeChunk<brpc::Socket, 78ul>&) Unexecuted instantiation: butil::ResourcePool<brpc::IOEventData>::pop_free_chunk(butil::ResourcePoolFreeChunk<brpc::IOEventData, 256ul>&) Unexecuted instantiation: butil::ResourcePool<bthread::ExecutionQueueBase>::pop_free_chunk(butil::ResourcePoolFreeChunk<bthread::ExecutionQueueBase, 170ul>&) Unexecuted instantiation: butil::ResourcePool<bthread::Id>::pop_free_chunk(butil::ResourcePoolFreeChunk<bthread::Id, 256ul>&) |
532 | | |
533 | 0 | bool push_free_chunk(const FreeChunk& c) { |
534 | 0 | DynamicFreeChunk* p = (DynamicFreeChunk*)malloc( |
535 | 0 | offsetof(DynamicFreeChunk, ids) + sizeof(*c.ids) * c.nfree); |
536 | 0 | if (!p) { |
537 | 0 | return false; |
538 | 0 | } |
539 | 0 | p->nfree = c.nfree; |
540 | 0 | memcpy(p->ids, c.ids, sizeof(*c.ids) * c.nfree); |
541 | 0 | pthread_mutex_lock(&_free_chunks_mutex); |
542 | 0 | _free_chunks.push_back(p); |
543 | 0 | pthread_mutex_unlock(&_free_chunks_mutex); |
544 | 0 | return true; |
545 | 0 | } Unexecuted instantiation: butil::ResourcePool<bthread::TaskMeta>::push_free_chunk(butil::ResourcePoolFreeChunk<bthread::TaskMeta, 256ul> const&) Unexecuted instantiation: butil::ResourcePool<bthread::TimerThread::Task>::push_free_chunk(butil::ResourcePoolFreeChunk<bthread::TimerThread::Task, 256ul> const&) Unexecuted instantiation: butil::ResourcePool<butil::details::ExtendedEndPoint>::push_free_chunk(butil::ResourcePoolFreeChunk<butil::details::ExtendedEndPoint, 256ul> const&) Unexecuted instantiation: butil::ResourcePool<brpc::IOEventData>::push_free_chunk(butil::ResourcePoolFreeChunk<brpc::IOEventData, 256ul> const&) Unexecuted instantiation: butil::ResourcePool<brpc::Socket>::push_free_chunk(butil::ResourcePoolFreeChunk<brpc::Socket, 78ul> const&) Unexecuted instantiation: butil::ResourcePool<bthread::ExecutionQueueBase>::push_free_chunk(butil::ResourcePoolFreeChunk<bthread::ExecutionQueueBase, 170ul> const&) Unexecuted instantiation: butil::ResourcePool<bthread::Id>::push_free_chunk(butil::ResourcePoolFreeChunk<bthread::Id, 256ul> const&) |
546 | | |
547 | | static butil::static_atomic<ResourcePool*> _singleton; |
548 | | static pthread_mutex_t _singleton_mutex; |
549 | | STATIC_MEMBER_BAIDU_VOLATILE_THREAD_LOCAL(LocalPool*, _local_pool); |
550 | | static butil::static_atomic<long> _nlocal; |
551 | | static butil::static_atomic<size_t> _ngroup; |
552 | | static pthread_mutex_t _block_group_mutex; |
553 | | static pthread_mutex_t _change_thread_mutex; |
554 | | static butil::static_atomic<BlockGroup*> _block_groups[RP_MAX_BLOCK_NGROUP]; |
555 | | |
556 | | std::vector<DynamicFreeChunk*> _free_chunks; |
557 | | pthread_mutex_t _free_chunks_mutex; |
558 | | |
559 | | #ifdef BUTIL_RESOURCE_POOL_NEED_FREE_ITEM_NUM |
560 | | static butil::static_atomic<size_t> _global_nfree; |
561 | | #endif |
562 | | }; |
563 | | |
564 | | // Declare template static variables: |
565 | | |
566 | | template <typename T> |
567 | | const size_t ResourcePool<T>::FREE_CHUNK_NITEM; |
568 | | |
569 | | template <typename T> |
570 | | BAIDU_THREAD_LOCAL typename ResourcePool<T>::LocalPool* |
571 | | ResourcePool<T>::_local_pool = NULL; |
572 | | |
573 | | template <typename T> |
574 | | butil::static_atomic<ResourcePool<T>*> ResourcePool<T>::_singleton = |
575 | | BUTIL_STATIC_ATOMIC_INIT(NULL); |
576 | | |
577 | | template <typename T> |
578 | | pthread_mutex_t ResourcePool<T>::_singleton_mutex = PTHREAD_MUTEX_INITIALIZER; |
579 | | |
580 | | template <typename T> |
581 | | butil::static_atomic<long> ResourcePool<T>::_nlocal = BUTIL_STATIC_ATOMIC_INIT(0); |
582 | | |
583 | | template <typename T> |
584 | | butil::static_atomic<size_t> ResourcePool<T>::_ngroup = BUTIL_STATIC_ATOMIC_INIT(0); |
585 | | |
586 | | template <typename T> |
587 | | pthread_mutex_t ResourcePool<T>::_block_group_mutex = PTHREAD_MUTEX_INITIALIZER; |
588 | | |
589 | | template <typename T> |
590 | | pthread_mutex_t ResourcePool<T>::_change_thread_mutex = |
591 | | PTHREAD_MUTEX_INITIALIZER; |
592 | | |
593 | | template <typename T> |
594 | | butil::static_atomic<typename ResourcePool<T>::BlockGroup*> |
595 | | ResourcePool<T>::_block_groups[RP_MAX_BLOCK_NGROUP] = {}; |
596 | | |
597 | | #ifdef BUTIL_RESOURCE_POOL_NEED_FREE_ITEM_NUM |
598 | | template <typename T> |
599 | | butil::static_atomic<size_t> ResourcePool<T>::_global_nfree = BUTIL_STATIC_ATOMIC_INIT(0); |
600 | | #endif |
601 | | |
602 | | template <typename T> |
603 | | inline bool operator==(ResourceId<T> id1, ResourceId<T> id2) { |
604 | | return id1.value == id2.value; |
605 | | } |
606 | | |
607 | | template <typename T> |
608 | | inline bool operator!=(ResourceId<T> id1, ResourceId<T> id2) { |
609 | | return id1.value != id2.value; |
610 | | } |
611 | | |
612 | | // Disable comparisons between different typed ResourceId |
613 | | template <typename T1, typename T2> |
614 | | bool operator==(ResourceId<T1> id1, ResourceId<T2> id2); |
615 | | |
616 | | template <typename T1, typename T2> |
617 | | bool operator!=(ResourceId<T1> id1, ResourceId<T2> id2); |
618 | | |
619 | | inline std::ostream& operator<<(std::ostream& os, |
620 | 0 | ResourcePoolInfo const& info) { |
621 | 0 | return os << "local_pool_num: " << info.local_pool_num |
622 | 0 | << "\nblock_group_num: " << info.block_group_num |
623 | 0 | << "\nblock_num: " << info.block_num |
624 | 0 | << "\nitem_num: " << info.item_num |
625 | 0 | << "\nblock_item_num: " << info.block_item_num |
626 | 0 | << "\nfree_chunk_item_num: " << info.free_chunk_item_num |
627 | 0 | << "\ntotal_size: " << info.total_size; |
628 | 0 | #ifdef BUTIL_RESOURCE_POOL_NEED_FREE_ITEM_NUM |
629 | 0 | << "\nfree_num: " << info.free_item_num |
630 | 0 | #endif |
631 | 0 | ; |
632 | 0 | } |
633 | | |
634 | | } // namespace butil |
635 | | |
636 | | #endif // BUTIL_RESOURCE_POOL_INL_H |