thread.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. #include "tls.h"
  2. #include "thread.h"
  3. #include "thread.i"
  4. #include <util/generic/ptr.h>
  5. #include <util/generic/ymath.h>
  6. #include <util/generic/ylimits.h>
  7. #include <util/generic/yexception.h>
  8. #include "yassert.h"
  9. #include <utility>
  10. #if defined(_glibc_)
  11. #if !__GLIBC_PREREQ(2, 30)
  12. #include <sys/syscall.h>
  13. #endif
  14. #endif
  15. #if defined(_unix_)
  16. #include <pthread.h>
  17. #include <sys/types.h>
  18. #elif defined(_win_)
  19. #include "dynlib.h"
  20. #include <util/charset/wide.h>
  21. #include <util/generic/scope.h>
  22. #else
  23. #error "FIXME"
  24. #endif
  25. bool SetHighestThreadPriority() {
  26. #ifdef _win_
  27. return SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
  28. #else
  29. struct sched_param sch;
  30. memset(&sch, 0, sizeof(sch));
  31. sch.sched_priority = 31;
  32. return pthread_setschedparam(pthread_self(), SCHED_RR, &sch) == 0;
  33. #endif
  34. }
  35. namespace {
  36. using TParams = TThread::TParams;
  37. using TId = TThread::TId;
  38. inline void SetThrName(const TParams& p) {
  39. try {
  40. if (p.Name) {
  41. TThread::SetCurrentThreadName(p.Name.data());
  42. }
  43. } catch (...) {
  44. // ¯\_(ツ)_/¯
  45. }
  46. }
  47. inline size_t StackSize(const TParams& p) noexcept {
  48. if (p.StackSize) {
  49. return FastClp2(p.StackSize);
  50. }
  51. return 0;
  52. }
  53. #if defined(_win_)
  54. class TWinThread {
  55. struct TMyParams: public TParams, public TThrRefBase {
  56. inline TMyParams(const TParams& p)
  57. : TParams(p)
  58. , Result(0)
  59. {
  60. }
  61. void* Result;
  62. };
  63. using TParamsRef = TIntrusivePtr<TMyParams>;
  64. public:
  65. inline TWinThread(const TParams& params)
  66. : P_(new TMyParams(params))
  67. , Handle(0)
  68. #if _WIN32_WINNT < 0x0502
  69. , ThreadId(0)
  70. #endif
  71. {
  72. }
  73. inline bool Running() const noexcept {
  74. return Handle != 0;
  75. }
  76. inline TId SystemThreadId() const noexcept {
  77. #if _WIN32_WINNT < 0x0502
  78. return (TId)ThreadId;
  79. #else
  80. return (TId)GetThreadId(Handle);
  81. #endif
  82. }
  83. inline void* Join() {
  84. ::WaitForSingleObject(Handle, INFINITE);
  85. ::CloseHandle(Handle);
  86. return P_->Result;
  87. }
  88. inline void Detach() {
  89. ::CloseHandle(Handle);
  90. }
  91. static ui32 __stdcall Proxy(void* ptr) {
  92. NTls::TCleaner cleaner;
  93. (void)cleaner;
  94. {
  95. TParamsRef p((TMyParams*)(ptr));
  96. //drop counter, gotten in Start()
  97. p->UnRef();
  98. SetThrName(*p);
  99. p->Result = p->Proc(p->Data);
  100. }
  101. return 0;
  102. }
  103. inline void Start() {
  104. //do not do this, kids, at home
  105. P_->Ref();
  106. #if _WIN32_WINNT < 0x0502
  107. Handle = reinterpret_cast<HANDLE>(::_beginthreadex(nullptr, (unsigned)StackSize(*P_), Proxy, (void*)P_.Get(), 0, &ThreadId));
  108. #else
  109. Handle = reinterpret_cast<HANDLE>(::_beginthreadex(nullptr, (unsigned)StackSize(*P_), Proxy, (void*)P_.Get(), 0, nullptr));
  110. #endif
  111. if (!Handle) {
  112. P_->UnRef();
  113. ythrow yexception() << "failed to create a thread";
  114. }
  115. }
  116. private:
  117. TParamsRef P_;
  118. HANDLE Handle;
  119. #if _WIN32_WINNT < 0x0502
  120. ui32 ThreadId;
  121. #endif
  122. };
  123. using TThreadBase = TWinThread;
  124. #else
  125. //unix
  126. #define PCHECK(x, y) \
  127. { \
  128. const int err_ = x; \
  129. if (err_) { \
  130. ythrow TSystemError(err_) << TStringBuf(y); \
  131. } \
  132. }
  133. class TPosixThread {
  134. public:
  135. inline TPosixThread(const TParams& params)
  136. : P_(new TParams(params))
  137. , H_()
  138. {
  139. static_assert(sizeof(H_) == sizeof(TId), "expect sizeof(H_) == sizeof(TId)");
  140. }
  141. inline TId SystemThreadId() const noexcept {
  142. return (TId)H_;
  143. }
  144. inline void* Join() {
  145. void* tec = nullptr;
  146. PCHECK(pthread_join(H_, &tec), "can not join thread");
  147. return tec;
  148. }
  149. inline void Detach() {
  150. PCHECK(pthread_detach(H_), "can not detach thread");
  151. }
  152. inline bool Running() const noexcept {
  153. return (bool)H_;
  154. }
  155. inline void Start() {
  156. pthread_attr_t* pattrs = nullptr;
  157. pthread_attr_t attrs;
  158. if (P_->StackSize > 0) {
  159. Zero(attrs);
  160. pthread_attr_init(&attrs);
  161. pattrs = &attrs;
  162. if (P_->StackPointer) {
  163. pthread_attr_setstack(pattrs, P_->StackPointer, P_->StackSize);
  164. } else {
  165. pthread_attr_setstacksize(pattrs, StackSize(*P_));
  166. }
  167. }
  168. {
  169. TParams* holdP = P_.Release();
  170. int err = pthread_create(&H_, pattrs, ThreadProxy, holdP);
  171. if (err) {
  172. H_ = {};
  173. P_.Reset(holdP);
  174. PCHECK(err, "failed to create thread");
  175. }
  176. }
  177. }
  178. private:
  179. static void* ThreadProxy(void* arg) {
  180. THolder<TParams> p((TParams*)arg);
  181. SetThrName(*p);
  182. return p->Proc(p->Data);
  183. }
  184. private:
  185. THolder<TParams> P_;
  186. pthread_t H_;
  187. };
  188. #undef PCHECK
  189. using TThreadBase = TPosixThread;
  190. #endif
  191. template <class T>
  192. static inline typename T::TValueType* Impl(T& t, const char* op, bool check = true) {
  193. if (!t) {
  194. ythrow yexception() << "can not " << op << " dead thread";
  195. }
  196. if (t->Running() != check) {
  197. static const char* const msg[] = {"running", "not running"};
  198. ythrow yexception() << "can not " << op << " " << msg[check] << " thread";
  199. }
  200. return t.Get();
  201. }
  202. }
  203. class TThread::TImpl: public TThreadBase {
  204. public:
  205. inline TImpl(const TParams& params, THolder<TCallableBase> callable = {})
  206. : TThreadBase(params)
  207. , Callable_(std::move(callable))
  208. {
  209. }
  210. inline TId Id() const noexcept {
  211. return ThreadIdHashFunction(SystemThreadId());
  212. }
  213. static THolder<TImpl> Create(THolder<TCallableBase> callable) {
  214. TParams params(TCallableBase::ThreadWorker, callable.Get());
  215. return MakeHolder<TImpl>(std::move(params), std::move(callable));
  216. }
  217. private:
  218. THolder<TCallableBase> Callable_;
  219. };
  220. TThread::TThread(const TParams& p)
  221. : Impl_(new TImpl(p))
  222. {
  223. }
  224. TThread::TThread(TThreadProc threadProc, void* param)
  225. : Impl_(new TImpl(TParams(threadProc, param)))
  226. {
  227. }
  228. TThread::TThread(TPrivateCtor, THolder<TCallableBase> callable)
  229. : Impl_(TImpl::Create(std::move(callable)))
  230. {
  231. }
  232. TThread::~TThread() {
  233. Join();
  234. }
  235. void TThread::Start() {
  236. Impl(Impl_, "start", false)->Start();
  237. }
  238. void* TThread::Join() {
  239. if (Running()) {
  240. void* ret = Impl_->Join();
  241. Impl_.Destroy();
  242. return ret;
  243. }
  244. return nullptr;
  245. }
  246. void TThread::Detach() {
  247. if (Running()) {
  248. Impl_->Detach();
  249. Impl_.Destroy();
  250. }
  251. }
  252. bool TThread::Running() const noexcept {
  253. return Impl_ && Impl_->Running();
  254. }
  255. TThread::TId TThread::Id() const noexcept {
  256. if (Running()) {
  257. return Impl_->Id();
  258. }
  259. return ImpossibleThreadId();
  260. }
  261. TThread::TId TThread::CurrentThreadId() noexcept {
  262. return SystemCurrentThreadId();
  263. }
  264. TThread::TId TThread::CurrentThreadNumericId() noexcept {
  265. #if defined(_win_)
  266. return GetCurrentThreadId();
  267. #elif defined(_darwin_)
  268. // There is no gettid() on MacOS and SYS_gettid returns completely unrelated numbers.
  269. // See: http://elliotth.blogspot.com/2012/04/gettid-on-mac-os.html
  270. uint64_t threadId;
  271. pthread_threadid_np(nullptr, &threadId);
  272. return threadId;
  273. #elif defined(_musl_) || defined(_bionic_)
  274. // both musl and android libc provide gettid() function
  275. return gettid();
  276. #elif defined(_glibc_)
  277. #if __GLIBC_PREREQ(2, 30)
  278. return gettid();
  279. #else
  280. // gettid() was introduced in glibc=2.30, previous versions lack neat syscall wrapper
  281. return syscall(SYS_gettid);
  282. #endif
  283. #else
  284. #error "Implement me"
  285. #endif
  286. }
  287. TThread::TId TThread::ImpossibleThreadId() noexcept {
  288. return Max<TThread::TId>();
  289. }
  290. namespace {
  291. template <class T>
  292. static void* ThreadProcWrapper(void* param) {
  293. return reinterpret_cast<T*>(param)->ThreadProc();
  294. }
  295. }
  296. ISimpleThread::ISimpleThread(size_t stackSize)
  297. : TThread(TParams(ThreadProcWrapper<ISimpleThread>, reinterpret_cast<void*>(this), stackSize))
  298. {
  299. }
  300. #if defined(_MSC_VER)
  301. // This beautiful piece of code is borrowed from
  302. // http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
  303. //
  304. // Usage: WindowsCurrentSetThreadName (-1, "MainThread");
  305. //
  306. #include <windows.h>
  307. #include <processthreadsapi.h>
  308. const DWORD MS_VC_EXCEPTION = 0x406D1388;
  309. #pragma pack(push, 8)
  310. typedef struct tagTHREADNAME_INFO {
  311. DWORD dwType; // Must be 0x1000.
  312. LPCSTR szName; // Pointer to name (in user addr space).
  313. DWORD dwThreadID; // Thread ID (-1=caller thread).
  314. DWORD dwFlags; // Reserved for future use, must be zero.
  315. } THREADNAME_INFO;
  316. #pragma pack(pop)
  317. static void WindowsCurrentSetThreadName(DWORD dwThreadID, const char* threadName) {
  318. THREADNAME_INFO info;
  319. info.dwType = 0x1000;
  320. info.szName = threadName;
  321. info.dwThreadID = dwThreadID;
  322. info.dwFlags = 0;
  323. __try {
  324. RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
  325. } __except (EXCEPTION_EXECUTE_HANDLER) {
  326. }
  327. }
  328. #endif
  329. #if defined(_win_)
  330. namespace {
  331. struct TWinThreadDescrAPI {
  332. TWinThreadDescrAPI()
  333. : Kernel32Dll("kernel32.dll")
  334. , SetThreadDescription((TSetThreadDescription)Kernel32Dll.SymOptional("SetThreadDescription"))
  335. , GetThreadDescription((TGetThreadDescription)Kernel32Dll.SymOptional("GetThreadDescription"))
  336. {
  337. }
  338. // This API is for Windows 10+ only:
  339. // https://msdn.microsoft.com/en-us/library/windows/desktop/mt774972(v=vs.85).aspx
  340. bool HasAPI() noexcept {
  341. return SetThreadDescription && GetThreadDescription;
  342. }
  343. // Should always succeed, unless something very strange is passed in `descr'
  344. void SetDescr(const char* descr) {
  345. auto hr = SetThreadDescription(GetCurrentThread(), (const WCHAR*)UTF8ToWide(descr).data());
  346. Y_VERIFY(SUCCEEDED(hr), "SetThreadDescription failed");
  347. }
  348. TString GetDescr() {
  349. PWSTR wideName;
  350. auto hr = GetThreadDescription(GetCurrentThread(), &wideName);
  351. Y_VERIFY(SUCCEEDED(hr), "GetThreadDescription failed");
  352. Y_DEFER {
  353. LocalFree(wideName);
  354. };
  355. return WideToUTF8((const wchar16*)wideName);
  356. }
  357. typedef HRESULT(__cdecl* TSetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription);
  358. typedef HRESULT(__cdecl* TGetThreadDescription)(HANDLE hThread, PWSTR* ppszThreadDescription);
  359. TDynamicLibrary Kernel32Dll;
  360. TSetThreadDescription SetThreadDescription;
  361. TGetThreadDescription GetThreadDescription;
  362. };
  363. }
  364. #endif // _win_
  365. void TThread::SetCurrentThreadName(const char* name) {
  366. (void)name;
  367. #if defined(_freebsd_)
  368. pthread_t thread = pthread_self();
  369. pthread_set_name_np(thread, name);
  370. #elif defined(_linux_)
  371. prctl(PR_SET_NAME, name, 0, 0, 0);
  372. #elif defined(_darwin_)
  373. pthread_setname_np(name);
  374. #elif defined(_win_)
  375. auto api = Singleton<TWinThreadDescrAPI>();
  376. if (api->HasAPI()) {
  377. api->SetDescr(name);
  378. } else {
  379. #if defined(_MSC_VER)
  380. WindowsCurrentSetThreadName(DWORD(-1), name);
  381. #endif
  382. }
  383. #else
  384. // no idea
  385. #endif // OS
  386. }
  387. TString TThread::CurrentThreadName() {
  388. #if defined(_freebsd_)
  389. // TODO: check pthread_get_name_np API availability
  390. #elif defined(_linux_)
  391. // > The buffer should allow space for up to 16 bytes; the returned string will be
  392. // > null-terminated.
  393. // via `man prctl`
  394. char name[16];
  395. memset(name, 0, sizeof(name));
  396. Y_VERIFY(prctl(PR_GET_NAME, name, 0, 0, 0) == 0, "pctl failed: %s", strerror(errno));
  397. return name;
  398. #elif defined(_darwin_)
  399. // available on Mac OS 10.6+
  400. const auto thread = pthread_self();
  401. char name[256];
  402. memset(name, 0, sizeof(name));
  403. Y_VERIFY(pthread_getname_np(thread, name, sizeof(name)) == 0, "pthread_getname_np failed: %s", strerror(errno));
  404. return name;
  405. #elif defined(_win_)
  406. auto api = Singleton<TWinThreadDescrAPI>();
  407. if (api->HasAPI()) {
  408. return api->GetDescr();
  409. }
  410. return {};
  411. #else
  412. // no idea
  413. #endif // OS
  414. return {};
  415. }
  416. bool TThread::CanGetCurrentThreadName() {
  417. #if defined(_linux_) || defined(_darwin_)
  418. return true;
  419. #elif defined(_win_)
  420. return Singleton<TWinThreadDescrAPI>()->HasAPI();
  421. #else
  422. return false;
  423. #endif // OS
  424. }
  425. TCurrentThreadLimits::TCurrentThreadLimits() noexcept
  426. : StackBegin(nullptr)
  427. , StackLength(0)
  428. {
  429. #if defined(_linux_) || defined(_cygwin_) || defined(_freebsd_)
  430. pthread_attr_t attr;
  431. pthread_attr_init(&attr);
  432. #if defined(_linux_) || defined(_cygwin_)
  433. Y_VERIFY(pthread_getattr_np(pthread_self(), &attr) == 0, "pthread_getattr failed");
  434. #else
  435. Y_VERIFY(pthread_attr_get_np(pthread_self(), &attr) == 0, "pthread_attr_get_np failed");
  436. #endif
  437. pthread_attr_getstack(&attr, (void**)&StackBegin, &StackLength);
  438. pthread_attr_destroy(&attr);
  439. #elif defined(_darwin_)
  440. StackBegin = pthread_get_stackaddr_np(pthread_self());
  441. StackLength = pthread_get_stacksize_np(pthread_self());
  442. #elif defined(_MSC_VER)
  443. #if _WIN32_WINNT >= _WIN32_WINNT_WIN8
  444. ULONG_PTR b = 0;
  445. ULONG_PTR e = 0;
  446. GetCurrentThreadStackLimits(&b, &e);
  447. StackBegin = (const void*)b;
  448. StackLength = e - b;
  449. #else
  450. // Copied from https://github.com/llvm-mirror/compiler-rt/blob/release_40/lib/sanitizer_common/sanitizer_win.cc#L91
  451. void* place_on_stack = alloca(16);
  452. MEMORY_BASIC_INFORMATION memory_info;
  453. Y_VERIFY(VirtualQuery(place_on_stack, &memory_info, sizeof(memory_info)));
  454. StackBegin = memory_info.AllocationBase;
  455. StackLength = static_cast<const char*>(memory_info.BaseAddress) + memory_info.RegionSize - static_cast<const char*>(StackBegin);
  456. #endif
  457. #else
  458. #error port me
  459. #endif
  460. }