thread.cpp 15 KB

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