Coverage Report

Created: 2026-03-15 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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