xref: /llvm-project/libcxx/src/support/win32/thread_win32.cpp (revision d0438d2d087e78571a671c98cbb42308e4dcfcec)
1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include <__thread/support/windows.h>
10 #include <chrono>
11 
12 #define NOMINMAX
13 #define WIN32_LEAN_AND_MEAN
14 #include <windows.h>
15 #include <process.h>
16 #include <fibersapi.h>
17 
18 _LIBCPP_BEGIN_NAMESPACE_STD
19 
20 static_assert(sizeof(__libcpp_mutex_t) == sizeof(SRWLOCK), "");
21 static_assert(alignof(__libcpp_mutex_t) == alignof(SRWLOCK), "");
22 
23 static_assert(sizeof(__libcpp_recursive_mutex_t) == sizeof(CRITICAL_SECTION), "");
24 static_assert(alignof(__libcpp_recursive_mutex_t) == alignof(CRITICAL_SECTION), "");
25 
26 static_assert(sizeof(__libcpp_condvar_t) == sizeof(CONDITION_VARIABLE), "");
27 static_assert(alignof(__libcpp_condvar_t) == alignof(CONDITION_VARIABLE), "");
28 
29 static_assert(sizeof(__libcpp_exec_once_flag) == sizeof(INIT_ONCE), "");
30 static_assert(alignof(__libcpp_exec_once_flag) == alignof(INIT_ONCE), "");
31 
32 static_assert(sizeof(__libcpp_thread_id) == sizeof(DWORD), "");
33 static_assert(alignof(__libcpp_thread_id) == alignof(DWORD), "");
34 
35 static_assert(sizeof(__libcpp_thread_t) == sizeof(HANDLE), "");
36 static_assert(alignof(__libcpp_thread_t) == alignof(HANDLE), "");
37 
38 static_assert(sizeof(__libcpp_tls_key) == sizeof(DWORD), "");
39 static_assert(alignof(__libcpp_tls_key) == alignof(DWORD), "");
40 
41 // Mutex
42 int __libcpp_recursive_mutex_init(__libcpp_recursive_mutex_t* __m) {
43   InitializeCriticalSection((LPCRITICAL_SECTION)__m);
44   return 0;
45 }
46 
47 int __libcpp_recursive_mutex_lock(__libcpp_recursive_mutex_t* __m) {
48   EnterCriticalSection((LPCRITICAL_SECTION)__m);
49   return 0;
50 }
51 
52 bool __libcpp_recursive_mutex_trylock(__libcpp_recursive_mutex_t* __m) {
53   return TryEnterCriticalSection((LPCRITICAL_SECTION)__m) != 0;
54 }
55 
56 int __libcpp_recursive_mutex_unlock(__libcpp_recursive_mutex_t* __m) {
57   LeaveCriticalSection((LPCRITICAL_SECTION)__m);
58   return 0;
59 }
60 
61 int __libcpp_recursive_mutex_destroy(__libcpp_recursive_mutex_t* __m) {
62   DeleteCriticalSection((LPCRITICAL_SECTION)__m);
63   return 0;
64 }
65 
66 int __libcpp_mutex_lock(__libcpp_mutex_t* __m) {
67   AcquireSRWLockExclusive((PSRWLOCK)__m);
68   return 0;
69 }
70 
71 bool __libcpp_mutex_trylock(__libcpp_mutex_t* __m) { return TryAcquireSRWLockExclusive((PSRWLOCK)__m) != 0; }
72 
73 int __libcpp_mutex_unlock(__libcpp_mutex_t* __m) {
74   ReleaseSRWLockExclusive((PSRWLOCK)__m);
75   return 0;
76 }
77 
78 int __libcpp_mutex_destroy(__libcpp_mutex_t* __m) {
79   static_cast<void>(__m);
80   return 0;
81 }
82 
83 // Condition Variable
84 int __libcpp_condvar_signal(__libcpp_condvar_t* __cv) {
85   WakeConditionVariable((PCONDITION_VARIABLE)__cv);
86   return 0;
87 }
88 
89 int __libcpp_condvar_broadcast(__libcpp_condvar_t* __cv) {
90   WakeAllConditionVariable((PCONDITION_VARIABLE)__cv);
91   return 0;
92 }
93 
94 int __libcpp_condvar_wait(__libcpp_condvar_t* __cv, __libcpp_mutex_t* __m) {
95   SleepConditionVariableSRW((PCONDITION_VARIABLE)__cv, (PSRWLOCK)__m, INFINITE, 0);
96   return 0;
97 }
98 
99 int __libcpp_condvar_timedwait(__libcpp_condvar_t* __cv, __libcpp_mutex_t* __m, __libcpp_timespec_t* __ts) {
100   using namespace std::chrono;
101 
102   auto duration   = seconds(__ts->tv_sec) + nanoseconds(__ts->tv_nsec);
103   auto abstime    = system_clock::time_point(duration_cast<system_clock::duration>(duration));
104   auto timeout_ms = duration_cast<milliseconds>(abstime - system_clock::now());
105 
106   if (!SleepConditionVariableSRW(
107           (PCONDITION_VARIABLE)__cv, (PSRWLOCK)__m, timeout_ms.count() > 0 ? timeout_ms.count() : 0, 0)) {
108     auto __ec = GetLastError();
109     return __ec == ERROR_TIMEOUT ? ETIMEDOUT : __ec;
110   }
111   return 0;
112 }
113 
114 int __libcpp_condvar_destroy(__libcpp_condvar_t* __cv) {
115   static_cast<void>(__cv);
116   return 0;
117 }
118 
119 // Execute Once
120 static inline _LIBCPP_HIDE_FROM_ABI BOOL CALLBACK
121 __libcpp_init_once_execute_once_thunk(PINIT_ONCE __init_once, PVOID __parameter, PVOID* __context) {
122   static_cast<void>(__init_once);
123   static_cast<void>(__context);
124 
125   void (*init_routine)(void) = reinterpret_cast<void (*)(void)>(__parameter);
126   init_routine();
127   return TRUE;
128 }
129 
130 int __libcpp_execute_once(__libcpp_exec_once_flag* __flag, void (*__init_routine)(void)) {
131   if (!InitOnceExecuteOnce(
132           (PINIT_ONCE)__flag, __libcpp_init_once_execute_once_thunk, reinterpret_cast<void*>(__init_routine), nullptr))
133     return GetLastError();
134   return 0;
135 }
136 
137 // Thread ID
138 bool __libcpp_thread_id_equal(__libcpp_thread_id __lhs, __libcpp_thread_id __rhs) { return __lhs == __rhs; }
139 
140 bool __libcpp_thread_id_less(__libcpp_thread_id __lhs, __libcpp_thread_id __rhs) { return __lhs < __rhs; }
141 
142 // Thread
143 struct __libcpp_beginthreadex_thunk_data {
144   void* (*__func)(void*);
145   void* __arg;
146 };
147 
148 static inline _LIBCPP_HIDE_FROM_ABI unsigned WINAPI __libcpp_beginthreadex_thunk(void* __raw_data) {
149   auto* __data = static_cast<__libcpp_beginthreadex_thunk_data*>(__raw_data);
150   auto* __func = __data->__func;
151   void* __arg  = __data->__arg;
152   delete __data;
153   return static_cast<unsigned>(reinterpret_cast<uintptr_t>(__func(__arg)));
154 }
155 
156 bool __libcpp_thread_isnull(const __libcpp_thread_t* __t) { return *__t == 0; }
157 
158 int __libcpp_thread_create(__libcpp_thread_t* __t, void* (*__func)(void*), void* __arg) {
159   auto* __data   = new __libcpp_beginthreadex_thunk_data;
160   __data->__func = __func;
161   __data->__arg  = __arg;
162 
163   *__t = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, __libcpp_beginthreadex_thunk, __data, 0, nullptr));
164 
165   if (*__t)
166     return 0;
167   return GetLastError();
168 }
169 
170 __libcpp_thread_id __libcpp_thread_get_current_id() { return GetCurrentThreadId(); }
171 
172 __libcpp_thread_id __libcpp_thread_get_id(const __libcpp_thread_t* __t) { return GetThreadId(*__t); }
173 
174 int __libcpp_thread_join(__libcpp_thread_t* __t) {
175   if (WaitForSingleObjectEx(*__t, INFINITE, FALSE) == WAIT_FAILED)
176     return GetLastError();
177   if (!CloseHandle(*__t))
178     return GetLastError();
179   return 0;
180 }
181 
182 int __libcpp_thread_detach(__libcpp_thread_t* __t) {
183   if (!CloseHandle(*__t))
184     return GetLastError();
185   return 0;
186 }
187 
188 void __libcpp_thread_yield() { SwitchToThread(); }
189 
190 void __libcpp_thread_sleep_for(const chrono::nanoseconds& __ns) {
191   // round-up to the nearest millisecond
192   chrono::milliseconds __ms = chrono::ceil<chrono::milliseconds>(__ns);
193   // FIXME(compnerd) this should be an alertable sleep (WFSO or SleepEx)
194   Sleep(__ms.count());
195 }
196 
197 // Thread Local Storage
198 int __libcpp_tls_create(__libcpp_tls_key* __key, void(_LIBCPP_TLS_DESTRUCTOR_CC* __at_exit)(void*)) {
199   DWORD index = FlsAlloc(__at_exit);
200   if (index == FLS_OUT_OF_INDEXES)
201     return GetLastError();
202   *__key = index;
203   return 0;
204 }
205 
206 void* __libcpp_tls_get(__libcpp_tls_key __key) { return FlsGetValue(__key); }
207 
208 int __libcpp_tls_set(__libcpp_tls_key __key, void* __p) {
209   if (!FlsSetValue(__key, __p))
210     return GetLastError();
211   return 0;
212 }
213 
214 _LIBCPP_END_NAMESPACE_STD
215