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
28namespace folly {
29
30namespace {
31
32class 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
43class 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
90class 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
118GlobalThreadPoolList& GlobalThreadPoolList::instance() {
119 static folly::Indestructible<GlobalThreadPoolList> ret;
120 return *ret;
121}
122
123void GlobalThreadPoolList::registerThreadPool(
124 ThreadPoolListHook* threadPoolId,
125 std::string name) {
126 globalListImpl_->registerThreadPool(threadPoolId, name);
127}
128
129void GlobalThreadPoolList::unregisterThreadPool(
130 ThreadPoolListHook* threadPoolId) {
131 globalListImpl_->unregisterThreadPool(threadPoolId);
132}
133
134void 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
143void GlobalThreadPoolList::unregisterThreadPoolThread(
144 ThreadPoolListHook* threadPoolId,
145 std::thread::id threadId) {
146 (void)threadPoolId;
147 (void)threadId;
148 globalListImpl_->unregisterThreadPoolThread(threadPoolId, threadId);
149}
150
151void 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
160void 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
171void 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
182void 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
193ThreadListHook::ThreadListHook(
194 ThreadPoolListHook* poolId,
195 std::thread::id threadId) {
196 poolId_ = poolId;
197 threadId_ = threadId;
198}
199
200ThreadListHook::~ThreadListHook() {
201 GlobalThreadPoolList::instance().unregisterThreadPoolThread(
202 poolId_, threadId_);
203}
204
205ThreadPoolListHook::ThreadPoolListHook(std::string name) {
206 GlobalThreadPoolList::instance().registerThreadPool(this, name);
207}
208
209ThreadPoolListHook::~ThreadPoolListHook() {
210 GlobalThreadPoolList::instance().unregisterThreadPool(this);
211}
212
213void ThreadPoolListHook::registerThread() {
214 GlobalThreadPoolList::instance().registerThreadPoolThread(
215 this, std::this_thread::get_id());
216}
217
218} // namespace folly
219