thread.cpp 14 KB

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