/* * SRT - Secure, Reliable, Transport * Copyright (c) 2018 Haivision Systems Inc. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * */ /***************************************************************************** written by Haivision Systems Inc. *****************************************************************************/ #ifndef INC_SRT_THREADNAME_H #define INC_SRT_THREADNAME_H // NOTE: // HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H // HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H // HAVE_PTHREAD_GETNAME_NP // HAVE_PTHREAD_GETNAME_NP // Are detected and set in ../CMakeLists.txt. // OS Availability of pthread_getname_np(..) and pthread_setname_np(..):: // MacOS(10.6) // iOS(3.2) // AIX(7.1) // FreeBSD(version?), OpenBSD(Version?) // Linux-GLIBC(GLIBC-2.12). // Linux-MUSL(MUSL-1.1.20 Partial Implementation. See below). // MINGW-W64(4.0.6) #if defined(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) \ || defined(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) #include #if defined(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) \ && !defined(HAVE_PTHREAD_GETNAME_NP) #define HAVE_PTHREAD_GETNAME_NP 1 #endif #if defined(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) \ && !defined(HAVE_PTHREAD_SETNAME_NP) #define HAVE_PTHREAD_SETNAME_NP 1 #endif #endif #if (defined(HAVE_PTHREAD_GETNAME_NP) && defined(HAVE_PTHREAD_GETNAME_NP)) \ || defined(__linux__) // NOTE: // Linux pthread_getname_np() and pthread_setname_np() became available // in GLIBC-2.12 and later. // Some Linux runtimes do not have pthread_getname_np(), but have // pthread_setname_np(), for instance MUSL at least as of v1.1.20. // So using the prctl() for Linux is more portable. #if defined(__linux__) #include #endif #include #endif #include #include #include #include "common.h" #include "sync.h" namespace srt { class ThreadName { #if (defined(HAVE_PTHREAD_GETNAME_NP) && defined(HAVE_PTHREAD_GETNAME_NP)) \ || defined(__linux__) class ThreadNameImpl { public: static const size_t BUFSIZE = 64; static const bool DUMMY_IMPL = false; static bool get(char* namebuf) { #if defined(__linux__) // since Linux 2.6.11. The buffer should allow space for up to 16 // bytes; the returned string will be null-terminated. return prctl(PR_GET_NAME, (unsigned long)namebuf, 0, 0) != -1; #elif defined(HAVE_PTHREAD_GETNAME_NP) return pthread_getname_np(pthread_self(), namebuf, BUFSIZE) == 0; #else #error "unsupported platform" #endif } static bool set(const char* name) { SRT_ASSERT(name != NULL); #if defined(__linux__) // The name can be up to 16 bytes long, including the terminating // null byte. (If the length of the string, including the terminating // null byte, exceeds 16 bytes, the string is silently truncated.) return prctl(PR_SET_NAME, (unsigned long)name, 0, 0) != -1; #elif defined(HAVE_PTHREAD_SETNAME_NP) #if defined(__APPLE__) return pthread_setname_np(name) == 0; #else return pthread_setname_np(pthread_self(), name) == 0; #endif #else #error "unsupported platform" #endif } explicit ThreadNameImpl(const std::string& name) : reset(false) { tid = pthread_self(); if (!get(old_name)) return; reset = set(name.c_str()); if (reset) return; // Try with a shorter name. 15 is the upper limit supported by Linux, // other platforms should support a larger value. So 15 should works // on all platforms. const size_t max_len = 15; if (name.size() > max_len) reset = set(name.substr(0, max_len).c_str()); } ~ThreadNameImpl() { if (!reset) return; // ensure it's called on the right thread if (tid == pthread_self()) set(old_name); } private: ThreadNameImpl(ThreadNameImpl& other); ThreadNameImpl& operator=(const ThreadNameImpl& other); private: bool reset; pthread_t tid; char old_name[BUFSIZE]; }; #else class ThreadNameImpl { public: static const bool DUMMY_IMPL = true; static const size_t BUFSIZE = 64; static bool get(char* output) { // The default implementation will simply try to get the thread ID std::ostringstream bs; bs << "T" << sync::this_thread::get_id(); size_t s = bs.str().copy(output, BUFSIZE - 1); output[s] = '\0'; return true; } static bool set(const char*) { return false; } ThreadNameImpl(const std::string&) {} ~ThreadNameImpl() // just to make it "non-trivially-destructible" for compatibility with normal version { } }; #endif // platform dependent impl // Why delegate to impl: // 1. to make sure implementation on different platforms have the same interface. // 2. it's simple to add some wrappers like get(const std::string &). ThreadNameImpl impl; public: static const bool DUMMY_IMPL = ThreadNameImpl::DUMMY_IMPL; static const size_t BUFSIZE = ThreadNameImpl::BUFSIZE; /// @brief Print thread ID to the provided buffer. /// The size of the destination buffer is assumed to be at least ThreadName::BUFSIZE. /// @param [out] output destination buffer to get thread name /// @return true on success, false on failure static bool get(char* output) { return ThreadNameImpl::get(output); } static bool get(std::string& name) { char buf[BUFSIZE]; bool ret = get(buf); if (ret) name = buf; return ret; } static bool set(const std::string& name) { return ThreadNameImpl::set(name.c_str()); } explicit ThreadName(const std::string& name) : impl(name) { } private: ThreadName(const ThreadName&); ThreadName(const char*); ThreadName& operator=(const ThreadName& other); }; } // namespace srt #endif