1 | /* |
2 | * Copyright 2017-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #include <folly/executors/GlobalThreadPoolList.h> |
18 | |
19 | #include <memory> |
20 | #include <string> |
21 | #include <vector> |
22 | |
23 | #include <folly/CppAttributes.h> |
24 | #include <folly/Indestructible.h> |
25 | #include <folly/Synchronized.h> |
26 | #include <folly/ThreadLocal.h> |
27 | |
28 | namespace folly { |
29 | |
30 | namespace { |
31 | |
32 | class ThreadListHook { |
33 | public: |
34 | ThreadListHook(ThreadPoolListHook* poolId, std::thread::id threadId); |
35 | ~ThreadListHook(); |
36 | |
37 | private: |
38 | ThreadListHook() {} |
39 | ThreadPoolListHook* poolId_; |
40 | std::thread::id threadId_; |
41 | }; |
42 | |
43 | class GlobalThreadPoolListImpl { |
44 | public: |
45 | GlobalThreadPoolListImpl() {} |
46 | |
47 | void registerThreadPool(ThreadPoolListHook* threadPoolId, std::string name); |
48 | |
49 | void unregisterThreadPool(ThreadPoolListHook* threadPoolId); |
50 | |
51 | void registerThreadPoolThread( |
52 | ThreadPoolListHook* threadPoolId, |
53 | std::thread::id threadId); |
54 | |
55 | void unregisterThreadPoolThread( |
56 | ThreadPoolListHook* threadPoolId, |
57 | std::thread::id threadId); |
58 | |
59 | private: |
60 | struct PoolInfo { |
61 | ThreadPoolListHook* addr; |
62 | std::string name; |
63 | std::vector<std::thread::id> threads; |
64 | }; |
65 | |
66 | struct Pools { |
67 | // Just a vector since ease of access from gdb is the most important |
68 | // property |
69 | std::vector<PoolInfo> poolsInfo_; |
70 | |
71 | std::vector<std::thread::id>* FOLLY_NULLABLE |
72 | getThreadVector(void* threadPoolId) { |
73 | for (auto& elem : vector()) { |
74 | if (elem.addr == threadPoolId) { |
75 | return &elem.threads; |
76 | } |
77 | } |
78 | |
79 | return nullptr; |
80 | } |
81 | |
82 | std::vector<PoolInfo>& vector() { |
83 | return poolsInfo_; |
84 | } |
85 | }; |
86 | |
87 | Pools pools_; |
88 | }; |
89 | |
90 | class GlobalThreadPoolList { |
91 | public: |
92 | GlobalThreadPoolList() {} |
93 | |
94 | static GlobalThreadPoolList& instance(); |
95 | |
96 | void registerThreadPool(ThreadPoolListHook* threadPoolId, std::string name); |
97 | |
98 | void unregisterThreadPool(ThreadPoolListHook* threadPoolId); |
99 | |
100 | void registerThreadPoolThread( |
101 | ThreadPoolListHook* threadPoolId, |
102 | std::thread::id threadId); |
103 | |
104 | void unregisterThreadPoolThread( |
105 | ThreadPoolListHook* threadPoolId, |
106 | std::thread::id threadId); |
107 | |
108 | GlobalThreadPoolList(GlobalThreadPoolList const&) = delete; |
109 | void operator=(GlobalThreadPoolList const&) = delete; |
110 | |
111 | private: |
112 | folly::Synchronized<GlobalThreadPoolListImpl> globalListImpl_; |
113 | folly::ThreadLocalPtr<ThreadListHook> threadHook_; |
114 | }; |
115 | |
116 | } // namespace |
117 | |
118 | GlobalThreadPoolList& GlobalThreadPoolList::instance() { |
119 | static folly::Indestructible<GlobalThreadPoolList> ret; |
120 | return *ret; |
121 | } |
122 | |
123 | void GlobalThreadPoolList::registerThreadPool( |
124 | ThreadPoolListHook* threadPoolId, |
125 | std::string name) { |
126 | globalListImpl_->registerThreadPool(threadPoolId, name); |
127 | } |
128 | |
129 | void GlobalThreadPoolList::unregisterThreadPool( |
130 | ThreadPoolListHook* threadPoolId) { |
131 | globalListImpl_->unregisterThreadPool(threadPoolId); |
132 | } |
133 | |
134 | void GlobalThreadPoolList::registerThreadPoolThread( |
135 | ThreadPoolListHook* threadPoolId, |
136 | std::thread::id threadId) { |
137 | DCHECK(!threadHook_); |
138 | threadHook_.reset(make_unique<ThreadListHook>(threadPoolId, threadId)); |
139 | |
140 | globalListImpl_->registerThreadPoolThread(threadPoolId, threadId); |
141 | } |
142 | |
143 | void GlobalThreadPoolList::unregisterThreadPoolThread( |
144 | ThreadPoolListHook* threadPoolId, |
145 | std::thread::id threadId) { |
146 | (void)threadPoolId; |
147 | (void)threadId; |
148 | globalListImpl_->unregisterThreadPoolThread(threadPoolId, threadId); |
149 | } |
150 | |
151 | void GlobalThreadPoolListImpl::registerThreadPool( |
152 | ThreadPoolListHook* threadPoolId, |
153 | std::string name) { |
154 | PoolInfo info; |
155 | info.name = name; |
156 | info.addr = threadPoolId; |
157 | pools_.vector().push_back(info); |
158 | } |
159 | |
160 | void GlobalThreadPoolListImpl::unregisterThreadPool( |
161 | ThreadPoolListHook* threadPoolId) { |
162 | auto& vector = pools_.vector(); |
163 | vector.erase( |
164 | std::remove_if( |
165 | vector.begin(), |
166 | vector.end(), |
167 | [=](PoolInfo& i) { return i.addr == threadPoolId; }), |
168 | vector.end()); |
169 | } |
170 | |
171 | void GlobalThreadPoolListImpl::registerThreadPoolThread( |
172 | ThreadPoolListHook* threadPoolId, |
173 | std::thread::id threadId) { |
174 | auto vec = pools_.getThreadVector(threadPoolId); |
175 | if (vec == nullptr) { |
176 | return; |
177 | } |
178 | |
179 | vec->push_back(threadId); |
180 | } |
181 | |
182 | void GlobalThreadPoolListImpl::unregisterThreadPoolThread( |
183 | ThreadPoolListHook* threadPoolId, |
184 | std::thread::id threadId) { |
185 | auto vec = pools_.getThreadVector(threadPoolId); |
186 | if (vec == nullptr) { |
187 | return; |
188 | } |
189 | |
190 | vec->erase(std::remove(vec->begin(), vec->end(), threadId), vec->end()); |
191 | } |
192 | |
193 | ThreadListHook::ThreadListHook( |
194 | ThreadPoolListHook* poolId, |
195 | std::thread::id threadId) { |
196 | poolId_ = poolId; |
197 | threadId_ = threadId; |
198 | } |
199 | |
200 | ThreadListHook::~ThreadListHook() { |
201 | GlobalThreadPoolList::instance().unregisterThreadPoolThread( |
202 | poolId_, threadId_); |
203 | } |
204 | |
205 | ThreadPoolListHook::ThreadPoolListHook(std::string name) { |
206 | GlobalThreadPoolList::instance().registerThreadPool(this, name); |
207 | } |
208 | |
209 | ThreadPoolListHook::~ThreadPoolListHook() { |
210 | GlobalThreadPoolList::instance().unregisterThreadPool(this); |
211 | } |
212 | |
213 | void ThreadPoolListHook::registerThread() { |
214 | GlobalThreadPoolList::instance().registerThreadPoolThread( |
215 | this, std::this_thread::get_id()); |
216 | } |
217 | |
218 | } // namespace folly |
219 | |