Mercurial
comparison third_party/libuv/src/win/thread.c @ 160:948de3f54cea
[ThirdParty] Added libuv
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Wed, 14 Jan 2026 19:39:52 -0800 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 159:05cf9467a1c3 | 160:948de3f54cea |
|---|---|
| 1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. | |
| 2 * | |
| 3 * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 4 * of this software and associated documentation files (the "Software"), to | |
| 5 * deal in the Software without restriction, including without limitation the | |
| 6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |
| 7 * sell copies of the Software, and to permit persons to whom the Software is | |
| 8 * furnished to do so, subject to the following conditions: | |
| 9 * | |
| 10 * The above copyright notice and this permission notice shall be included in | |
| 11 * all copies or substantial portions of the Software. | |
| 12 * | |
| 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
| 18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
| 19 * IN THE SOFTWARE. | |
| 20 */ | |
| 21 | |
| 22 #include <assert.h> | |
| 23 #include <limits.h> | |
| 24 #include <stdlib.h> | |
| 25 | |
| 26 #if defined(__MINGW64_VERSION_MAJOR) | |
| 27 /* MemoryBarrier expands to __mm_mfence in some cases (x86+sse2), which may | |
| 28 * require this header in some versions of mingw64. */ | |
| 29 #include <intrin.h> | |
| 30 #endif | |
| 31 | |
| 32 #include "uv.h" | |
| 33 #include "internal.h" | |
| 34 | |
| 35 typedef void (*uv__once_cb)(void); | |
| 36 | |
| 37 typedef struct { | |
| 38 uv__once_cb callback; | |
| 39 } uv__once_data_t; | |
| 40 | |
| 41 static BOOL WINAPI uv__once_inner(INIT_ONCE *once, void* param, void** context) { | |
| 42 uv__once_data_t* data = param; | |
| 43 | |
| 44 data->callback(); | |
| 45 | |
| 46 return TRUE; | |
| 47 } | |
| 48 | |
| 49 void uv_once(uv_once_t* guard, uv__once_cb callback) { | |
| 50 uv__once_data_t data = { .callback = callback }; | |
| 51 InitOnceExecuteOnce(&guard->init_once, uv__once_inner, (void*) &data, NULL); | |
| 52 } | |
| 53 | |
| 54 | |
| 55 /* Verify that uv_thread_t can be stored in a TLS slot. */ | |
| 56 STATIC_ASSERT(sizeof(uv_thread_t) <= sizeof(void*)); | |
| 57 | |
| 58 static uv_key_t uv__current_thread_key; | |
| 59 static uv_once_t uv__current_thread_init_guard = UV_ONCE_INIT; | |
| 60 static uv_once_t uv__thread_name_once = UV_ONCE_INIT; | |
| 61 HRESULT (WINAPI *pGetThreadDescription)(HANDLE, PWSTR*); | |
| 62 HRESULT (WINAPI *pSetThreadDescription)(HANDLE, PCWSTR); | |
| 63 | |
| 64 | |
| 65 static void uv__init_current_thread_key(void) { | |
| 66 if (uv_key_create(&uv__current_thread_key)) | |
| 67 abort(); | |
| 68 } | |
| 69 | |
| 70 | |
| 71 struct thread_ctx { | |
| 72 void (*entry)(void* arg); | |
| 73 void* arg; | |
| 74 uv_thread_t self; | |
| 75 }; | |
| 76 | |
| 77 | |
| 78 static UINT __stdcall uv__thread_start(void* arg) { | |
| 79 struct thread_ctx *ctx_p; | |
| 80 struct thread_ctx ctx; | |
| 81 | |
| 82 ctx_p = arg; | |
| 83 ctx = *ctx_p; | |
| 84 uv__free(ctx_p); | |
| 85 | |
| 86 uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key); | |
| 87 uv_key_set(&uv__current_thread_key, ctx.self); | |
| 88 | |
| 89 ctx.entry(ctx.arg); | |
| 90 | |
| 91 return 0; | |
| 92 } | |
| 93 | |
| 94 | |
| 95 int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { | |
| 96 uv_thread_options_t params; | |
| 97 params.flags = UV_THREAD_NO_FLAGS; | |
| 98 return uv_thread_create_ex(tid, ¶ms, entry, arg); | |
| 99 } | |
| 100 | |
| 101 | |
| 102 int uv_thread_detach(uv_thread_t *tid) { | |
| 103 if (CloseHandle(*tid) == 0) | |
| 104 return uv_translate_sys_error(GetLastError()); | |
| 105 | |
| 106 return 0; | |
| 107 } | |
| 108 | |
| 109 | |
| 110 int uv_thread_create_ex(uv_thread_t* tid, | |
| 111 const uv_thread_options_t* params, | |
| 112 void (*entry)(void *arg), | |
| 113 void *arg) { | |
| 114 struct thread_ctx* ctx; | |
| 115 int err; | |
| 116 HANDLE thread; | |
| 117 SYSTEM_INFO sysinfo; | |
| 118 size_t stack_size; | |
| 119 size_t pagesize; | |
| 120 | |
| 121 stack_size = | |
| 122 params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0; | |
| 123 | |
| 124 if (stack_size != 0) { | |
| 125 GetNativeSystemInfo(&sysinfo); | |
| 126 pagesize = (size_t)sysinfo.dwPageSize; | |
| 127 /* Round up to the nearest page boundary. */ | |
| 128 stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1); | |
| 129 | |
| 130 if ((unsigned)stack_size != stack_size) | |
| 131 return UV_EINVAL; | |
| 132 } | |
| 133 | |
| 134 ctx = uv__malloc(sizeof(*ctx)); | |
| 135 if (ctx == NULL) | |
| 136 return UV_ENOMEM; | |
| 137 | |
| 138 ctx->entry = entry; | |
| 139 ctx->arg = arg; | |
| 140 | |
| 141 /* Create the thread in suspended state so we have a chance to pass | |
| 142 * its own creation handle to it */ | |
| 143 thread = (HANDLE) _beginthreadex(NULL, | |
| 144 (unsigned)stack_size, | |
| 145 uv__thread_start, | |
| 146 ctx, | |
| 147 CREATE_SUSPENDED, | |
| 148 NULL); | |
| 149 if (thread == NULL) { | |
| 150 err = errno; | |
| 151 uv__free(ctx); | |
| 152 } else { | |
| 153 err = 0; | |
| 154 *tid = thread; | |
| 155 ctx->self = thread; | |
| 156 ResumeThread(thread); | |
| 157 } | |
| 158 | |
| 159 switch (err) { | |
| 160 case 0: | |
| 161 return 0; | |
| 162 case EACCES: | |
| 163 return UV_EACCES; | |
| 164 case EAGAIN: | |
| 165 return UV_EAGAIN; | |
| 166 case EINVAL: | |
| 167 return UV_EINVAL; | |
| 168 } | |
| 169 | |
| 170 return UV_EIO; | |
| 171 } | |
| 172 | |
| 173 int uv_thread_setaffinity(uv_thread_t* tid, | |
| 174 char* cpumask, | |
| 175 char* oldmask, | |
| 176 size_t mask_size) { | |
| 177 int i; | |
| 178 HANDLE hproc; | |
| 179 DWORD_PTR procmask; | |
| 180 DWORD_PTR sysmask; | |
| 181 DWORD_PTR threadmask; | |
| 182 DWORD_PTR oldthreadmask; | |
| 183 int cpumasksize; | |
| 184 | |
| 185 cpumasksize = uv_cpumask_size(); | |
| 186 assert(cpumasksize > 0); | |
| 187 if (mask_size < (size_t)cpumasksize) | |
| 188 return UV_EINVAL; | |
| 189 | |
| 190 hproc = GetCurrentProcess(); | |
| 191 if (!GetProcessAffinityMask(hproc, &procmask, &sysmask)) | |
| 192 return uv_translate_sys_error(GetLastError()); | |
| 193 | |
| 194 threadmask = 0; | |
| 195 for (i = 0; i < cpumasksize; i++) { | |
| 196 if (cpumask[i]) { | |
| 197 if (procmask & (1 << i)) | |
| 198 threadmask |= 1 << i; | |
| 199 else | |
| 200 return UV_EINVAL; | |
| 201 } | |
| 202 } | |
| 203 | |
| 204 oldthreadmask = SetThreadAffinityMask(*tid, threadmask); | |
| 205 if (oldthreadmask == 0) | |
| 206 return uv_translate_sys_error(GetLastError()); | |
| 207 | |
| 208 if (oldmask != NULL) { | |
| 209 for (i = 0; i < cpumasksize; i++) | |
| 210 oldmask[i] = (oldthreadmask >> i) & 1; | |
| 211 } | |
| 212 | |
| 213 return 0; | |
| 214 } | |
| 215 | |
| 216 int uv_thread_getaffinity(uv_thread_t* tid, | |
| 217 char* cpumask, | |
| 218 size_t mask_size) { | |
| 219 int i; | |
| 220 HANDLE hproc; | |
| 221 DWORD_PTR procmask; | |
| 222 DWORD_PTR sysmask; | |
| 223 DWORD_PTR threadmask; | |
| 224 int cpumasksize; | |
| 225 | |
| 226 cpumasksize = uv_cpumask_size(); | |
| 227 assert(cpumasksize > 0); | |
| 228 if (mask_size < (size_t)cpumasksize) | |
| 229 return UV_EINVAL; | |
| 230 | |
| 231 hproc = GetCurrentProcess(); | |
| 232 if (!GetProcessAffinityMask(hproc, &procmask, &sysmask)) | |
| 233 return uv_translate_sys_error(GetLastError()); | |
| 234 | |
| 235 threadmask = SetThreadAffinityMask(*tid, procmask); | |
| 236 if (threadmask == 0 || SetThreadAffinityMask(*tid, threadmask) == 0) | |
| 237 return uv_translate_sys_error(GetLastError()); | |
| 238 | |
| 239 for (i = 0; i < cpumasksize; i++) | |
| 240 cpumask[i] = (threadmask >> i) & 1; | |
| 241 | |
| 242 return 0; | |
| 243 } | |
| 244 | |
| 245 int uv_thread_getcpu(void) { | |
| 246 return GetCurrentProcessorNumber(); | |
| 247 } | |
| 248 | |
| 249 uv_thread_t uv_thread_self(void) { | |
| 250 uv_thread_t key; | |
| 251 uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key); | |
| 252 key = uv_key_get(&uv__current_thread_key); | |
| 253 if (key == NULL) { | |
| 254 /* If the thread wasn't started by uv_thread_create (such as the main | |
| 255 * thread), we assign an id to it now. */ | |
| 256 if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), | |
| 257 GetCurrentProcess(), &key, 0, | |
| 258 FALSE, DUPLICATE_SAME_ACCESS)) { | |
| 259 uv_fatal_error(GetLastError(), "DuplicateHandle"); | |
| 260 } | |
| 261 uv_key_set(&uv__current_thread_key, key); | |
| 262 } | |
| 263 return key; | |
| 264 } | |
| 265 | |
| 266 | |
| 267 int uv_thread_join(uv_thread_t *tid) { | |
| 268 if (WaitForSingleObject(*tid, INFINITE)) | |
| 269 return uv_translate_sys_error(GetLastError()); | |
| 270 else { | |
| 271 CloseHandle(*tid); | |
| 272 *tid = 0; | |
| 273 MemoryBarrier(); /* For feature parity with pthread_join(). */ | |
| 274 return 0; | |
| 275 } | |
| 276 } | |
| 277 | |
| 278 | |
| 279 int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) { | |
| 280 return *t1 == *t2; | |
| 281 } | |
| 282 | |
| 283 | |
| 284 static void uv__thread_name_init_once(void) { | |
| 285 HMODULE m; | |
| 286 | |
| 287 m = GetModuleHandleA("api-ms-win-core-processthreads-l1-1-3.dll"); | |
| 288 if (m != NULL) { | |
| 289 pGetThreadDescription = (void*) GetProcAddress(m, "GetThreadDescription"); | |
| 290 pSetThreadDescription = (void*) GetProcAddress(m, "SetThreadDescription"); | |
| 291 } | |
| 292 } | |
| 293 | |
| 294 | |
| 295 int uv_thread_setname(const char* name) { | |
| 296 HRESULT hr; | |
| 297 WCHAR* namew; | |
| 298 int err; | |
| 299 char namebuf[UV_PTHREAD_MAX_NAMELEN_NP]; | |
| 300 | |
| 301 uv_once(&uv__thread_name_once, uv__thread_name_init_once); | |
| 302 | |
| 303 if (pSetThreadDescription == NULL) | |
| 304 return UV_ENOSYS; | |
| 305 | |
| 306 if (name == NULL) | |
| 307 return UV_EINVAL; | |
| 308 | |
| 309 strncpy(namebuf, name, sizeof(namebuf) - 1); | |
| 310 namebuf[sizeof(namebuf) - 1] = '\0'; | |
| 311 | |
| 312 namew = NULL; | |
| 313 err = uv__convert_utf8_to_utf16(namebuf, &namew); | |
| 314 if (err) | |
| 315 return err; | |
| 316 | |
| 317 hr = pSetThreadDescription(GetCurrentThread(), namew); | |
| 318 uv__free(namew); | |
| 319 if (FAILED(hr)) | |
| 320 return uv_translate_sys_error(HRESULT_CODE(hr)); | |
| 321 | |
| 322 return 0; | |
| 323 } | |
| 324 | |
| 325 | |
| 326 int uv_thread_getname(uv_thread_t* tid, char* name, size_t size) { | |
| 327 HRESULT hr; | |
| 328 WCHAR* namew; | |
| 329 char* thread_name; | |
| 330 size_t buf_size; | |
| 331 int r; | |
| 332 DWORD exit_code; | |
| 333 | |
| 334 uv_once(&uv__thread_name_once, uv__thread_name_init_once); | |
| 335 | |
| 336 if (pGetThreadDescription == NULL) | |
| 337 return UV_ENOSYS; | |
| 338 | |
| 339 if (name == NULL || size == 0) | |
| 340 return UV_EINVAL; | |
| 341 | |
| 342 if (tid == NULL || *tid == NULL) | |
| 343 return UV_EINVAL; | |
| 344 | |
| 345 /* Check if the thread handle is valid */ | |
| 346 if (!GetExitCodeThread(*tid, &exit_code) || exit_code != STILL_ACTIVE) | |
| 347 return UV_ENOENT; | |
| 348 | |
| 349 namew = NULL; | |
| 350 thread_name = NULL; | |
| 351 hr = pGetThreadDescription(*tid, &namew); | |
| 352 if (FAILED(hr)) | |
| 353 return uv_translate_sys_error(HRESULT_CODE(hr)); | |
| 354 | |
| 355 buf_size = size; | |
| 356 r = uv__copy_utf16_to_utf8(namew, -1, name, &buf_size); | |
| 357 if (r == UV_ENOBUFS) { | |
| 358 r = uv__convert_utf16_to_utf8(namew, wcslen(namew), &thread_name); | |
| 359 if (r == 0) { | |
| 360 uv__strscpy(name, thread_name, size); | |
| 361 uv__free(thread_name); | |
| 362 } | |
| 363 } | |
| 364 | |
| 365 LocalFree(namew); | |
| 366 return r; | |
| 367 } | |
| 368 | |
| 369 | |
| 370 int uv_mutex_init(uv_mutex_t* mutex) { | |
| 371 InitializeCriticalSection(mutex); | |
| 372 return 0; | |
| 373 } | |
| 374 | |
| 375 | |
| 376 int uv_mutex_init_recursive(uv_mutex_t* mutex) { | |
| 377 return uv_mutex_init(mutex); | |
| 378 } | |
| 379 | |
| 380 | |
| 381 void uv_mutex_destroy(uv_mutex_t* mutex) { | |
| 382 DeleteCriticalSection(mutex); | |
| 383 } | |
| 384 | |
| 385 | |
| 386 void uv_mutex_lock(uv_mutex_t* mutex) { | |
| 387 EnterCriticalSection(mutex); | |
| 388 } | |
| 389 | |
| 390 | |
| 391 int uv_mutex_trylock(uv_mutex_t* mutex) { | |
| 392 if (TryEnterCriticalSection(mutex)) | |
| 393 return 0; | |
| 394 else | |
| 395 return UV_EBUSY; | |
| 396 } | |
| 397 | |
| 398 | |
| 399 void uv_mutex_unlock(uv_mutex_t* mutex) { | |
| 400 LeaveCriticalSection(mutex); | |
| 401 } | |
| 402 | |
| 403 /* Ensure that the ABI for this type remains stable in v1.x */ | |
| 404 #ifdef _WIN64 | |
| 405 STATIC_ASSERT(sizeof(uv_rwlock_t) == 80); | |
| 406 #else | |
| 407 STATIC_ASSERT(sizeof(uv_rwlock_t) == 48); | |
| 408 #endif | |
| 409 | |
| 410 int uv_rwlock_init(uv_rwlock_t* rwlock) { | |
| 411 memset(rwlock, 0, sizeof(*rwlock)); | |
| 412 InitializeSRWLock(&rwlock->read_write_lock_); | |
| 413 | |
| 414 return 0; | |
| 415 } | |
| 416 | |
| 417 | |
| 418 void uv_rwlock_destroy(uv_rwlock_t* rwlock) { | |
| 419 /* SRWLock does not need explicit destruction so long as there are no waiting threads | |
| 420 See: https://docs.microsoft.com/windows/win32/api/synchapi/nf-synchapi-initializesrwlock#remarks */ | |
| 421 } | |
| 422 | |
| 423 | |
| 424 void uv_rwlock_rdlock(uv_rwlock_t* rwlock) { | |
| 425 AcquireSRWLockShared(&rwlock->read_write_lock_); | |
| 426 } | |
| 427 | |
| 428 | |
| 429 int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) { | |
| 430 if (!TryAcquireSRWLockShared(&rwlock->read_write_lock_)) | |
| 431 return UV_EBUSY; | |
| 432 | |
| 433 return 0; | |
| 434 } | |
| 435 | |
| 436 | |
| 437 void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) { | |
| 438 ReleaseSRWLockShared(&rwlock->read_write_lock_); | |
| 439 } | |
| 440 | |
| 441 | |
| 442 void uv_rwlock_wrlock(uv_rwlock_t* rwlock) { | |
| 443 AcquireSRWLockExclusive(&rwlock->read_write_lock_); | |
| 444 } | |
| 445 | |
| 446 | |
| 447 int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) { | |
| 448 if (!TryAcquireSRWLockExclusive(&rwlock->read_write_lock_)) | |
| 449 return UV_EBUSY; | |
| 450 | |
| 451 return 0; | |
| 452 } | |
| 453 | |
| 454 | |
| 455 void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) { | |
| 456 ReleaseSRWLockExclusive(&rwlock->read_write_lock_); | |
| 457 } | |
| 458 | |
| 459 | |
| 460 int uv_sem_init(uv_sem_t* sem, unsigned int value) { | |
| 461 *sem = CreateSemaphore(NULL, value, INT_MAX, NULL); | |
| 462 if (*sem == NULL) | |
| 463 return uv_translate_sys_error(GetLastError()); | |
| 464 else | |
| 465 return 0; | |
| 466 } | |
| 467 | |
| 468 | |
| 469 void uv_sem_destroy(uv_sem_t* sem) { | |
| 470 if (!CloseHandle(*sem)) | |
| 471 abort(); | |
| 472 } | |
| 473 | |
| 474 | |
| 475 void uv_sem_post(uv_sem_t* sem) { | |
| 476 if (!ReleaseSemaphore(*sem, 1, NULL)) | |
| 477 abort(); | |
| 478 } | |
| 479 | |
| 480 | |
| 481 void uv_sem_wait(uv_sem_t* sem) { | |
| 482 if (WaitForSingleObject(*sem, INFINITE) != WAIT_OBJECT_0) | |
| 483 abort(); | |
| 484 } | |
| 485 | |
| 486 | |
| 487 int uv_sem_trywait(uv_sem_t* sem) { | |
| 488 DWORD r = WaitForSingleObject(*sem, 0); | |
| 489 | |
| 490 if (r == WAIT_OBJECT_0) | |
| 491 return 0; | |
| 492 | |
| 493 if (r == WAIT_TIMEOUT) | |
| 494 return UV_EAGAIN; | |
| 495 | |
| 496 abort(); | |
| 497 return -1; /* Satisfy the compiler. */ | |
| 498 } | |
| 499 | |
| 500 | |
| 501 int uv_cond_init(uv_cond_t* cond) { | |
| 502 InitializeConditionVariable(&cond->cond_var); | |
| 503 return 0; | |
| 504 } | |
| 505 | |
| 506 | |
| 507 void uv_cond_destroy(uv_cond_t* cond) { | |
| 508 /* nothing to do */ | |
| 509 (void) &cond; | |
| 510 } | |
| 511 | |
| 512 | |
| 513 void uv_cond_signal(uv_cond_t* cond) { | |
| 514 WakeConditionVariable(&cond->cond_var); | |
| 515 } | |
| 516 | |
| 517 | |
| 518 void uv_cond_broadcast(uv_cond_t* cond) { | |
| 519 WakeAllConditionVariable(&cond->cond_var); | |
| 520 } | |
| 521 | |
| 522 | |
| 523 void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) { | |
| 524 if (!SleepConditionVariableCS(&cond->cond_var, mutex, INFINITE)) | |
| 525 abort(); | |
| 526 } | |
| 527 | |
| 528 | |
| 529 int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { | |
| 530 if (SleepConditionVariableCS(&cond->cond_var, mutex, (DWORD)(timeout / 1e6))) | |
| 531 return 0; | |
| 532 if (GetLastError() != ERROR_TIMEOUT) | |
| 533 abort(); | |
| 534 return UV_ETIMEDOUT; | |
| 535 } | |
| 536 | |
| 537 | |
| 538 int uv_key_create(uv_key_t* key) { | |
| 539 key->tls_index = TlsAlloc(); | |
| 540 if (key->tls_index == TLS_OUT_OF_INDEXES) | |
| 541 return UV_ENOMEM; | |
| 542 return 0; | |
| 543 } | |
| 544 | |
| 545 | |
| 546 void uv_key_delete(uv_key_t* key) { | |
| 547 if (TlsFree(key->tls_index) == FALSE) | |
| 548 abort(); | |
| 549 key->tls_index = TLS_OUT_OF_INDEXES; | |
| 550 } | |
| 551 | |
| 552 | |
| 553 void* uv_key_get(uv_key_t* key) { | |
| 554 void* value; | |
| 555 | |
| 556 value = TlsGetValue(key->tls_index); | |
| 557 if (value == NULL) | |
| 558 if (GetLastError() != ERROR_SUCCESS) | |
| 559 abort(); | |
| 560 | |
| 561 return value; | |
| 562 } | |
| 563 | |
| 564 | |
| 565 void uv_key_set(uv_key_t* key, void* value) { | |
| 566 if (TlsSetValue(key->tls_index, value) == FALSE) | |
| 567 abort(); | |
| 568 } |