Coverage Report

Created: 2025-09-17 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/brpc/src/butil/thread_key.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
#ifndef  BUTIL_THREAD_KEY_H
19
#define  BUTIL_THREAD_KEY_H
20
21
#include <limits>
22
#include <pthread.h>
23
#include <stdlib.h>
24
#include <vector>
25
#include "butil/scoped_lock.h"
26
#include "butil/type_traits.h"
27
28
namespace butil {
29
30
typedef void (*DtorFunction)(void *);
31
32
class ThreadKey {
33
public:
34
    friend int thread_key_create(ThreadKey& thread_key, DtorFunction dtor);
35
    friend int thread_key_delete(ThreadKey& thread_key);
36
    friend int thread_setspecific(ThreadKey& thread_key, void* data);
37
    friend void* thread_getspecific(ThreadKey& thread_key);
38
39
    static constexpr size_t InvalidID = std::numeric_limits<size_t>::max();
40
    static constexpr size_t InitSeq = 0;
41
42
0
    constexpr ThreadKey() : _id(InvalidID), _seq(InitSeq) {}
43
44
0
    ~ThreadKey() {
45
0
        Reset();
46
0
    }
47
48
    ThreadKey(ThreadKey&& other) noexcept
49
        : _id(other._id)
50
0
        , _seq(other._seq) {
51
0
        other.Reset();
52
0
    }
53
54
    ThreadKey& operator=(ThreadKey&& other) noexcept;
55
56
    ThreadKey(const ThreadKey& other) = delete;
57
    ThreadKey& operator=(const ThreadKey& other) = delete;
58
59
    bool Valid() const;
60
61
0
    void Reset() {
62
0
        _id = InvalidID;
63
0
        _seq = InitSeq;
64
0
    }
65
66
private:
67
    size_t _id; // Key id.
68
    // Sequence number form g_thread_keys set in thread_key_create.
69
    size_t _seq;
70
};
71
72
struct ThreadKeyInfo {
73
0
    ThreadKeyInfo() : seq(0), dtor(NULL) {}
74
75
    size_t seq; // Already allocated?
76
    DtorFunction dtor; // Destruction routine.
77
};
78
79
struct ThreadKeyTLS {
80
0
    ThreadKeyTLS() : seq(0), data(NULL) {}
81
82
    // Sequence number form ThreadKey,
83
    // set in `thread_setspecific',
84
    // used to check if the key is valid in `thread_getspecific'.
85
    size_t seq;
86
    void* data; // User data.
87
};
88
89
// pthread_key_xxx implication without num limit of key.
90
int thread_key_create(ThreadKey& thread_key, DtorFunction dtor);
91
int thread_key_delete(ThreadKey& thread_key);
92
int thread_setspecific(ThreadKey& thread_key, void* data);
93
void* thread_getspecific(ThreadKey& thread_key);
94
95
96
template <typename T>
97
class ThreadLocal {
98
public:
99
0
    ThreadLocal() : ThreadLocal(false) {}
100
101
    explicit ThreadLocal(bool delete_on_thread_exit);
102
103
    ~ThreadLocal();
104
105
    // non-copyable
106
    ThreadLocal(const ThreadLocal&) = delete;
107
    ThreadLocal& operator=(const ThreadLocal&) = delete;
108
109
    T* get();
110
111
    T* operator->() { return get(); }
112
113
    T& operator*() { return *get(); }
114
115
    // Iterate through all thread local objects.
116
    // Callback, which must accept Args params and return void,
117
    // will be called under a thread lock.
118
    template <typename Callback>
119
    void for_each(Callback&& callback) {
120
        BAIDU_CASSERT(
121
            (is_result_void<Callback, T*>::value),
122
            "Callback must accept Args params and return void");
123
        BAIDU_SCOPED_LOCK(_mutex);
124
        for (auto ptr : ptrs) {
125
            callback(ptr);
126
        }
127
    }
128
129
    void reset(T* ptr);
130
131
    void reset() {
132
        reset(NULL);
133
    }
134
135
private:
136
0
    static void DefaultDtor(void* ptr) {
137
0
        if (ptr) {
138
0
            delete static_cast<T*>(ptr);
139
0
        }
140
0
    }
141
142
    ThreadKey _key;
143
    pthread_mutex_t _mutex;
144
    // All pointers of data allocated by the ThreadLocal.
145
    std::vector<T*> ptrs;
146
    // Delete data on thread exit or destructor of ThreadLocal.
147
    bool _delete_on_thread_exit;
148
};
149
150
template <typename T>
151
ThreadLocal<T>::ThreadLocal(bool delete_on_thread_exit)
152
0
        : _mutex(PTHREAD_MUTEX_INITIALIZER)
153
0
        , _delete_on_thread_exit(delete_on_thread_exit) {
154
0
    DtorFunction dtor = _delete_on_thread_exit ? DefaultDtor : NULL;
155
0
    thread_key_create(_key, dtor);
156
0
}
157
158
159
template <typename T>
160
0
ThreadLocal<T>::~ThreadLocal() {
161
0
    thread_key_delete(_key);
162
0
    if (!_delete_on_thread_exit) {
163
0
        BAIDU_SCOPED_LOCK(_mutex);
164
0
        for (auto ptr : ptrs) {
165
0
            DefaultDtor(ptr);
166
0
        }
167
0
    }
168
0
    pthread_mutex_destroy(&_mutex);
169
0
}
170
171
template <typename T>
172
0
T* ThreadLocal<T>::get() {
173
0
    T* ptr = static_cast<T*>(thread_getspecific(_key));
174
0
    if (!ptr) {
175
0
        ptr = new (std::nothrow) T;
176
0
        if (!ptr) {
177
0
            return NULL;
178
0
        }
179
0
        int rc = thread_setspecific(_key, ptr);
180
0
        if (rc != 0) {
181
0
            DefaultDtor(ptr);
182
0
            return NULL;
183
0
        }
184
0
        {
185
0
            BAIDU_SCOPED_LOCK(_mutex);
186
0
            ptrs.push_back(ptr);
187
0
        }
188
0
    }
189
0
    return ptr;
190
0
}
191
192
template <typename T>
193
void ThreadLocal<T>::reset(T* ptr) {
194
    T* old_ptr = get();
195
    if (ptr == old_ptr) {
196
        return;
197
    }
198
    if (thread_setspecific(_key, ptr) != 0) {
199
        return;
200
    }
201
    {
202
        BAIDU_SCOPED_LOCK(_mutex);
203
        if (ptr) {
204
            ptrs.push_back(ptr);
205
        }
206
        // Remove and delete old_ptr.
207
        if (old_ptr) {
208
            auto iter = std::remove(ptrs.begin(), ptrs.end(), old_ptr);
209
            if (iter != ptrs.end()) {
210
                ptrs.erase(iter, ptrs.end());
211
            }
212
            DefaultDtor(old_ptr);
213
        }
214
    }
215
}
216
217
}
218
219
220
#endif // BUTIL_THREAD_KEY_H