--- /dev/null
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/GlobalThreadPoolList.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <folly/CppAttributes.h>
+#include <folly/Indestructible.h>
+#include <folly/Synchronized.h>
+#include <folly/ThreadLocal.h>
+
+namespace folly {
+
+namespace {
+
+class ThreadListHook {
+ public:
+ ThreadListHook(ThreadPoolListHook* poolId, std::thread::id threadId);
+ ~ThreadListHook();
+
+ private:
+ ThreadListHook() {}
+ ThreadPoolListHook* poolId_;
+ std::thread::id threadId_;
+};
+
+class GlobalThreadPoolListImpl {
+ public:
+ GlobalThreadPoolListImpl() {}
+
+ void registerThreadPool(ThreadPoolListHook* threadPoolId, std::string name);
+
+ void unregisterThreadPool(ThreadPoolListHook* threadPoolId);
+
+ void registerThreadPoolThread(
+ ThreadPoolListHook* threadPoolId,
+ std::thread::id threadId);
+
+ void unregisterThreadPoolThread(
+ ThreadPoolListHook* threadPoolId,
+ std::thread::id threadId);
+
+ private:
+ struct PoolInfo {
+ ThreadPoolListHook* addr;
+ std::string name;
+ std::vector<std::thread::id> threads;
+ };
+
+ struct Pools {
+ // Just a vector since ease of access from gdb is the most important
+ // property
+ std::vector<PoolInfo> poolsInfo_;
+
+ std::vector<std::thread::id>* FOLLY_NULLABLE
+ getThreadVector(void* threadPoolId) {
+ for (auto& elem : vector()) {
+ if (elem.addr == threadPoolId) {
+ return &elem.threads;
+ }
+ }
+
+ return nullptr;
+ }
+
+ std::vector<PoolInfo>& vector() {
+ return poolsInfo_;
+ }
+ };
+
+ Pools pools_;
+};
+
+class GlobalThreadPoolList {
+ public:
+ GlobalThreadPoolList() {}
+
+ static GlobalThreadPoolList& instance();
+
+ void registerThreadPool(ThreadPoolListHook* threadPoolId, std::string name);
+
+ void unregisterThreadPool(ThreadPoolListHook* threadPoolId);
+
+ void registerThreadPoolThread(
+ ThreadPoolListHook* threadPoolId,
+ std::thread::id threadId);
+
+ void unregisterThreadPoolThread(
+ ThreadPoolListHook* threadPoolId,
+ std::thread::id threadId);
+
+ GlobalThreadPoolList(GlobalThreadPoolList const&) = delete;
+ void operator=(GlobalThreadPoolList const&) = delete;
+
+ private:
+ folly::Synchronized<GlobalThreadPoolListImpl> globalListImpl_;
+ folly::ThreadLocalPtr<ThreadListHook> threadHook_;
+};
+
+} // namespace
+
+GlobalThreadPoolList& GlobalThreadPoolList::instance() {
+ static folly::Indestructible<GlobalThreadPoolList> ret;
+ return *ret;
+}
+
+void GlobalThreadPoolList::registerThreadPool(
+ ThreadPoolListHook* threadPoolId,
+ std::string name) {
+ globalListImpl_->registerThreadPool(threadPoolId, name);
+}
+
+void GlobalThreadPoolList::unregisterThreadPool(
+ ThreadPoolListHook* threadPoolId) {
+ globalListImpl_->unregisterThreadPool(threadPoolId);
+}
+
+void GlobalThreadPoolList::registerThreadPoolThread(
+ ThreadPoolListHook* threadPoolId,
+ std::thread::id threadId) {
+ DCHECK(!threadHook_);
+ threadHook_.reset(make_unique<ThreadListHook>(threadPoolId, threadId));
+
+ globalListImpl_->registerThreadPoolThread(threadPoolId, threadId);
+}
+
+void GlobalThreadPoolList::unregisterThreadPoolThread(
+ ThreadPoolListHook* threadPoolId,
+ std::thread::id threadId) {
+ (void)threadPoolId;
+ (void)threadId;
+ globalListImpl_->unregisterThreadPoolThread(threadPoolId, threadId);
+}
+
+void GlobalThreadPoolListImpl::registerThreadPool(
+ ThreadPoolListHook* threadPoolId,
+ std::string name) {
+ PoolInfo info;
+ info.name = name;
+ info.addr = threadPoolId;
+ pools_.vector().push_back(info);
+}
+
+void GlobalThreadPoolListImpl::unregisterThreadPool(
+ ThreadPoolListHook* threadPoolId) {
+ auto& vector = pools_.vector();
+ vector.erase(
+ std::remove_if(
+ vector.begin(),
+ vector.end(),
+ [=](PoolInfo& i) { return i.addr == threadPoolId; }),
+ vector.end());
+}
+
+void GlobalThreadPoolListImpl::registerThreadPoolThread(
+ ThreadPoolListHook* threadPoolId,
+ std::thread::id threadId) {
+ auto vec = pools_.getThreadVector(threadPoolId);
+ if (vec == nullptr) {
+ return;
+ }
+
+ vec->push_back(threadId);
+}
+
+void GlobalThreadPoolListImpl::unregisterThreadPoolThread(
+ ThreadPoolListHook* threadPoolId,
+ std::thread::id threadId) {
+ auto vec = pools_.getThreadVector(threadPoolId);
+ if (vec == nullptr) {
+ return;
+ }
+
+ vec->erase(std::remove(vec->begin(), vec->end(), threadId), vec->end());
+}
+
+ThreadListHook::ThreadListHook(
+ ThreadPoolListHook* poolId,
+ std::thread::id threadId) {
+ poolId_ = poolId;
+ threadId_ = threadId;
+}
+
+ThreadListHook::~ThreadListHook() {
+ GlobalThreadPoolList::instance().unregisterThreadPoolThread(
+ poolId_, threadId_);
+}
+
+ThreadPoolListHook::ThreadPoolListHook(std::string name) {
+ GlobalThreadPoolList::instance().registerThreadPool(this, name);
+}
+
+ThreadPoolListHook::~ThreadPoolListHook() {
+ GlobalThreadPoolList::instance().unregisterThreadPool(this);
+}
+
+void ThreadPoolListHook::registerThread() {
+ GlobalThreadPoolList::instance().registerThreadPoolThread(
+ this, std::this_thread::get_id());
+}
+
+} // folly
--- /dev/null
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <folly/Indestructible.h>
+#include <folly/Synchronized.h>
+#include <folly/ThreadLocal.h>
+
+namespace folly {
+
+/**
+ * A hook for tracking which threads belong to which thread pools.
+ * This is used only by a gdb extension to aid in debugging. You won't be able
+ * to see any useful information from within C++ code.
+ *
+ * An instance of ThreadPoolListHook should be created in the thread pool class
+ * that you want to keep track of. Then, to register a thread you call
+ * registerThread() on your instance of ThreadPoolListHook from that thread.
+ *
+ * When a thread exits it will be removed from the list
+ * When the thread pool is destroyed, it will be removed from the list
+ */
+class ThreadPoolListHook {
+ public:
+ /**
+ * Name is used to identify the thread pool when listing threads.
+ */
+ explicit ThreadPoolListHook(std::string name);
+ ~ThreadPoolListHook();
+
+ /**
+ * Call this from any new thread that the thread pool creates.
+ */
+ void registerThread();
+
+ ThreadPoolListHook(const ThreadPoolListHook& other) = delete;
+ ThreadPoolListHook& operator=(const ThreadPoolListHook&) = delete;
+
+ private:
+ ThreadPoolListHook();
+};
+
+} // folly