Coverage Report

Created: 2026-09-01 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/brpc/src/bvar/detail/combiner.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
// Date 2014/09/22 11:57:43
19
20
#ifndef  BVAR_COMBINER_H
21
#define  BVAR_COMBINER_H
22
23
#include <string>                       // std::string
24
#include <vector>                       // std::vector
25
#include <memory>
26
#include "butil/atomicops.h"             // butil::atomic
27
#include "butil/scoped_lock.h"           // BAIDU_SCOPED_LOCK
28
#include "butil/type_traits.h"           // butil::add_cr_non_integral
29
#include "butil/synchronization/lock.h"  // butil::Lock
30
#include "butil/containers/linked_list.h"// LinkNode
31
#include "bvar/detail/agent_group.h"    // detail::AgentGroup
32
#include "bvar/detail/is_atomical.h"
33
#include "bvar/detail/call_op_returning_void.h"
34
35
namespace bvar {
36
namespace detail {
37
38
// Parameter to merge_global.
39
template <typename Combiner>
40
class GlobalValue {
41
public:
42
    typedef typename Combiner::result_type result_type;
43
    typedef typename Combiner::Agent agent_type;
44
45
0
    GlobalValue(agent_type* a, Combiner* c) : _a(a), _c(c) {}
46
47
    // Call this method to unlock tls element and lock the combiner.
48
    // Unlocking tls element avoids potential deadlock with
49
    // AgentCombiner::reset(), which also means that tls element may be
50
    // changed during calling of this method. BE AWARE OF THIS!
51
    // After this method is called (and before unlock), tls element and
52
    // global_result will not be changed provided this method is called
53
    // from the thread owning the agent.
54
0
    result_type* lock() {
55
0
        _a->element._lock.Release();
56
0
        _c->_lock.Acquire();
57
0
        return &_c->_global_result;
58
0
    }
59
60
    // Call this method to unlock the combiner and lock tls element again.
61
0
    void unlock() {
62
0
        _c->_lock.Release();
63
0
        _a->element._lock.Acquire();
64
0
    }
65
66
private:
67
    agent_type* _a;
68
    Combiner* _c;
69
};
70
71
// Abstraction of tls element whose operations are all atomic.
72
template <typename T, typename Enabler = void>
73
class ElementContainer {
74
template <typename> friend class GlobalValue;
75
public:
76
0
    void load(T* out) {
77
0
        butil::AutoLock guard(_lock);
78
0
        *out = _value;
79
0
    }
Unexecuted instantiation: bvar::detail::ElementContainer<bvar::detail::PercentileSamples<30ul>, void>::load(bvar::detail::PercentileSamples<30ul>*)
Unexecuted instantiation: bvar::detail::ElementContainer<bvar::Collected*, void>::load(bvar::Collected**)
Unexecuted instantiation: bvar::detail::ElementContainer<bvar::detail::Sampler*, void>::load(bvar::detail::Sampler**)
80
81
2
    void store(const T& new_value) {
82
2
        butil::AutoLock guard(_lock);
83
2
        _value = new_value;
84
2
    }
Unexecuted instantiation: bvar::detail::ElementContainer<bvar::Collected*, void>::store(bvar::Collected* const&)
bvar::detail::ElementContainer<bvar::detail::Sampler*, void>::store(bvar::detail::Sampler* const&)
Line
Count
Source
81
2
    void store(const T& new_value) {
82
2
        butil::AutoLock guard(_lock);
83
2
        _value = new_value;
84
2
    }
Unexecuted instantiation: bvar::detail::ElementContainer<bvar::detail::PercentileSamples<30ul>, void>::store(bvar::detail::PercentileSamples<30ul> const&)
85
86
2
    void exchange(T* prev, const T& new_value) {
87
2
        butil::AutoLock guard(_lock);
88
2
        *prev = _value;
89
2
        _value = new_value;
90
2
    }
Unexecuted instantiation: bvar::detail::ElementContainer<bvar::detail::PercentileSamples<30ul>, void>::exchange(bvar::detail::PercentileSamples<30ul>*, bvar::detail::PercentileSamples<30ul> const&)
Unexecuted instantiation: bvar::detail::ElementContainer<bvar::Collected*, void>::exchange(bvar::Collected**, bvar::Collected* const&)
bvar::detail::ElementContainer<bvar::detail::Sampler*, void>::exchange(bvar::detail::Sampler**, bvar::detail::Sampler* const&)
Line
Count
Source
86
2
    void exchange(T* prev, const T& new_value) {
87
2
        butil::AutoLock guard(_lock);
88
2
        *prev = _value;
89
2
        _value = new_value;
90
2
    }
91
92
    template <typename Op, typename T1>
93
8
    void modify(const Op &op, const T1 &value2) {
94
8
        butil::AutoLock guard(_lock);
95
8
        call_op_returning_void(op, _value, value2);
96
8
    }
Unexecuted instantiation: void bvar::detail::ElementContainer<bvar::Collected*, void>::modify<bvar::CombineCollected, bvar::Collected*>(bvar::CombineCollected const&, bvar::Collected* const&)
void bvar::detail::ElementContainer<bvar::detail::Sampler*, void>::modify<bvar::detail::CombineSampler, bvar::detail::Sampler*>(bvar::detail::CombineSampler const&, bvar::detail::Sampler* const&)
Line
Count
Source
93
8
    void modify(const Op &op, const T1 &value2) {
94
8
        butil::AutoLock guard(_lock);
95
8
        call_op_returning_void(op, _value, value2);
96
8
    }
97
98
    // [Unique]
99
    template <typename Op, typename GlobalValue>
100
0
    void merge_global(const Op &op, GlobalValue & global_value) {
101
0
        _lock.Acquire();
102
0
        op(global_value, _value);
103
0
        _lock.Release();
104
0
    }
105
106
private:
107
    T _value;
108
    butil::Lock _lock;
109
};
110
111
template <typename T>
112
class ElementContainer<
113
    T, typename butil::enable_if<is_atomical<T>::value>::type> {
114
public:
115
    // We don't need any memory fencing here, every op is relaxed.
116
    
117
0
    inline void load(T* out) {
118
0
        *out = _value.load(butil::memory_order_relaxed);
119
0
    }
Unexecuted instantiation: bvar::detail::ElementContainer<unsigned long, void>::load(unsigned long*)
Unexecuted instantiation: bvar::detail::ElementContainer<long, void>::load(long*)
120
121
0
    inline void store(T new_value) {
122
0
        _value.store(new_value, butil::memory_order_relaxed);
123
0
    }
Unexecuted instantiation: bvar::detail::ElementContainer<unsigned long, void>::store(unsigned long)
Unexecuted instantiation: bvar::detail::ElementContainer<long, void>::store(long)
124
125
0
    inline void exchange(T* prev, T new_value) {
126
0
        *prev = _value.exchange(new_value, butil::memory_order_relaxed);
127
0
    }
Unexecuted instantiation: bvar::detail::ElementContainer<unsigned long, void>::exchange(unsigned long*, unsigned long)
Unexecuted instantiation: bvar::detail::ElementContainer<long, void>::exchange(long*, long)
128
129
    // [Unique]
130
0
    inline bool compare_exchange_weak(T& expected, T new_value) {
131
0
        return _value.compare_exchange_weak(expected, new_value,
132
0
                                            butil::memory_order_relaxed);
133
0
    }
134
135
    template <typename Op, typename T1>
136
0
    void modify(const Op &op, const T1 &value2) {
137
0
        T old_value = _value.load(butil::memory_order_relaxed);
138
0
        T new_value = old_value;
139
0
        call_op_returning_void(op, new_value, value2);
140
        // There's a contention with the reset operation of combiner,
141
        // if the tls value has been modified during _op, the
142
        // compare_exchange_weak operation will fail and recalculation is
143
        // to be processed according to the new version of value
144
0
        while (!_value.compare_exchange_weak(
145
0
                   old_value, new_value, butil::memory_order_relaxed)) {
146
0
            new_value = old_value;
147
0
            call_op_returning_void(op, new_value, value2);
148
0
        }
149
0
    }
Unexecuted instantiation: void bvar::detail::ElementContainer<long, void>::modify<bvar::detail::AddTo<long>, long>(bvar::detail::AddTo<long> const&, long const&)
Unexecuted instantiation: void bvar::detail::ElementContainer<long, void>::modify<bvar::detail::MaxTo<long>, long>(bvar::detail::MaxTo<long> const&, long const&)
150
151
private:
152
    butil::atomic<T> _value;
153
};
154
155
template <typename ResultTp, typename ElementTp, typename BinaryOp>
156
class AgentCombiner
157
    : public std::enable_shared_from_this<AgentCombiner<ResultTp, ElementTp, BinaryOp>> {
158
159
public:
160
    typedef ResultTp result_type;
161
    typedef ElementTp element_type;
162
    typedef AgentCombiner<ResultTp, ElementTp, BinaryOp> self_type;
163
    typedef std::shared_ptr<self_type> self_shared_type;
164
    typedef std::weak_ptr<self_type> self_weak_type;
165
friend class GlobalValue<self_type>;
166
167
    struct Agent : public butil::LinkNode<Agent> {
168
0
        ~Agent() {
169
0
            self_shared_type c = combiner.lock();
170
0
            if (nullptr != c) {
171
0
                c->commit_and_erase(this);
172
0
            }
173
0
        }
Unexecuted instantiation: bvar::detail::AgentCombiner<long, long, bvar::detail::AddTo<long> >::Agent::~Agent()
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::Collected*, bvar::Collected*, bvar::CombineCollected>::Agent::~Agent()
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::detail::Sampler*, bvar::detail::Sampler*, bvar::detail::CombineSampler>::Agent::~Agent()
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::Stat, unsigned long, bvar::IntRecorder::AddToStat>::Agent::~Agent()
Unexecuted instantiation: bvar::detail::AgentCombiner<long, long, bvar::detail::MaxTo<long> >::Agent::~Agent()
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::detail::PercentileSamples<254ul>, bvar::detail::PercentileSamples<30ul>, bvar::detail::detail::AddPercentileSamples>::Agent::~Agent()
174
        
175
2
        void reset(const ElementTp& val, const self_shared_type& c) {
176
2
            combiner = c;
177
2
            element.store(val);
178
2
        }
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::Stat, unsigned long, bvar::IntRecorder::AddToStat>::Agent::reset(unsigned long const&, std::shared_ptr<bvar::detail::AgentCombiner<bvar::Stat, unsigned long, bvar::IntRecorder::AddToStat> > const&)
Unexecuted instantiation: bvar::detail::AgentCombiner<long, long, bvar::detail::AddTo<long> >::Agent::reset(long const&, std::shared_ptr<bvar::detail::AgentCombiner<long, long, bvar::detail::AddTo<long> > > const&)
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::Collected*, bvar::Collected*, bvar::CombineCollected>::Agent::reset(bvar::Collected* const&, std::shared_ptr<bvar::detail::AgentCombiner<bvar::Collected*, bvar::Collected*, bvar::CombineCollected> > const&)
bvar::detail::AgentCombiner<bvar::detail::Sampler*, bvar::detail::Sampler*, bvar::detail::CombineSampler>::Agent::reset(bvar::detail::Sampler* const&, std::shared_ptr<bvar::detail::AgentCombiner<bvar::detail::Sampler*, bvar::detail::Sampler*, bvar::detail::CombineSampler> > const&)
Line
Count
Source
175
2
        void reset(const ElementTp& val, const self_shared_type& c) {
176
2
            combiner = c;
177
2
            element.store(val);
178
2
        }
Unexecuted instantiation: bvar::detail::AgentCombiner<long, long, bvar::detail::MaxTo<long> >::Agent::reset(long const&, std::shared_ptr<bvar::detail::AgentCombiner<long, long, bvar::detail::MaxTo<long> > > const&)
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::detail::PercentileSamples<254ul>, bvar::detail::PercentileSamples<30ul>, bvar::detail::detail::AddPercentileSamples>::Agent::reset(bvar::detail::PercentileSamples<30ul> const&, std::shared_ptr<bvar::detail::AgentCombiner<bvar::detail::PercentileSamples<254ul>, bvar::detail::PercentileSamples<30ul>, bvar::detail::detail::AddPercentileSamples> > const&)
179
180
        // Call op(GlobalValue<Combiner> &, ElementTp &) to merge tls element
181
        // into global_result. The common impl. is:
182
        //   struct XXX {
183
        //       void operator()(GlobalValue<Combiner> & global_value,
184
        //                       ElementTp & local_value) const {
185
        //           if (test_for_merging(local_value)) {
186
        // 
187
        //               // Unlock tls element and lock combiner. Obviously
188
        //               // tls element can be changed during lock().
189
        //               ResultTp* g = global_value.lock();
190
        // 
191
        //               // *g and local_value are not changed provided
192
        //               // merge_global is called from the thread owning
193
        //               // the agent.
194
        //               merge(*g, local_value);
195
        //
196
        //               // unlock combiner and lock tls element again.
197
        //               global_value.unlock();
198
        //           }
199
        //
200
        //           // safe to modify local_value because it's already locked
201
        //           // or locked again after merging.
202
        //           ...
203
        //       }
204
        //   };
205
        // 
206
        // NOTE: Only available to non-atomic types.
207
        template <typename Op>
208
0
        void merge_global(const Op &op, self_shared_type& c) {
209
0
            const self_shared_type& c_ref = nullptr != c ? c : combiner.lock();
210
0
            if (nullptr != c_ref) {
211
0
                GlobalValue<self_type> g(this, c_ref.get());
212
0
                element.merge_global(op, g);
213
0
            }
214
0
        }
215
216
        ElementContainer<ElementTp> element;
217
    private:
218
    friend class AgentCombiner<ResultTp, ElementTp, BinaryOp>;
219
        self_weak_type combiner;
220
    };
221
222
    typedef detail::AgentGroup<Agent> AgentGroup;
223
224
    explicit AgentCombiner(const ResultTp result_identity = ResultTp(),
225
                           const ElementTp element_identity = ElementTp(),
226
                           const BinaryOp& op = BinaryOp())
227
2
        : _id(AgentGroup::create_new_agent())
228
2
        , _op(op)
229
2
        , _global_result(result_identity)
230
2
        , _result_identity(result_identity)
231
2
        , _element_identity(element_identity) {
232
2
    }
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::Collected*, bvar::Collected*, bvar::CombineCollected>::AgentCombiner(bvar::Collected*, bvar::Collected*, bvar::CombineCollected const&)
bvar::detail::AgentCombiner<bvar::detail::Sampler*, bvar::detail::Sampler*, bvar::detail::CombineSampler>::AgentCombiner(bvar::detail::Sampler*, bvar::detail::Sampler*, bvar::detail::CombineSampler const&)
Line
Count
Source
227
2
        : _id(AgentGroup::create_new_agent())
228
2
        , _op(op)
229
2
        , _global_result(result_identity)
230
2
        , _result_identity(result_identity)
231
2
        , _element_identity(element_identity) {
232
2
    }
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::Stat, unsigned long, bvar::IntRecorder::AddToStat>::AgentCombiner(bvar::Stat, unsigned long, bvar::IntRecorder::AddToStat const&)
Unexecuted instantiation: bvar::detail::AgentCombiner<long, long, bvar::detail::MaxTo<long> >::AgentCombiner(long, long, bvar::detail::MaxTo<long> const&)
Unexecuted instantiation: bvar::detail::AgentCombiner<long, long, bvar::detail::AddTo<long> >::AgentCombiner(long, long, bvar::detail::AddTo<long> const&)
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::detail::PercentileSamples<254ul>, bvar::detail::PercentileSamples<30ul>, bvar::detail::detail::AddPercentileSamples>::AgentCombiner(bvar::detail::PercentileSamples<254ul>, bvar::detail::PercentileSamples<30ul>, bvar::detail::detail::AddPercentileSamples const&)
233
234
0
    ~AgentCombiner() {
235
0
        if (_id >= 0) {
236
            // NOTE: We intentionally do NOT walk `_agents` here (e.g. via the
237
            // previously existed `clear_all_agents()`).
238
            //
239
            // `Agent` instances live inside per-thread `ThreadBlock`s owned by
240
            // `AgentGroup` and are destroyed when their owning thread exits
241
            // (via `_destroy_tls_blocks`). At that point `~Agent` calls
242
            // `combiner.lock()`; if the combiner has already started its
243
            // destruction the `weak_ptr` is expired and the agent will skip
244
            // `commit_and_erase`, leaving its `LinkNode` linked to this
245
            // combiner's `_agents`. If we tried to traverse `_agents` here we
246
            // could touch agent nodes whose `ThreadBlock` was just freed by
247
            // a concurrent thread-exit, causing heap-use-after-free
248
            // (see issue #2937 follow-up).
249
            //
250
            // It is safe to leave the list "dirty" because:
251
            //   * `butil::LinkedList` / `butil::LinkNode` have trivial
252
            //     destructors and never traverse on destruction, so tearing
253
            //     down `_agents` here does not dereference any agent node.
254
            //   * After this combiner is gone, every still-alive `Agent` will
255
            //     observe `combiner.expired() == true` in `~Agent` and skip
256
            //     `commit_and_erase`, so the dangling `prev_/next_` pointers
257
            //     in those agents are never read.
258
            //   * If the freed `_id` is later reused by a new combiner and the
259
            //     same TLS slot is taken, `get_or_create_tls_agent` will call
260
            //     `Agent::reset` and `Append` the agent into the new
261
            //     combiner's `_agents`. `LinkNode::InsertBefore` only writes
262
            //     `prev_/next_` (never reads their stale values), so the
263
            //     dangling pointers are safely overwritten.
264
            //   * `Agent::element` is destroyed together with the `ThreadBlock`,
265
            //     so any non-POD resource it holds is still released; if the
266
            //     agent slot is reused, `Agent::reset` will overwrite the
267
            //     element value before it is observed again.
268
0
            AgentGroup::destroy_agent(_id);
269
0
            _id = -1;
270
0
        }
271
0
    }
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::Collected*, bvar::Collected*, bvar::CombineCollected>::~AgentCombiner()
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::detail::Sampler*, bvar::detail::Sampler*, bvar::detail::CombineSampler>::~AgentCombiner()
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::Stat, unsigned long, bvar::IntRecorder::AddToStat>::~AgentCombiner()
Unexecuted instantiation: bvar::detail::AgentCombiner<long, long, bvar::detail::MaxTo<long> >::~AgentCombiner()
Unexecuted instantiation: bvar::detail::AgentCombiner<long, long, bvar::detail::AddTo<long> >::~AgentCombiner()
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::detail::PercentileSamples<254ul>, bvar::detail::PercentileSamples<30ul>, bvar::detail::detail::AddPercentileSamples>::~AgentCombiner()
272
    
273
    // [Threadsafe] May be called from anywhere
274
0
    ResultTp combine_agents() const {
275
0
        ElementTp tls_value;
276
0
        butil::AutoLock guard(_lock);
277
0
        ResultTp ret = _global_result;
278
0
        for (butil::LinkNode<Agent>* node = _agents.head();
279
0
             node != _agents.end(); node = node->next()) {
280
0
            node->value()->element.load(&tls_value);
281
0
            call_op_returning_void(_op, ret, tls_value);
282
0
        }
283
0
        return ret;
284
0
    }
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::Stat, unsigned long, bvar::IntRecorder::AddToStat>::combine_agents() const
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::detail::PercentileSamples<254ul>, bvar::detail::PercentileSamples<30ul>, bvar::detail::detail::AddPercentileSamples>::combine_agents() const
Unexecuted instantiation: bvar::detail::AgentCombiner<long, long, bvar::detail::MaxTo<long> >::combine_agents() const
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::Collected*, bvar::Collected*, bvar::CombineCollected>::combine_agents() const
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::detail::Sampler*, bvar::detail::Sampler*, bvar::detail::CombineSampler>::combine_agents() const
Unexecuted instantiation: bvar::detail::AgentCombiner<long, long, bvar::detail::AddTo<long> >::combine_agents() const
285
286
    typename butil::add_cr_non_integral<ElementTp>::type
287
    element_identity() const { return _element_identity; }
288
    typename butil::add_cr_non_integral<ResultTp>::type
289
    result_identity() const { return _result_identity; }
290
291
    // [Threadsafe] May be called from anywhere.
292
2
    ResultTp reset_all_agents() {
293
2
        ElementTp prev;
294
2
        butil::AutoLock guard(_lock);
295
2
        ResultTp tmp = _global_result;
296
2
        _global_result = _result_identity;
297
2
        for (butil::LinkNode<Agent>* node = _agents.head();
298
4
             node != _agents.end(); node = node->next()) {
299
2
            node->value()->element.exchange(&prev, _element_identity);
300
2
            call_op_returning_void(_op, tmp, prev);
301
2
        }
302
2
        return tmp;
303
2
    }
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::Stat, unsigned long, bvar::IntRecorder::AddToStat>::reset_all_agents()
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::detail::PercentileSamples<254ul>, bvar::detail::PercentileSamples<30ul>, bvar::detail::detail::AddPercentileSamples>::reset_all_agents()
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::Collected*, bvar::Collected*, bvar::CombineCollected>::reset_all_agents()
bvar::detail::AgentCombiner<bvar::detail::Sampler*, bvar::detail::Sampler*, bvar::detail::CombineSampler>::reset_all_agents()
Line
Count
Source
292
2
    ResultTp reset_all_agents() {
293
2
        ElementTp prev;
294
2
        butil::AutoLock guard(_lock);
295
2
        ResultTp tmp = _global_result;
296
2
        _global_result = _result_identity;
297
2
        for (butil::LinkNode<Agent>* node = _agents.head();
298
4
             node != _agents.end(); node = node->next()) {
299
2
            node->value()->element.exchange(&prev, _element_identity);
300
2
            call_op_returning_void(_op, tmp, prev);
301
2
        }
302
2
        return tmp;
303
2
    }
Unexecuted instantiation: bvar::detail::AgentCombiner<long, long, bvar::detail::MaxTo<long> >::reset_all_agents()
304
305
    // Always called from the thread owning the agent.
306
0
    void commit_and_erase(Agent* agent) {
307
0
        if (nullptr == agent) {
308
0
            return;
309
0
        }
310
0
        ElementTp local;
311
0
        butil::AutoLock guard(_lock);
312
        // TODO: For non-atomic types, we can pass the reference to op directly.
313
        // But atomic types cannot. The code is a little troublesome to write.
314
0
        agent->element.load(&local);
315
0
        call_op_returning_void(_op, _global_result, local);
316
0
        agent->RemoveFromList();
317
0
    }
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::Stat, unsigned long, bvar::IntRecorder::AddToStat>::commit_and_erase(bvar::detail::AgentCombiner<bvar::Stat, unsigned long, bvar::IntRecorder::AddToStat>::Agent*)
Unexecuted instantiation: bvar::detail::AgentCombiner<long, long, bvar::detail::AddTo<long> >::commit_and_erase(bvar::detail::AgentCombiner<long, long, bvar::detail::AddTo<long> >::Agent*)
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::Collected*, bvar::Collected*, bvar::CombineCollected>::commit_and_erase(bvar::detail::AgentCombiner<bvar::Collected*, bvar::Collected*, bvar::CombineCollected>::Agent*)
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::detail::Sampler*, bvar::detail::Sampler*, bvar::detail::CombineSampler>::commit_and_erase(bvar::detail::AgentCombiner<bvar::detail::Sampler*, bvar::detail::Sampler*, bvar::detail::CombineSampler>::Agent*)
Unexecuted instantiation: bvar::detail::AgentCombiner<long, long, bvar::detail::MaxTo<long> >::commit_and_erase(bvar::detail::AgentCombiner<long, long, bvar::detail::MaxTo<long> >::Agent*)
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::detail::PercentileSamples<254ul>, bvar::detail::PercentileSamples<30ul>, bvar::detail::detail::AddPercentileSamples>::commit_and_erase(bvar::detail::AgentCombiner<bvar::detail::PercentileSamples<254ul>, bvar::detail::PercentileSamples<30ul>, bvar::detail::detail::AddPercentileSamples>::Agent*)
318
319
    // Always called from the thread owning the agent
320
0
    void commit_and_clear(Agent* agent) {
321
0
        if (nullptr == agent) {
322
0
            return;
323
0
        }
324
0
        ElementTp prev;
325
0
        butil::AutoLock guard(_lock);
326
0
        agent->element.exchange(&prev, _element_identity);
327
0
        call_op_returning_void(_op, _global_result, prev);
328
0
    }
329
330
    // We need this function to be as fast as possible.
331
8
    Agent* get_or_create_tls_agent() {
332
8
        Agent* agent = AgentGroup::get_tls_agent(_id);
333
8
        if (!agent) {
334
            // Create the agent
335
2
            agent = AgentGroup::get_or_create_tls_agent(_id);
336
2
            if (nullptr == agent) {
337
0
                LOG(FATAL) << "Fail to create agent";
338
0
                return nullptr;
339
0
            }
340
2
        }
341
8
        if (!agent->combiner.expired()) {
342
6
            return agent;
343
6
        }
344
2
        agent->reset(_element_identity, this->shared_from_this());
345
        // TODO: Is uniqueness-checking necessary here?
346
2
        {
347
2
            butil::AutoLock guard(_lock);
348
2
            _agents.Append(agent);
349
2
        }
350
2
        return agent;
351
8
    }
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::Stat, unsigned long, bvar::IntRecorder::AddToStat>::get_or_create_tls_agent()
Unexecuted instantiation: bvar::detail::AgentCombiner<long, long, bvar::detail::AddTo<long> >::get_or_create_tls_agent()
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::Collected*, bvar::Collected*, bvar::CombineCollected>::get_or_create_tls_agent()
bvar::detail::AgentCombiner<bvar::detail::Sampler*, bvar::detail::Sampler*, bvar::detail::CombineSampler>::get_or_create_tls_agent()
Line
Count
Source
331
8
    Agent* get_or_create_tls_agent() {
332
8
        Agent* agent = AgentGroup::get_tls_agent(_id);
333
8
        if (!agent) {
334
            // Create the agent
335
2
            agent = AgentGroup::get_or_create_tls_agent(_id);
336
2
            if (nullptr == agent) {
337
0
                LOG(FATAL) << "Fail to create agent";
338
0
                return nullptr;
339
0
            }
340
2
        }
341
8
        if (!agent->combiner.expired()) {
342
6
            return agent;
343
6
        }
344
2
        agent->reset(_element_identity, this->shared_from_this());
345
        // TODO: Is uniqueness-checking necessary here?
346
2
        {
347
2
            butil::AutoLock guard(_lock);
348
2
            _agents.Append(agent);
349
2
        }
350
2
        return agent;
351
8
    }
Unexecuted instantiation: bvar::detail::AgentCombiner<long, long, bvar::detail::MaxTo<long> >::get_or_create_tls_agent()
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::detail::PercentileSamples<254ul>, bvar::detail::PercentileSamples<30ul>, bvar::detail::detail::AddPercentileSamples>::get_or_create_tls_agent()
352
353
    // NOTE: `clear_all_agents()` is intentionally kept but no longer called
354
    // from `~AgentCombiner` (see the long comment in `~AgentCombiner`).
355
    //
356
    // Calling it from the destructor is unsafe: by the time the destructor
357
    // runs, agent weak_ptrs have already expired and `~Agent` will skip
358
    // `commit_and_erase`; a concurrent thread-exit can therefore free the
359
    // `ThreadBlock` (and the agents inside it) while we are still walking
360
    // `_agents` here, which is a heap-use-after-free.
361
    //
362
    // The body is left around (commented out) for reference / future use --
363
    // do NOT re-enable it from `~AgentCombiner`.
364
    //
365
    // void clear_all_agents() {
366
    //     butil::AutoLock guard(_lock);
367
    //     // Resetting agents is a must because the agent object may be
368
    //     // reused. Set element to be default-constructed so that if it's
369
    //     // non-pod, internal allocations should be released.
370
    //     for (butil::LinkNode<Agent>* node = _agents.head();
371
    //          node != _agents.end();) {
372
    //         node->value()->reset(ElementTp(), nullptr);
373
    //         butil::LinkNode<Agent>* const saved_next = node->next();
374
    //         node->RemoveFromList();
375
    //         node = saved_next;
376
    //     }
377
    // }
378
379
8
    const BinaryOp& op() const { return _op; }
Unexecuted instantiation: bvar::detail::AgentCombiner<long, long, bvar::detail::MaxTo<long> >::op() const
Unexecuted instantiation: bvar::detail::AgentCombiner<long, long, bvar::detail::AddTo<long> >::op() const
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::Collected*, bvar::Collected*, bvar::CombineCollected>::op() const
bvar::detail::AgentCombiner<bvar::detail::Sampler*, bvar::detail::Sampler*, bvar::detail::CombineSampler>::op() const
Line
Count
Source
379
8
    const BinaryOp& op() const { return _op; }
380
381
0
    bool valid() const { return _id >= 0; }
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::Stat, unsigned long, bvar::IntRecorder::AddToStat>::valid() const
Unexecuted instantiation: bvar::detail::AgentCombiner<bvar::detail::PercentileSamples<254ul>, bvar::detail::PercentileSamples<30ul>, bvar::detail::detail::AddPercentileSamples>::valid() const
382
383
private:
384
    AgentId                                     _id;
385
    BinaryOp                                    _op;
386
    mutable butil::Lock                          _lock;
387
    ResultTp                                    _global_result;
388
    ResultTp                                    _result_identity;
389
    ElementTp                                   _element_identity;
390
    butil::LinkedList<Agent>                     _agents;
391
};
392
393
}  // namespace detail
394
}  // namespace bvar
395
396
#endif  // BVAR_COMBINER_H