threadname.h 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. /*
  2. * SRT - Secure, Reliable, Transport
  3. * Copyright (c) 2018 Haivision Systems Inc.
  4. *
  5. * This Source Code Form is subject to the terms of the Mozilla Public
  6. * License, v. 2.0. If a copy of the MPL was not distributed with this
  7. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  8. *
  9. */
  10. /*****************************************************************************
  11. written by
  12. Haivision Systems Inc.
  13. *****************************************************************************/
  14. #ifndef INC_SRT_THREADNAME_H
  15. #define INC_SRT_THREADNAME_H
  16. // NOTE:
  17. // HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H
  18. // HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H
  19. // HAVE_PTHREAD_GETNAME_NP
  20. // HAVE_PTHREAD_GETNAME_NP
  21. // Are detected and set in ../CMakeLists.txt.
  22. // OS Availability of pthread_getname_np(..) and pthread_setname_np(..)::
  23. // MacOS(10.6)
  24. // iOS(3.2)
  25. // AIX(7.1)
  26. // FreeBSD(version?), OpenBSD(Version?)
  27. // Linux-GLIBC(GLIBC-2.12).
  28. // Linux-MUSL(MUSL-1.1.20 Partial Implementation. See below).
  29. // MINGW-W64(4.0.6)
  30. #if defined(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) \
  31. || defined(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H)
  32. #include <pthread_np.h>
  33. #if defined(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) \
  34. && !defined(HAVE_PTHREAD_GETNAME_NP)
  35. #define HAVE_PTHREAD_GETNAME_NP 1
  36. #endif
  37. #if defined(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) \
  38. && !defined(HAVE_PTHREAD_SETNAME_NP)
  39. #define HAVE_PTHREAD_SETNAME_NP 1
  40. #endif
  41. #endif
  42. #if (defined(HAVE_PTHREAD_GETNAME_NP) && defined(HAVE_PTHREAD_GETNAME_NP)) \
  43. || defined(__linux__)
  44. // NOTE:
  45. // Linux pthread_getname_np() and pthread_setname_np() became available
  46. // in GLIBC-2.12 and later.
  47. // Some Linux runtimes do not have pthread_getname_np(), but have
  48. // pthread_setname_np(), for instance MUSL at least as of v1.1.20.
  49. // So using the prctl() for Linux is more portable.
  50. #if defined(__linux__)
  51. #include <sys/prctl.h>
  52. #endif
  53. #include <pthread.h>
  54. #endif
  55. #include <cstdio>
  56. #include <cstring>
  57. #include <string>
  58. #include "common.h"
  59. #include "sync.h"
  60. namespace srt {
  61. class ThreadName
  62. {
  63. #if (defined(HAVE_PTHREAD_GETNAME_NP) && defined(HAVE_PTHREAD_GETNAME_NP)) \
  64. || defined(__linux__)
  65. class ThreadNameImpl
  66. {
  67. public:
  68. static const size_t BUFSIZE = 64;
  69. static const bool DUMMY_IMPL = false;
  70. static bool get(char* namebuf)
  71. {
  72. #if defined(__linux__)
  73. // since Linux 2.6.11. The buffer should allow space for up to 16
  74. // bytes; the returned string will be null-terminated.
  75. return prctl(PR_GET_NAME, (unsigned long)namebuf, 0, 0) != -1;
  76. #elif defined(HAVE_PTHREAD_GETNAME_NP)
  77. return pthread_getname_np(pthread_self(), namebuf, BUFSIZE) == 0;
  78. #else
  79. #error "unsupported platform"
  80. #endif
  81. }
  82. static bool set(const char* name)
  83. {
  84. SRT_ASSERT(name != NULL);
  85. #if defined(__linux__)
  86. // The name can be up to 16 bytes long, including the terminating
  87. // null byte. (If the length of the string, including the terminating
  88. // null byte, exceeds 16 bytes, the string is silently truncated.)
  89. return prctl(PR_SET_NAME, (unsigned long)name, 0, 0) != -1;
  90. #elif defined(HAVE_PTHREAD_SETNAME_NP)
  91. #if defined(__APPLE__)
  92. return pthread_setname_np(name) == 0;
  93. #else
  94. return pthread_setname_np(pthread_self(), name) == 0;
  95. #endif
  96. #else
  97. #error "unsupported platform"
  98. #endif
  99. }
  100. explicit ThreadNameImpl(const std::string& name)
  101. : reset(false)
  102. {
  103. tid = pthread_self();
  104. if (!get(old_name))
  105. return;
  106. reset = set(name.c_str());
  107. if (reset)
  108. return;
  109. // Try with a shorter name. 15 is the upper limit supported by Linux,
  110. // other platforms should support a larger value. So 15 should works
  111. // on all platforms.
  112. const size_t max_len = 15;
  113. if (name.size() > max_len)
  114. reset = set(name.substr(0, max_len).c_str());
  115. }
  116. ~ThreadNameImpl()
  117. {
  118. if (!reset)
  119. return;
  120. // ensure it's called on the right thread
  121. if (tid == pthread_self())
  122. set(old_name);
  123. }
  124. private:
  125. ThreadNameImpl(ThreadNameImpl& other);
  126. ThreadNameImpl& operator=(const ThreadNameImpl& other);
  127. private:
  128. bool reset;
  129. pthread_t tid;
  130. char old_name[BUFSIZE];
  131. };
  132. #else
  133. class ThreadNameImpl
  134. {
  135. public:
  136. static const bool DUMMY_IMPL = true;
  137. static const size_t BUFSIZE = 64;
  138. static bool get(char* output)
  139. {
  140. // The default implementation will simply try to get the thread ID
  141. std::ostringstream bs;
  142. bs << "T" << sync::this_thread::get_id();
  143. size_t s = bs.str().copy(output, BUFSIZE - 1);
  144. output[s] = '\0';
  145. return true;
  146. }
  147. static bool set(const char*) { return false; }
  148. ThreadNameImpl(const std::string&) {}
  149. ~ThreadNameImpl() // just to make it "non-trivially-destructible" for compatibility with normal version
  150. {
  151. }
  152. };
  153. #endif // platform dependent impl
  154. // Why delegate to impl:
  155. // 1. to make sure implementation on different platforms have the same interface.
  156. // 2. it's simple to add some wrappers like get(const std::string &).
  157. ThreadNameImpl impl;
  158. public:
  159. static const bool DUMMY_IMPL = ThreadNameImpl::DUMMY_IMPL;
  160. static const size_t BUFSIZE = ThreadNameImpl::BUFSIZE;
  161. /// @brief Print thread ID to the provided buffer.
  162. /// The size of the destination buffer is assumed to be at least ThreadName::BUFSIZE.
  163. /// @param [out] output destination buffer to get thread name
  164. /// @return true on success, false on failure
  165. static bool get(char* output) {
  166. return ThreadNameImpl::get(output);
  167. }
  168. static bool get(std::string& name)
  169. {
  170. char buf[BUFSIZE];
  171. bool ret = get(buf);
  172. if (ret)
  173. name = buf;
  174. return ret;
  175. }
  176. static bool set(const std::string& name) { return ThreadNameImpl::set(name.c_str()); }
  177. explicit ThreadName(const std::string& name)
  178. : impl(name)
  179. {
  180. }
  181. private:
  182. ThreadName(const ThreadName&);
  183. ThreadName(const char*);
  184. ThreadName& operator=(const ThreadName& other);
  185. };
  186. } // namespace srt
  187. #endif