1 /* 2 * Win32 implementation for mutex/cond/thread functions 3 * 4 * Copyright Red Hat, Inc. 2010 5 * 6 * Author: 7 * Paolo Bonzini <pbonzini@redhat.com> 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2 or later. 10 * See the COPYING file in the top-level directory. 11 * 12 */ 13 14 #include "qemu/osdep.h" 15 #include "qemu/thread.h" 16 #include "qemu/notify.h" 17 #include "qemu-thread-common.h" 18 #include <process.h> 19 20 static bool name_threads; 21 22 typedef HRESULT (WINAPI *pSetThreadDescription) (HANDLE hThread, 23 PCWSTR lpThreadDescription); 24 static pSetThreadDescription SetThreadDescriptionFunc; 25 static HMODULE kernel32_module; 26 27 static bool load_set_thread_description(void) 28 { 29 static gsize _init_once = 0; 30 31 if (g_once_init_enter(&_init_once)) { 32 kernel32_module = LoadLibrary("kernel32.dll"); 33 if (kernel32_module) { 34 SetThreadDescriptionFunc = 35 (pSetThreadDescription)GetProcAddress(kernel32_module, 36 "SetThreadDescription"); 37 if (!SetThreadDescriptionFunc) { 38 FreeLibrary(kernel32_module); 39 } 40 } 41 g_once_init_leave(&_init_once, 1); 42 } 43 44 return !!SetThreadDescriptionFunc; 45 } 46 47 void qemu_thread_naming(bool enable) 48 { 49 name_threads = enable; 50 51 if (enable && !load_set_thread_description()) { 52 fprintf(stderr, "qemu: thread naming not supported on this host\n"); 53 name_threads = false; 54 } 55 } 56 57 static void error_exit(int err, const char *msg) 58 { 59 char *pstr; 60 61 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, 62 NULL, err, 0, (LPTSTR)&pstr, 2, NULL); 63 fprintf(stderr, "qemu: %s: %s\n", msg, pstr); 64 LocalFree(pstr); 65 abort(); 66 } 67 68 void qemu_mutex_init(QemuMutex *mutex) 69 { 70 InitializeSRWLock(&mutex->lock); 71 qemu_mutex_post_init(mutex); 72 } 73 74 void qemu_mutex_destroy(QemuMutex *mutex) 75 { 76 assert(mutex->initialized); 77 mutex->initialized = false; 78 InitializeSRWLock(&mutex->lock); 79 } 80 81 void qemu_mutex_lock_impl(QemuMutex *mutex, const char *file, const int line) 82 { 83 assert(mutex->initialized); 84 qemu_mutex_pre_lock(mutex, file, line); 85 AcquireSRWLockExclusive(&mutex->lock); 86 qemu_mutex_post_lock(mutex, file, line); 87 } 88 89 int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line) 90 { 91 int owned; 92 93 assert(mutex->initialized); 94 owned = TryAcquireSRWLockExclusive(&mutex->lock); 95 if (owned) { 96 qemu_mutex_post_lock(mutex, file, line); 97 return 0; 98 } 99 return -EBUSY; 100 } 101 102 void qemu_mutex_unlock_impl(QemuMutex *mutex, const char *file, const int line) 103 { 104 assert(mutex->initialized); 105 qemu_mutex_pre_unlock(mutex, file, line); 106 ReleaseSRWLockExclusive(&mutex->lock); 107 } 108 109 void qemu_rec_mutex_init(QemuRecMutex *mutex) 110 { 111 InitializeCriticalSection(&mutex->lock); 112 mutex->initialized = true; 113 } 114 115 void qemu_rec_mutex_destroy(QemuRecMutex *mutex) 116 { 117 assert(mutex->initialized); 118 mutex->initialized = false; 119 DeleteCriticalSection(&mutex->lock); 120 } 121 122 void qemu_rec_mutex_lock_impl(QemuRecMutex *mutex, const char *file, int line) 123 { 124 assert(mutex->initialized); 125 EnterCriticalSection(&mutex->lock); 126 } 127 128 int qemu_rec_mutex_trylock_impl(QemuRecMutex *mutex, const char *file, int line) 129 { 130 assert(mutex->initialized); 131 return !TryEnterCriticalSection(&mutex->lock); 132 } 133 134 void qemu_rec_mutex_unlock_impl(QemuRecMutex *mutex, const char *file, int line) 135 { 136 assert(mutex->initialized); 137 LeaveCriticalSection(&mutex->lock); 138 } 139 140 void qemu_cond_init(QemuCond *cond) 141 { 142 memset(cond, 0, sizeof(*cond)); 143 InitializeConditionVariable(&cond->var); 144 cond->initialized = true; 145 } 146 147 void qemu_cond_destroy(QemuCond *cond) 148 { 149 assert(cond->initialized); 150 cond->initialized = false; 151 InitializeConditionVariable(&cond->var); 152 } 153 154 void qemu_cond_signal(QemuCond *cond) 155 { 156 assert(cond->initialized); 157 WakeConditionVariable(&cond->var); 158 } 159 160 void qemu_cond_broadcast(QemuCond *cond) 161 { 162 assert(cond->initialized); 163 WakeAllConditionVariable(&cond->var); 164 } 165 166 void qemu_cond_wait_impl(QemuCond *cond, QemuMutex *mutex, const char *file, const int line) 167 { 168 assert(cond->initialized); 169 qemu_mutex_pre_unlock(mutex, file, line); 170 SleepConditionVariableSRW(&cond->var, &mutex->lock, INFINITE, 0); 171 qemu_mutex_post_lock(mutex, file, line); 172 } 173 174 bool qemu_cond_timedwait_impl(QemuCond *cond, QemuMutex *mutex, int ms, 175 const char *file, const int line) 176 { 177 int rc = 0; 178 179 assert(cond->initialized); 180 trace_qemu_mutex_unlock(mutex, file, line); 181 if (!SleepConditionVariableSRW(&cond->var, &mutex->lock, ms, 0)) { 182 rc = GetLastError(); 183 } 184 trace_qemu_mutex_locked(mutex, file, line); 185 if (rc && rc != ERROR_TIMEOUT) { 186 error_exit(rc, __func__); 187 } 188 return rc != ERROR_TIMEOUT; 189 } 190 191 void qemu_sem_init(QemuSemaphore *sem, int init) 192 { 193 /* Manual reset. */ 194 sem->sema = CreateSemaphore(NULL, init, LONG_MAX, NULL); 195 sem->initialized = true; 196 } 197 198 void qemu_sem_destroy(QemuSemaphore *sem) 199 { 200 assert(sem->initialized); 201 sem->initialized = false; 202 CloseHandle(sem->sema); 203 } 204 205 void qemu_sem_post(QemuSemaphore *sem) 206 { 207 assert(sem->initialized); 208 ReleaseSemaphore(sem->sema, 1, NULL); 209 } 210 211 int qemu_sem_timedwait(QemuSemaphore *sem, int ms) 212 { 213 int rc; 214 215 assert(sem->initialized); 216 rc = WaitForSingleObject(sem->sema, ms); 217 if (rc == WAIT_OBJECT_0) { 218 return 0; 219 } 220 if (rc != WAIT_TIMEOUT) { 221 error_exit(GetLastError(), __func__); 222 } 223 return -1; 224 } 225 226 void qemu_sem_wait(QemuSemaphore *sem) 227 { 228 assert(sem->initialized); 229 if (WaitForSingleObject(sem->sema, INFINITE) != WAIT_OBJECT_0) { 230 error_exit(GetLastError(), __func__); 231 } 232 } 233 234 struct QemuThreadData { 235 /* Passed to win32_start_routine. */ 236 void *(*start_routine)(void *); 237 void *arg; 238 short mode; 239 NotifierList exit; 240 241 /* Only used for joinable threads. */ 242 bool exited; 243 void *ret; 244 CRITICAL_SECTION cs; 245 }; 246 247 static bool atexit_registered; 248 static NotifierList main_thread_exit; 249 250 static __thread QemuThreadData *qemu_thread_data; 251 252 static void run_main_thread_exit(void) 253 { 254 notifier_list_notify(&main_thread_exit, NULL); 255 } 256 257 void qemu_thread_atexit_add(Notifier *notifier) 258 { 259 if (!qemu_thread_data) { 260 if (!atexit_registered) { 261 atexit_registered = true; 262 atexit(run_main_thread_exit); 263 } 264 notifier_list_add(&main_thread_exit, notifier); 265 } else { 266 notifier_list_add(&qemu_thread_data->exit, notifier); 267 } 268 } 269 270 void qemu_thread_atexit_remove(Notifier *notifier) 271 { 272 notifier_remove(notifier); 273 } 274 275 static unsigned __stdcall win32_start_routine(void *arg) 276 { 277 QemuThreadData *data = (QemuThreadData *) arg; 278 void *(*start_routine)(void *) = data->start_routine; 279 void *thread_arg = data->arg; 280 281 qemu_thread_data = data; 282 qemu_thread_exit(start_routine(thread_arg)); 283 abort(); 284 } 285 286 void qemu_thread_exit(void *arg) 287 { 288 QemuThreadData *data = qemu_thread_data; 289 290 notifier_list_notify(&data->exit, NULL); 291 if (data->mode == QEMU_THREAD_JOINABLE) { 292 data->ret = arg; 293 EnterCriticalSection(&data->cs); 294 data->exited = true; 295 LeaveCriticalSection(&data->cs); 296 } else { 297 g_free(data); 298 } 299 _endthreadex(0); 300 } 301 302 void *qemu_thread_join(QemuThread *thread) 303 { 304 QemuThreadData *data; 305 void *ret; 306 HANDLE handle; 307 308 data = thread->data; 309 if (data->mode == QEMU_THREAD_DETACHED) { 310 return NULL; 311 } 312 313 /* 314 * Because multiple copies of the QemuThread can exist via 315 * qemu_thread_get_self, we need to store a value that cannot 316 * leak there. The simplest, non racy way is to store the TID, 317 * discard the handle that _beginthreadex gives back, and 318 * get another copy of the handle here. 319 */ 320 handle = qemu_thread_get_handle(thread); 321 if (handle) { 322 WaitForSingleObject(handle, INFINITE); 323 CloseHandle(handle); 324 } 325 ret = data->ret; 326 DeleteCriticalSection(&data->cs); 327 g_free(data); 328 return ret; 329 } 330 331 static bool set_thread_description(HANDLE h, const char *name) 332 { 333 HRESULT hr; 334 g_autofree wchar_t *namew = NULL; 335 336 if (!load_set_thread_description()) { 337 return false; 338 } 339 340 namew = g_utf8_to_utf16(name, -1, NULL, NULL, NULL); 341 if (!namew) { 342 return false; 343 } 344 345 hr = SetThreadDescriptionFunc(h, namew); 346 347 return SUCCEEDED(hr); 348 } 349 350 void qemu_thread_create(QemuThread *thread, const char *name, 351 void *(*start_routine)(void *), 352 void *arg, int mode) 353 { 354 HANDLE hThread; 355 struct QemuThreadData *data; 356 357 data = g_malloc(sizeof *data); 358 data->start_routine = start_routine; 359 data->arg = arg; 360 data->mode = mode; 361 data->exited = false; 362 notifier_list_init(&data->exit); 363 364 if (data->mode != QEMU_THREAD_DETACHED) { 365 InitializeCriticalSection(&data->cs); 366 } 367 368 hThread = (HANDLE) _beginthreadex(NULL, 0, win32_start_routine, 369 data, 0, &thread->tid); 370 if (!hThread) { 371 error_exit(GetLastError(), __func__); 372 } 373 if (name_threads && name && !set_thread_description(hThread, name)) { 374 fprintf(stderr, "qemu: failed to set thread description: %s\n", name); 375 } 376 CloseHandle(hThread); 377 378 thread->data = data; 379 } 380 381 int qemu_thread_set_affinity(QemuThread *thread, unsigned long *host_cpus, 382 unsigned long nbits) 383 { 384 return -ENOSYS; 385 } 386 387 int qemu_thread_get_affinity(QemuThread *thread, unsigned long **host_cpus, 388 unsigned long *nbits) 389 { 390 return -ENOSYS; 391 } 392 393 void qemu_thread_get_self(QemuThread *thread) 394 { 395 thread->data = qemu_thread_data; 396 thread->tid = GetCurrentThreadId(); 397 } 398 399 HANDLE qemu_thread_get_handle(QemuThread *thread) 400 { 401 QemuThreadData *data; 402 HANDLE handle; 403 404 data = thread->data; 405 if (data->mode == QEMU_THREAD_DETACHED) { 406 return NULL; 407 } 408 409 EnterCriticalSection(&data->cs); 410 if (!data->exited) { 411 handle = OpenThread(SYNCHRONIZE | THREAD_SUSPEND_RESUME | 412 THREAD_SET_CONTEXT, FALSE, thread->tid); 413 } else { 414 handle = NULL; 415 } 416 LeaveCriticalSection(&data->cs); 417 return handle; 418 } 419 420 bool qemu_thread_is_self(QemuThread *thread) 421 { 422 return GetCurrentThreadId() == thread->tid; 423 } 424