Skip to content

Instantly share code, notes, and snippets.

@jamesob
Created May 1, 2018 18:36
Show Gist options
  • Save jamesob/88f7cf082ee0bf97a2411bf8bd120594 to your computer and use it in GitHub Desktop.
Save jamesob/88f7cf082ee0bf97a2411bf8bd120594 to your computer and use it in GitHub Desktop.
diff -uw <(git diff master..threadnames.1) <(git diff master..threadnames.3)
--- /proc/self/fd/15 2018-05-01 14:36:17.343988635 -0400
+++ /proc/self/fd/16 2018-05-01 14:36:17.343988635 -0400
@@ -196,10 +196,14 @@
qt/test/wallettests.cpp \
wallet/test/wallet_test_fixture.cpp
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
-index 91d3a3d..f7eb712 100644
+index 91d3a3d..40bc478 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
-@@ -83,7 +83,6 @@ BITCOIN_TESTS =\
+@@ -80,10 +80,10 @@ BITCOIN_TESTS =\
+ test/sigopcount_tests.cpp \
+ test/skiplist_tests.cpp \
+ test/streams_tests.cpp \
++ test/threadnames_tests.cpp \
test/timedata_tests.cpp \
test/torcontrol_tests.cpp \
test/transaction_tests.cpp \
@@ -1990,6 +1994,128 @@
BOOST_CHECK(!isInvalid);
}
+diff --git a/src/test/threadnames_tests.cpp b/src/test/threadnames_tests.cpp
+new file mode 100644
+index 0000000..a97cd08
+--- /dev/null
++++ b/src/test/threadnames_tests.cpp
+@@ -0,0 +1,116 @@
++// Copyright (c) 2018 The Bitcoin Core developers
++// Distributed under the MIT software license, see the accompanying
++// file COPYING or http://www.opensource.org/licenses/mit-license.php.
++
++#include <threadnames.h>
++#include <test/test_bitcoin.h>
++
++#include <thread>
++#include <vector>
++#include <set>
++#include <mutex>
++
++#include <boost/test/unit_test.hpp>
++
++BOOST_FIXTURE_TEST_SUITE(threadnames_tests, BasicTestingSetup)
++
++BOOST_AUTO_TEST_CASE(threadnames_test_process_rename_serial)
++{
++ ThreadNameRegistry reg;
++
++ std::string original_process_name = reg.GetProcessName();
++
++ reg.RenameProcess("bazzz");
++ BOOST_CHECK_EQUAL(reg.GetProcessName(), "bazzz");
++ reg.RenameProcess("barrr");
++ BOOST_CHECK_EQUAL(reg.GetProcessName(), "barrr");
++
++ reg.RenameProcess(original_process_name.c_str());
++ BOOST_CHECK_EQUAL(reg.GetProcessName(), original_process_name);
++}
++
++/**
++ * Ensure that expect_multiple prevents collisions by appending a numeric suffix.
++ */
++BOOST_AUTO_TEST_CASE(threadnames_test_rename_multiple_serial)
++{
++ ThreadNameRegistry reg;
++
++ std::string original_process_name = reg.GetProcessName();
++
++ BOOST_CHECK(reg.Rename("foo", /*expect_multiple=*/ true));
++ BOOST_CHECK_EQUAL(reg.GetName(), "foo.0");
++
++ // Can't rename to "foo" as that would be a collision.
++ BOOST_CHECK_EQUAL(reg.Rename("foo", /*expect_multiple=*/ false), false);
++ BOOST_CHECK_EQUAL(reg.GetName(), "foo.0");
++
++ BOOST_CHECK(reg.Rename("foo", /*expect_multiple=*/ true));
++ BOOST_CHECK_EQUAL(reg.GetName(), "foo.1");
++
++ BOOST_CHECK(reg.Rename("foo", /*expect_multiple=*/ true));
++ BOOST_CHECK_EQUAL(reg.GetName(), "foo.2");
++
++ reg.RenameProcess(original_process_name.c_str());
++ BOOST_CHECK_EQUAL(reg.GetProcessName(), original_process_name);
++}
++
++/**
++ * Run a bunch of threads to all call Rename with some parameters.
++ *
++ * @return the set of name each thread has after attempted renaming.
++ */
++std::set<std::string> RenameEnMasse(int num_threads, bool expect_multiple)
++{
++ ThreadNameRegistry reg;
++ std::vector<std::thread> threads;
++ std::set<std::string> names;
++ std::mutex lock;
++
++ auto RenameThisThread = [&]() {
++ reg.Rename("test_thread", /*expect_multiple=*/ expect_multiple);
++ std::lock_guard<std::mutex> guard(lock);
++ names.insert(reg.GetName());
++ };
++
++ for (int i = 0; i < num_threads; ++i) {
++ threads.push_back(std::thread(RenameThisThread));
++ }
++
++ for (std::thread& thread : threads) thread.join();
++
++ return names;
++}
++
++/**
++ * Rename a bunch of threads with the same basename (expect_multiple=true), ensuring suffixes are
++ * applied properly.
++ */
++BOOST_AUTO_TEST_CASE(threadnames_test_rename_multiple_threaded)
++{
++ std::set<std::string> names = RenameEnMasse(100, true);
++
++ BOOST_CHECK_EQUAL(names.size(), 100);
++
++ // Names "test_thread.[n]" should exist for n = [0, 99]
++ for (int i = 0; i < 100; ++i) {
++ BOOST_CHECK(names.find("test_thread." + std::to_string(i)) != names.end());
++ }
++
++}
++
++/**
++ * Rename a bunch of threads with the same basename (expect_multiple=false), ensuring only one
++ * rename succeeds.
++ */
++BOOST_AUTO_TEST_CASE(threadnames_test_rename_threaded)
++{
++ std::set<std::string> names = RenameEnMasse(100, false);
++
++ // Only one thread's Rename should have succeeded for the same name.
++ BOOST_CHECK_EQUAL(names.count("test_thread"), 1);
++ BOOST_CHECK_EQUAL(names.size(), 2); // test_thread and test_bitcoin (name of parent thread)
++
++}
++
++BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp
deleted file mode 100644
index 14158f2..0000000
@@ -2089,10 +2215,10 @@
void reset();
diff --git a/src/threadnames.cpp b/src/threadnames.cpp
new file mode 100644
-index 0000000..d044825
+index 0000000..305c3b4
--- /dev/null
+++ b/src/threadnames.cpp
-@@ -0,0 +1,147 @@
+@@ -0,0 +1,135 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -2100,7 +2226,13 @@
+#include <threadnames.h>
+#include <tinyformat.h>
+
++#include <atomic>
++#include <cstdint>
++#include <string>
++
++#if !defined(HAVE_THREAD_LOCAL)
+#include <boost/thread.hpp>
++#endif
+
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h> // For HAVE_SYS_PRCTL_H
@@ -2120,75 +2252,57 @@
+
+std::unique_ptr<ThreadNameRegistry> g_thread_names(new ThreadNameRegistry());
+
-+namespace {
-+
-+/**
-+ * @return true if `to_check` is prefixed with `prefix`.
-+ */
-+bool HasPrefix(const std::string& to_check, const std::string& prefix) {
-+ return to_check.compare(0, prefix.size(), prefix) == 0;
-+}
-+
-+/**
-+ * Count the number of entries in a map with a given prefix.
-+ *
-+ * @return The number of occurrences of keys with the given prefix.
-+ */
-+template <class T>
-+int CountMapPrefixes(T map, const std::string& prefix) {
-+ auto it = map.lower_bound(prefix);
-+ int count = 0;
-+
-+ while (it != map.end() && HasPrefix(it->first, prefix)) {
-+ ++it; ++count;
-+ }
+
-+ return count;
++std::string ThreadNameRegistry::GetId()
++{
++#if !defined(HAVE_THREAD_LOCAL)
++ // Fall back to boost when we don't have thread_local available.
++ //
++ // See: https://github.com/bitcoin/bitcoin/pull/11722#pullrequestreview-79322658
++ //
++ std::stringstream temp; // stringstream use necessary to get thread_id into a string.
++ temp << boost::this_thread::get_id();
++ return temp.str();
++#else
++ // Otherwise, an ID is computed once and cached per thread.
++ //
++ static std::atomic<std::uint32_t> uid{0};
++ static thread_local std::string this_id = std::to_string(uid++);
++ return this_id;
++#endif
+}
+
-+} // namespace
-+
+bool ThreadNameRegistry::Rename(std::string name, bool expect_multiple)
+{
+ std::lock_guard<std::mutex> guard(m_map_lock);
++ std::string name_without_suffix = name;
+
+ if (expect_multiple) {
-+ name = tfm::format("%s.%d", name, CountMapPrefixes(m_name_to_id, name));
++ name = tfm::format("%s.%d", name_without_suffix, m_name_to_count[name_without_suffix]);
+ }
+
-+ std::string process_name(name);
-+
-+ /*
-+ * Uncomment if we want to retain the `bitcoin-` system thread name prefix.
-+ *
-+ const std::string process_prefix("bitcoin-");
-+ if (!HasPrefix(process_name, process_prefix)) {
-+ process_name = process_prefix + process_name;
-+ }
-+ */
-+
-+ RenameProcess(process_name.c_str());
++ std::string thread_id = GetId();
++ std::string process_name = name;
+
-+ auto thread_id = boost::this_thread::get_id();
-+ auto it_name = m_name_to_id.find(name);
++ auto it_name = m_name_to_count.find(name);
+ auto it_id = m_id_to_name.find(thread_id);
+
+ // Don't allow name collisions.
-+ if (it_name != m_name_to_id.end() && it_name->second != thread_id) {
-+ std::stringstream errmsg; // stringstream use necessary to get thread_id into a string.
-+ errmsg << "Thread name '" << name << "' already registered (id: " << it_name->second << ")\n";
-+ LogPrintStr(errmsg.str());
++ if (it_name != m_name_to_count.end()) {
++ LogPrintStr(tfm::format("Thread name '%s' already registered", name));
+ return false;
+ }
+ // Warn on reregistration.
+ else if (it_id != m_id_to_name.end()) {
-+ std::stringstream warnmsg;
-+ warnmsg << "Reregistering thread " << thread_id << " with name '%s'; previously was '%s'\n";
-+ LogPrintStr(tfm::format(warnmsg.str().c_str(), name, it_id->second));
++ LogPrintStr(tfm::format(
++ "Reregistering thread %s with name '%s'; previously was '%s'\n",
++ thread_id, name, it_id->second));
+ }
+
++ RenameProcess(process_name.c_str());
+ m_id_to_name[thread_id] = name;
-+ m_name_to_id[name] = thread_id;
++ m_name_to_count[name_without_suffix]++;
++
+ return true;
+}
+
@@ -2196,7 +2310,7 @@
+{
+ std::lock_guard<std::mutex> guard(m_map_lock);
+
-+ auto thread_id = boost::this_thread::get_id();
++ auto thread_id = GetId();
+ auto found = m_id_to_name.find(thread_id);
+
+ if (found != m_id_to_name.end()) {
@@ -2204,7 +2318,7 @@
+ }
+
+ std::string pname = GetProcessName();
-+ m_name_to_id[pname] = thread_id;
++ ++m_name_to_count[pname];
+ m_id_to_name[thread_id] = pname;
+ return pname;
+}
@@ -2242,10 +2356,10 @@
+}
diff --git a/src/threadnames.h b/src/threadnames.h
new file mode 100644
-index 0000000..4073490
+index 0000000..10b2ca2
--- /dev/null
+++ b/src/threadnames.h
-@@ -0,0 +1,55 @@
+@@ -0,0 +1,64 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -2254,10 +2368,9 @@
+#define BITCOIN_THREADNAMES_H
+
+#include <map>
++#include <memory>
+#include <mutex>
+
-+#include <boost/thread.hpp>
-+
+
+/**
+ * Keeps a map of thread IDs to string names and handles system-level thread naming.
@@ -2292,10 +2405,20 @@
+ */
+ std::string GetProcessName();
+
++ /**
++ * @return a unique identifier for the calling thread.
++ */
++ std::string GetId();
++
+private:
+ std::mutex m_map_lock;
-+ std::map<boost::thread::id, std::string> m_id_to_name;
-+ std::map<std::string, boost::thread::id> m_name_to_id;
++ std::map<std::string, std::string> m_id_to_name;
++ /**
++ * The number of times this name has been used to identify a thread;
++ * used to generate numeric suffix.
++ */
++ std::map<std::string, size_t> m_name_to_count;
++
+};
+
+extern std::unique_ptr<ThreadNameRegistry> g_thread_names;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment