/src/brpc/src/butil/thread_key.cpp
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 | | #include "thread_key.h" |
19 | | #include "pthread.h" |
20 | | #include <deque> |
21 | | #include "butil/thread_local.h" |
22 | | |
23 | | namespace butil { |
24 | | |
25 | | // Check whether an entry is unused. |
26 | 0 | #define KEY_UNUSED(p) (((p) & 1) == 0) |
27 | | |
28 | | // Check whether a key is usable. We cannot reuse an allocated key if |
29 | | // the sequence counter would overflow after the next destroy call. |
30 | | // This would mean that we potentially free memory for a key with the |
31 | | // same sequence. This is *very* unlikely to happen, A program would |
32 | | // have to create and destroy a key 2^31 times. If it should happen we |
33 | | // simply don't use this specific key anymore. |
34 | 0 | #define KEY_USABLE(p) (((size_t) (p)) < ((size_t) ((p) + 2))) |
35 | | |
36 | | static const uint32_t THREAD_KEY_RESERVE = 8096; |
37 | | pthread_mutex_t g_thread_key_mutex = PTHREAD_MUTEX_INITIALIZER; |
38 | | static size_t g_id = 0; |
39 | | static std::deque<size_t>* g_free_ids = NULL; |
40 | | static std::vector<ThreadKeyInfo>* g_thread_keys = NULL; |
41 | | static __thread std::vector<ThreadKeyTLS>* thread_key_tls_data = NULL; |
42 | | |
43 | 0 | ThreadKey& ThreadKey::operator=(ThreadKey&& other) noexcept { |
44 | 0 | if (this == &other) { |
45 | 0 | return *this; |
46 | 0 | } |
47 | | |
48 | 0 | _id = other._id; |
49 | 0 | _seq = other._seq; |
50 | 0 | other.Reset(); |
51 | 0 | return *this; |
52 | 0 | } |
53 | | |
54 | 0 | bool ThreadKey::Valid() const { |
55 | 0 | return _id != InvalidID && !KEY_UNUSED(_seq); |
56 | 0 | } |
57 | | |
58 | 0 | static void DestroyTlsData() { |
59 | 0 | if (!thread_key_tls_data) { |
60 | 0 | return; |
61 | 0 | } |
62 | 0 | std::vector<ThreadKeyInfo> dummy_keys; |
63 | 0 | { |
64 | 0 | BAIDU_SCOPED_LOCK(g_thread_key_mutex); |
65 | 0 | dummy_keys.insert(dummy_keys.end(), |
66 | 0 | g_thread_keys->begin(), |
67 | 0 | g_thread_keys->end()); |
68 | 0 | } |
69 | 0 | for (size_t i = 0; i < thread_key_tls_data->size(); ++i) { |
70 | 0 | if (!KEY_UNUSED(dummy_keys[i].seq) && dummy_keys[i].dtor) { |
71 | 0 | dummy_keys[i].dtor((*thread_key_tls_data)[i].data); |
72 | 0 | } |
73 | 0 | } |
74 | 0 | delete thread_key_tls_data; |
75 | 0 | thread_key_tls_data = NULL; |
76 | 0 | } |
77 | | |
78 | 0 | int thread_key_create(ThreadKey& thread_key, DtorFunction dtor) { |
79 | 0 | BAIDU_SCOPED_LOCK(g_thread_key_mutex); |
80 | 0 | if (BAIDU_UNLIKELY(!g_free_ids)) { |
81 | 0 | g_free_ids = new std::deque<size_t>; |
82 | 0 | } |
83 | 0 | size_t id; |
84 | 0 | if (!g_free_ids->empty()) { |
85 | 0 | id = g_free_ids->back(); |
86 | 0 | g_free_ids->pop_back(); |
87 | 0 | } else { |
88 | 0 | if (g_id >= ThreadKey::InvalidID) { |
89 | | // No more available ids. |
90 | 0 | return EAGAIN; |
91 | 0 | } |
92 | 0 | id = g_id++; |
93 | 0 | if (BAIDU_UNLIKELY(!g_thread_keys)) { |
94 | 0 | g_thread_keys = new std::vector<ThreadKeyInfo>; |
95 | 0 | g_thread_keys->reserve(THREAD_KEY_RESERVE); |
96 | 0 | } |
97 | 0 | g_thread_keys->resize(id + 1); |
98 | 0 | } |
99 | | |
100 | 0 | ++((*g_thread_keys)[id].seq); |
101 | 0 | (*g_thread_keys)[id].dtor = dtor; |
102 | 0 | thread_key._id = id; |
103 | 0 | thread_key._seq = (*g_thread_keys)[id].seq; |
104 | |
|
105 | 0 | return 0; |
106 | 0 | } |
107 | | |
108 | 0 | int thread_key_delete(ThreadKey& thread_key) { |
109 | 0 | if (BAIDU_UNLIKELY(!thread_key.Valid())) { |
110 | 0 | return EINVAL; |
111 | 0 | } |
112 | | |
113 | 0 | BAIDU_SCOPED_LOCK(g_thread_key_mutex); |
114 | 0 | size_t id = thread_key._id; |
115 | 0 | size_t seq = thread_key._seq; |
116 | 0 | if (id >= g_thread_keys->size() || |
117 | 0 | seq != (*g_thread_keys)[id].seq || |
118 | 0 | KEY_UNUSED((*g_thread_keys)[id].seq)) { |
119 | 0 | thread_key.Reset(); |
120 | 0 | return EINVAL; |
121 | 0 | } |
122 | | |
123 | 0 | ++((*g_thread_keys)[id].seq); |
124 | | // Collect the usable key id for reuse. |
125 | 0 | if (KEY_USABLE((*g_thread_keys)[id].seq)) { |
126 | 0 | g_free_ids->push_back(id); |
127 | 0 | } |
128 | 0 | thread_key.Reset(); |
129 | |
|
130 | 0 | return 0; |
131 | 0 | } |
132 | | |
133 | 0 | int thread_setspecific(ThreadKey& thread_key, void* data) { |
134 | 0 | if (BAIDU_UNLIKELY(!thread_key.Valid())) { |
135 | 0 | return EINVAL; |
136 | 0 | } |
137 | 0 | size_t id = thread_key._id; |
138 | 0 | size_t seq = thread_key._seq; |
139 | 0 | if (BAIDU_UNLIKELY(!thread_key_tls_data)) { |
140 | 0 | thread_key_tls_data = new std::vector<ThreadKeyTLS>; |
141 | 0 | thread_key_tls_data->reserve(THREAD_KEY_RESERVE); |
142 | | // Register the destructor of tls_data in this thread. |
143 | 0 | butil::thread_atexit(DestroyTlsData); |
144 | 0 | } |
145 | |
|
146 | 0 | if (id >= thread_key_tls_data->size()) { |
147 | 0 | thread_key_tls_data->resize(id + 1); |
148 | 0 | } |
149 | |
|
150 | 0 | (*thread_key_tls_data)[id].seq = seq; |
151 | 0 | (*thread_key_tls_data)[id].data = data; |
152 | |
|
153 | 0 | return 0; |
154 | 0 | } |
155 | | |
156 | 0 | void* thread_getspecific(ThreadKey& thread_key) { |
157 | 0 | if (BAIDU_UNLIKELY(!thread_key.Valid())) { |
158 | 0 | return NULL; |
159 | 0 | } |
160 | 0 | size_t id = thread_key._id; |
161 | 0 | size_t seq = thread_key._seq; |
162 | 0 | if (BAIDU_UNLIKELY(!thread_key_tls_data || |
163 | 0 | id >= thread_key_tls_data->size() || |
164 | 0 | (*thread_key_tls_data)[id].seq != seq)){ |
165 | 0 | return NULL; |
166 | 0 | } |
167 | | |
168 | 0 | return (*thread_key_tls_data)[id].data; |
169 | 0 | } |
170 | | |
171 | | } // namespace butil |