| 1 | /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
| 2 | // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4: |
| 3 | #ident "$Id$" |
| 4 | /*====== |
| 5 | This file is part of PerconaFT. |
| 6 | |
| 7 | |
| 8 | Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved. |
| 9 | |
| 10 | PerconaFT is free software: you can redistribute it and/or modify |
| 11 | it under the terms of the GNU General Public License, version 2, |
| 12 | as published by the Free Software Foundation. |
| 13 | |
| 14 | PerconaFT is distributed in the hope that it will be useful, |
| 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 17 | GNU General Public License for more details. |
| 18 | |
| 19 | You should have received a copy of the GNU General Public License |
| 20 | along with PerconaFT. If not, see <http://www.gnu.org/licenses/>. |
| 21 | |
| 22 | ---------------------------------------- |
| 23 | |
| 24 | PerconaFT is free software: you can redistribute it and/or modify |
| 25 | it under the terms of the GNU Affero General Public License, version 3, |
| 26 | as published by the Free Software Foundation. |
| 27 | |
| 28 | PerconaFT is distributed in the hope that it will be useful, |
| 29 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 30 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 31 | GNU Affero General Public License for more details. |
| 32 | |
| 33 | You should have received a copy of the GNU Affero General Public License |
| 34 | along with PerconaFT. If not, see <http://www.gnu.org/licenses/>. |
| 35 | ======= */ |
| 36 | |
| 37 | #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved." |
| 38 | |
| 39 | #pragma once |
| 40 | |
| 41 | #include <pthread.h> |
| 42 | #include <time.h> |
| 43 | #include <stdint.h> |
| 44 | |
| 45 | #include "toku_portability.h" |
| 46 | #include "toku_assert.h" |
| 47 | |
| 48 | // TODO: some things moved toku_instrumentation.h, not necessarily the best |
| 49 | // place |
| 50 | typedef pthread_attr_t toku_pthread_attr_t; |
| 51 | typedef pthread_t toku_pthread_t; |
| 52 | typedef pthread_mutex_t toku_pthread_mutex_t; |
| 53 | typedef pthread_condattr_t toku_pthread_condattr_t; |
| 54 | typedef pthread_cond_t toku_pthread_cond_t; |
| 55 | typedef pthread_rwlockattr_t toku_pthread_rwlockattr_t; |
| 56 | typedef pthread_key_t toku_pthread_key_t; |
| 57 | typedef struct timespec toku_timespec_t; |
| 58 | |
| 59 | // TODO: break this include loop |
| 60 | #include <pthread.h> |
| 61 | typedef pthread_mutexattr_t toku_pthread_mutexattr_t; |
| 62 | |
| 63 | struct toku_mutex_t { |
| 64 | pthread_mutex_t pmutex; |
| 65 | struct PSI_mutex |
| 66 | *psi_mutex; /* The performance schema instrumentation hook */ |
| 67 | #if TOKU_PTHREAD_DEBUG |
| 68 | pthread_t owner; // = pthread_self(); // for debugging |
| 69 | bool locked; |
| 70 | bool valid; |
| 71 | pfs_key_t instr_key_id; |
| 72 | #endif |
| 73 | }; |
| 74 | |
| 75 | struct toku_cond_t { |
| 76 | pthread_cond_t pcond; |
| 77 | struct PSI_cond *psi_cond; |
| 78 | #if TOKU_PTHREAD_DEBUG |
| 79 | pfs_key_t instr_key_id; |
| 80 | #endif |
| 81 | }; |
| 82 | |
| 83 | #ifdef TOKU_PTHREAD_DEBUG |
| 84 | #define TOKU_COND_INITIALIZER \ |
| 85 | { \ |
| 86 | .pcond = PTHREAD_COND_INITIALIZER, .psi_cond = nullptr, \ |
| 87 | .instr_key_id = 0 \ |
| 88 | } |
| 89 | #else |
| 90 | #define TOKU_COND_INITIALIZER \ |
| 91 | { .pcond = PTHREAD_COND_INITIALIZER, .psi_cond = nullptr } |
| 92 | #endif |
| 93 | |
| 94 | struct toku_pthread_rwlock_t { |
| 95 | pthread_rwlock_t rwlock; |
| 96 | struct PSI_rwlock *psi_rwlock; |
| 97 | #if TOKU_PTHREAD_DEBUG |
| 98 | pfs_key_t instr_key_id; |
| 99 | #endif |
| 100 | }; |
| 101 | |
| 102 | typedef struct toku_mutex_aligned { |
| 103 | toku_mutex_t aligned_mutex __attribute__((__aligned__(64))); |
| 104 | } toku_mutex_aligned_t; |
| 105 | |
| 106 | // Initializing with {} will fill in a struct with all zeros. |
| 107 | // But you may also need a pragma to suppress the warnings, as follows |
| 108 | // |
| 109 | // #pragma GCC diagnostic push |
| 110 | // #pragma GCC diagnostic ignored "-Wmissing-field-initializers" |
| 111 | // toku_mutex_t foo = ZERO_MUTEX_INITIALIZER; |
| 112 | // #pragma GCC diagnostic pop |
| 113 | // |
| 114 | // In general it will be a lot of busy work to make this codebase compile |
| 115 | // cleanly with -Wmissing-field-initializers |
| 116 | |
| 117 | #define ZERO_MUTEX_INITIALIZER \ |
| 118 | {} |
| 119 | |
| 120 | #if TOKU_PTHREAD_DEBUG |
| 121 | #define TOKU_MUTEX_INITIALIZER \ |
| 122 | { \ |
| 123 | .pmutex = PTHREAD_MUTEX_INITIALIZER, .psi_mutex = nullptr, .owner = 0, \ |
| 124 | .locked = false, .valid = true, .instr_key_id = 0 \ |
| 125 | } |
| 126 | #else |
| 127 | #define TOKU_MUTEX_INITIALIZER \ |
| 128 | { .pmutex = PTHREAD_MUTEX_INITIALIZER, .psi_mutex = nullptr } |
| 129 | #endif |
| 130 | |
| 131 | // Darwin doesn't provide adaptive mutexes |
| 132 | #if defined(__APPLE__) |
| 133 | #define TOKU_MUTEX_ADAPTIVE PTHREAD_MUTEX_DEFAULT |
| 134 | #if TOKU_PTHREAD_DEBUG |
| 135 | #define TOKU_ADAPTIVE_MUTEX_INITIALIZER \ |
| 136 | { \ |
| 137 | .pmutex = PTHREAD_MUTEX_INITIALIZER, .psi_mutex = nullptr, .owner = 0, \ |
| 138 | .locked = false, .valid = true, .instr_key_id = 0 \ |
| 139 | } |
| 140 | #else |
| 141 | #define TOKU_ADAPTIVE_MUTEX_INITIALIZER \ |
| 142 | { .pmutex = PTHREAD_MUTEX_INITIALIZER, .psi_mutex = nullptr } |
| 143 | #endif |
| 144 | #else // __FreeBSD__, __linux__, at least |
| 145 | #define TOKU_MUTEX_ADAPTIVE PTHREAD_MUTEX_ADAPTIVE_NP |
| 146 | #if TOKU_PTHREAD_DEBUG |
| 147 | #define TOKU_ADAPTIVE_MUTEX_INITIALIZER \ |
| 148 | { \ |
| 149 | .pmutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP, .psi_mutex = nullptr, \ |
| 150 | .owner = 0, .locked = false, .valid = true, .instr_key_id = 0 \ |
| 151 | } |
| 152 | #else |
| 153 | #define TOKU_ADAPTIVE_MUTEX_INITIALIZER \ |
| 154 | { .pmutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP, .psi_mutex = nullptr } |
| 155 | #endif |
| 156 | #endif |
| 157 | |
| 158 | // Different OSes implement mutexes as different amounts of nested structs. |
| 159 | // C++ will fill out all missing values with zeroes if you provide at least one |
| 160 | // zero, but it needs the right amount of nesting. |
| 161 | #if defined(__FreeBSD__) |
| 162 | #define ZERO_COND_INITIALIZER \ |
| 163 | { 0 } |
| 164 | #elif defined(__APPLE__) |
| 165 | #define ZERO_COND_INITIALIZER \ |
| 166 | { \ |
| 167 | { 0 } \ |
| 168 | } |
| 169 | #else // __linux__, at least |
| 170 | #define ZERO_COND_INITIALIZER \ |
| 171 | {} |
| 172 | #endif |
| 173 | |
| 174 | static inline void toku_mutexattr_init(toku_pthread_mutexattr_t *attr) { |
| 175 | int r = pthread_mutexattr_init(attr); |
| 176 | assert_zero(r); |
| 177 | } |
| 178 | |
| 179 | static inline void |
| 180 | toku_mutexattr_settype(toku_pthread_mutexattr_t *attr, int type) { |
| 181 | int r = pthread_mutexattr_settype(attr, type); |
| 182 | assert_zero(r); |
| 183 | } |
| 184 | |
| 185 | static inline void |
| 186 | toku_mutexattr_destroy(toku_pthread_mutexattr_t *attr) { |
| 187 | int r = pthread_mutexattr_destroy(attr); |
| 188 | assert_zero(r); |
| 189 | } |
| 190 | |
| 191 | #if TOKU_PTHREAD_DEBUG |
| 192 | static inline void toku_mutex_assert_locked(const toku_mutex_t *mutex) { |
| 193 | invariant(mutex->locked); |
| 194 | invariant(mutex->owner == pthread_self()); |
| 195 | } |
| 196 | #else |
| 197 | static inline void |
| 198 | toku_mutex_assert_locked(const toku_mutex_t *mutex __attribute__((unused))) { |
| 199 | } |
| 200 | #endif |
| 201 | |
| 202 | // asserting that a mutex is unlocked only makes sense |
| 203 | // if the calling thread can guaruntee that no other threads |
| 204 | // are trying to lock this mutex at the time of the assertion |
| 205 | // |
| 206 | // a good example of this is a tree with mutexes on each node. |
| 207 | // when a node is locked the caller knows that no other threads |
| 208 | // can be trying to lock its childrens' mutexes. the children |
| 209 | // are in one of two fixed states: locked or unlocked. |
| 210 | #if TOKU_PTHREAD_DEBUG |
| 211 | static inline void |
| 212 | toku_mutex_assert_unlocked(toku_mutex_t *mutex) { |
| 213 | invariant(mutex->owner == 0); |
| 214 | invariant(!mutex->locked); |
| 215 | } |
| 216 | #else |
| 217 | static inline void toku_mutex_assert_unlocked(toku_mutex_t *mutex |
| 218 | __attribute__((unused))) {} |
| 219 | #endif |
| 220 | |
| 221 | #define toku_mutex_lock(M) \ |
| 222 | toku_mutex_lock_with_source_location(M, __FILE__, __LINE__) |
| 223 | |
| 224 | static inline void toku_cond_init(toku_cond_t *cond, |
| 225 | const toku_pthread_condattr_t *attr) { |
| 226 | int r = pthread_cond_init(&cond->pcond, attr); |
| 227 | assert_zero(r); |
| 228 | } |
| 229 | |
| 230 | #define toku_mutex_trylock(M) \ |
| 231 | toku_mutex_trylock_with_source_location(M, __FILE__, __LINE__) |
| 232 | |
| 233 | inline void toku_mutex_unlock(toku_mutex_t *mutex) { |
| 234 | #if TOKU_PTHREAD_DEBUG |
| 235 | invariant(mutex->owner == pthread_self()); |
| 236 | invariant(mutex->valid); |
| 237 | invariant(mutex->locked); |
| 238 | mutex->locked = false; |
| 239 | mutex->owner = 0; |
| 240 | #endif |
| 241 | toku_instr_mutex_unlock(mutex->psi_mutex); |
| 242 | int r = pthread_mutex_unlock(&mutex->pmutex); |
| 243 | assert_zero(r); |
| 244 | } |
| 245 | |
| 246 | inline void toku_mutex_lock_with_source_location(toku_mutex_t *mutex, |
| 247 | const char *src_file, |
| 248 | int src_line) { |
| 249 | |
| 250 | toku_mutex_instrumentation mutex_instr; |
| 251 | toku_instr_mutex_lock_start(mutex_instr, *mutex, src_file, src_line); |
| 252 | |
| 253 | const int r = pthread_mutex_lock(&mutex->pmutex); |
| 254 | toku_instr_mutex_lock_end(mutex_instr, r); |
| 255 | |
| 256 | assert_zero(r); |
| 257 | #if TOKU_PTHREAD_DEBUG |
| 258 | invariant(mutex->valid); |
| 259 | invariant(!mutex->locked); |
| 260 | invariant(mutex->owner == 0); |
| 261 | mutex->locked = true; |
| 262 | mutex->owner = pthread_self(); |
| 263 | #endif |
| 264 | } |
| 265 | |
| 266 | inline int toku_mutex_trylock_with_source_location(toku_mutex_t *mutex, |
| 267 | const char *src_file, |
| 268 | int src_line) { |
| 269 | |
| 270 | toku_mutex_instrumentation mutex_instr; |
| 271 | toku_instr_mutex_trylock_start(mutex_instr, *mutex, src_file, src_line); |
| 272 | |
| 273 | const int r = pthread_mutex_lock(&mutex->pmutex); |
| 274 | toku_instr_mutex_lock_end(mutex_instr, r); |
| 275 | |
| 276 | #if TOKU_PTHREAD_DEBUG |
| 277 | if (r == 0) { |
| 278 | invariant(mutex->valid); |
| 279 | invariant(!mutex->locked); |
| 280 | invariant(mutex->owner == 0); |
| 281 | mutex->locked = true; |
| 282 | mutex->owner = pthread_self(); |
| 283 | } |
| 284 | #endif |
| 285 | return r; |
| 286 | } |
| 287 | |
| 288 | #define toku_cond_wait(C, M) \ |
| 289 | toku_cond_wait_with_source_location(C, M, __FILE__, __LINE__) |
| 290 | |
| 291 | #define toku_cond_timedwait(C, M, W) \ |
| 292 | toku_cond_timedwait_with_source_location(C, M, W, __FILE__, __LINE__) |
| 293 | |
| 294 | inline void toku_cond_init(const toku_instr_key &key, |
| 295 | toku_cond_t *cond, |
| 296 | const pthread_condattr_t *attr) { |
| 297 | toku_instr_cond_init(key, *cond); |
| 298 | int r = pthread_cond_init(&cond->pcond, attr); |
| 299 | assert_zero(r); |
| 300 | } |
| 301 | |
| 302 | inline void toku_cond_destroy(toku_cond_t *cond) { |
| 303 | toku_instr_cond_destroy(cond->psi_cond); |
| 304 | int r = pthread_cond_destroy(&cond->pcond); |
| 305 | assert_zero(r); |
| 306 | } |
| 307 | |
| 308 | inline void toku_cond_wait_with_source_location(toku_cond_t *cond, |
| 309 | toku_mutex_t *mutex, |
| 310 | const char *src_file, |
| 311 | uint src_line) { |
| 312 | |
| 313 | #if TOKU_PTHREAD_DEBUG |
| 314 | invariant(mutex->locked); |
| 315 | mutex->locked = false; |
| 316 | mutex->owner = 0; |
| 317 | #endif |
| 318 | |
| 319 | /* Instrumentation start */ |
| 320 | toku_cond_instrumentation cond_instr; |
| 321 | toku_instr_cond_wait_start(cond_instr, |
| 322 | toku_instr_cond_op::cond_wait, |
| 323 | *cond, |
| 324 | *mutex, |
| 325 | src_file, |
| 326 | src_line); |
| 327 | |
| 328 | /* Instrumented code */ |
| 329 | const int r = pthread_cond_wait(&cond->pcond, &mutex->pmutex); |
| 330 | |
| 331 | /* Instrumentation end */ |
| 332 | toku_instr_cond_wait_end(cond_instr, r); |
| 333 | |
| 334 | assert_zero(r); |
| 335 | #if TOKU_PTHREAD_DEBUG |
| 336 | invariant(!mutex->locked); |
| 337 | mutex->locked = true; |
| 338 | mutex->owner = pthread_self(); |
| 339 | #endif |
| 340 | } |
| 341 | |
| 342 | inline int toku_cond_timedwait_with_source_location(toku_cond_t *cond, |
| 343 | toku_mutex_t *mutex, |
| 344 | toku_timespec_t *wakeup_at, |
| 345 | const char *src_file, |
| 346 | uint src_line) { |
| 347 | #if TOKU_PTHREAD_DEBUG |
| 348 | invariant(mutex->locked); |
| 349 | mutex->locked = false; |
| 350 | mutex->owner = 0; |
| 351 | #endif |
| 352 | |
| 353 | /* Instrumentation start */ |
| 354 | toku_cond_instrumentation cond_instr; |
| 355 | toku_instr_cond_wait_start(cond_instr, |
| 356 | toku_instr_cond_op::cond_timedwait, |
| 357 | *cond, |
| 358 | *mutex, |
| 359 | src_file, |
| 360 | src_line); |
| 361 | |
| 362 | /* Instrumented code */ |
| 363 | const int r = pthread_cond_timedwait( |
| 364 | &cond->pcond, &mutex->pmutex, wakeup_at); |
| 365 | |
| 366 | /* Instrumentation end */ |
| 367 | toku_instr_cond_wait_end(cond_instr, r); |
| 368 | |
| 369 | #if TOKU_PTHREAD_DEBUG |
| 370 | invariant(!mutex->locked); |
| 371 | mutex->locked = true; |
| 372 | mutex->owner = pthread_self(); |
| 373 | #endif |
| 374 | return r; |
| 375 | } |
| 376 | |
| 377 | inline void toku_cond_signal(toku_cond_t *cond) { |
| 378 | toku_instr_cond_signal(*cond); |
| 379 | const int r = pthread_cond_signal(&cond->pcond); |
| 380 | assert_zero(r); |
| 381 | } |
| 382 | |
| 383 | inline void toku_cond_broadcast(toku_cond_t *cond) { |
| 384 | toku_instr_cond_broadcast(*cond); |
| 385 | const int r = pthread_cond_broadcast(&cond->pcond); |
| 386 | assert_zero(r); |
| 387 | } |
| 388 | |
| 389 | inline void toku_mutex_init(const toku_instr_key &key, |
| 390 | toku_mutex_t *mutex, |
| 391 | const toku_pthread_mutexattr_t *attr) { |
| 392 | #if TOKU_PTHREAD_DEBUG |
| 393 | mutex->valid = true; |
| 394 | #endif |
| 395 | toku_instr_mutex_init(key, *mutex); |
| 396 | const int r = pthread_mutex_init(&mutex->pmutex, attr); |
| 397 | assert_zero(r); |
| 398 | #if TOKU_PTHREAD_DEBUG |
| 399 | mutex->locked = false; |
| 400 | invariant(mutex->valid); |
| 401 | mutex->valid = true; |
| 402 | mutex->owner = 0; |
| 403 | #endif |
| 404 | } |
| 405 | |
| 406 | inline void toku_mutex_destroy(toku_mutex_t *mutex) { |
| 407 | #if TOKU_PTHREAD_DEBUG |
| 408 | invariant(mutex->valid); |
| 409 | mutex->valid = false; |
| 410 | invariant(!mutex->locked); |
| 411 | #endif |
| 412 | toku_instr_mutex_destroy(mutex->psi_mutex); |
| 413 | int r = pthread_mutex_destroy(&mutex->pmutex); |
| 414 | assert_zero(r); |
| 415 | } |
| 416 | |
| 417 | #define toku_pthread_rwlock_rdlock(RW) \ |
| 418 | toku_pthread_rwlock_rdlock_with_source_location(RW, __FILE__, __LINE__) |
| 419 | |
| 420 | #define toku_pthread_rwlock_wrlock(RW) \ |
| 421 | toku_pthread_rwlock_wrlock_with_source_location(RW, __FILE__, __LINE__) |
| 422 | |
| 423 | inline void toku_pthread_rwlock_init( |
| 424 | const toku_instr_key &key, |
| 425 | toku_pthread_rwlock_t *__restrict rwlock, |
| 426 | const toku_pthread_rwlockattr_t *__restrict attr) { |
| 427 | toku_instr_rwlock_init(key, *rwlock); |
| 428 | int r = pthread_rwlock_init(&rwlock->rwlock, attr); |
| 429 | assert_zero(r); |
| 430 | } |
| 431 | |
| 432 | inline void toku_pthread_rwlock_destroy(toku_pthread_rwlock_t *rwlock) { |
| 433 | toku_instr_rwlock_destroy(rwlock->psi_rwlock); |
| 434 | int r = pthread_rwlock_destroy(&rwlock->rwlock); |
| 435 | assert_zero(r); |
| 436 | } |
| 437 | |
| 438 | inline void toku_pthread_rwlock_rdlock_with_source_location( |
| 439 | toku_pthread_rwlock_t *rwlock, |
| 440 | const char *src_file, |
| 441 | uint src_line) { |
| 442 | |
| 443 | /* Instrumentation start */ |
| 444 | toku_rwlock_instrumentation rwlock_instr; |
| 445 | toku_instr_rwlock_rdlock_wait_start( |
| 446 | rwlock_instr, *rwlock, src_file, src_line); |
| 447 | /* Instrumented code */ |
| 448 | const int r = pthread_rwlock_rdlock(&rwlock->rwlock); |
| 449 | |
| 450 | /* Instrumentation end */ |
| 451 | toku_instr_rwlock_rdlock_wait_end(rwlock_instr, r); |
| 452 | |
| 453 | assert_zero(r); |
| 454 | } |
| 455 | |
| 456 | inline void toku_pthread_rwlock_wrlock_with_source_location( |
| 457 | toku_pthread_rwlock_t *rwlock, |
| 458 | const char *src_file, |
| 459 | uint src_line) { |
| 460 | |
| 461 | /* Instrumentation start */ |
| 462 | toku_rwlock_instrumentation rwlock_instr; |
| 463 | toku_instr_rwlock_wrlock_wait_start( |
| 464 | rwlock_instr, *rwlock, src_file, src_line); |
| 465 | /* Instrumented code */ |
| 466 | const int r = pthread_rwlock_wrlock(&rwlock->rwlock); |
| 467 | |
| 468 | /* Instrumentation end */ |
| 469 | toku_instr_rwlock_wrlock_wait_end(rwlock_instr, r); |
| 470 | |
| 471 | assert_zero(r); |
| 472 | } |
| 473 | |
| 474 | inline void toku_pthread_rwlock_rdunlock(toku_pthread_rwlock_t *rwlock) { |
| 475 | toku_instr_rwlock_unlock(*rwlock); |
| 476 | const int r = pthread_rwlock_unlock(&rwlock->rwlock); |
| 477 | assert_zero(r); |
| 478 | } |
| 479 | |
| 480 | inline void toku_pthread_rwlock_wrunlock(toku_pthread_rwlock_t *rwlock) { |
| 481 | toku_instr_rwlock_unlock(*rwlock); |
| 482 | const int r = pthread_rwlock_unlock(&rwlock->rwlock); |
| 483 | assert_zero(r); |
| 484 | } |
| 485 | |
| 486 | static inline int toku_pthread_join(toku_pthread_t thread, void **value_ptr) { |
| 487 | return pthread_join(thread, value_ptr); |
| 488 | } |
| 489 | |
| 490 | static inline int |
| 491 | toku_pthread_detach(toku_pthread_t thread) { |
| 492 | return pthread_detach(thread); |
| 493 | } |
| 494 | |
| 495 | static inline int |
| 496 | toku_pthread_key_create(toku_pthread_key_t *key, void (*destroyf)(void *)) { |
| 497 | return pthread_key_create(key, destroyf); |
| 498 | } |
| 499 | |
| 500 | static inline int |
| 501 | toku_pthread_key_delete(toku_pthread_key_t key) { |
| 502 | return pthread_key_delete(key); |
| 503 | } |
| 504 | |
| 505 | static inline void * |
| 506 | toku_pthread_getspecific(toku_pthread_key_t key) { |
| 507 | return pthread_getspecific(key); |
| 508 | } |
| 509 | |
| 510 | static inline int toku_pthread_setspecific(toku_pthread_key_t key, void *data) { |
| 511 | return pthread_setspecific(key, data); |
| 512 | } |
| 513 | |
| 514 | int toku_pthread_yield(void) __attribute__((__visibility__("default" ))); |
| 515 | |
| 516 | static inline toku_pthread_t toku_pthread_self(void) { return pthread_self(); } |
| 517 | |
| 518 | static inline void *toku_pthread_done(void *exit_value) { |
| 519 | toku_instr_delete_current_thread(); |
| 520 | pthread_exit(exit_value); |
| 521 | } |
| 522 | |