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
38 Licensed under the Apache License, Version 2.0 (the "License");
39 you may not use this file except in compliance with the License.
40 You may obtain a copy of the License at
41
42 http://www.apache.org/licenses/LICENSE-2.0
43
44 Unless required by applicable law or agreed to in writing, software
45 distributed under the License is distributed on an "AS IS" BASIS,
46 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
47 See the License for the specific language governing permissions and
48 limitations under the License.
49 ======= */
50
51 #ident \
52 "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
53
54 #pragma once
55
56 #include <pthread.h>
57 #include <stdint.h>
58 #include <time.h>
59
60 #include "toku_portability.h"
61 // PORT2: #include "toku_assert.h"
62
63 // TODO: some things moved toku_instrumentation.h, not necessarily the best
64 // place
65 typedef pthread_attr_t toku_pthread_attr_t;
66 typedef pthread_t toku_pthread_t;
67 typedef pthread_mutex_t toku_pthread_mutex_t;
68 typedef pthread_condattr_t toku_pthread_condattr_t;
69 typedef pthread_cond_t toku_pthread_cond_t;
70 typedef pthread_rwlockattr_t toku_pthread_rwlockattr_t;
71 typedef pthread_key_t toku_pthread_key_t;
72 typedef struct timespec toku_timespec_t;
73
74 // TODO: break this include loop
75 #include <pthread.h>
76 typedef pthread_mutexattr_t toku_pthread_mutexattr_t;
77
78 struct toku_mutex_t {
79 pthread_mutex_t pmutex;
80 struct PSI_mutex *psi_mutex; /* The performance schema instrumentation hook */
81 #if defined(TOKU_PTHREAD_DEBUG)
82 pthread_t owner; // = pthread_self(); // for debugging
83 bool locked;
84 bool valid;
85 pfs_key_t instr_key_id;
86 #endif // defined(TOKU_PTHREAD_DEBUG)
87 };
88
89 struct toku_cond_t {
90 pthread_cond_t pcond;
91 struct PSI_cond *psi_cond;
92 #if defined(TOKU_PTHREAD_DEBUG)
93 pfs_key_t instr_key_id;
94 #endif // defined(TOKU_PTHREAD_DEBUG)
95 };
96
97 #if defined(TOKU_PTHREAD_DEBUG)
98 #define TOKU_COND_INITIALIZER \
99 { .pcond = PTHREAD_COND_INITIALIZER, .psi_cond = nullptr, .instr_key_id = 0 }
100 #else
101 #define TOKU_COND_INITIALIZER \
102 { .pcond = PTHREAD_COND_INITIALIZER, .psi_cond = nullptr }
103 #endif // defined(TOKU_PTHREAD_DEBUG)
104
105 struct toku_pthread_rwlock_t {
106 pthread_rwlock_t rwlock;
107 struct PSI_rwlock *psi_rwlock;
108 #if defined(TOKU_PTHREAD_DEBUG)
109 pfs_key_t instr_key_id;
110 #endif // defined(TOKU_PTHREAD_DEBUG)
111 };
112
113 typedef struct toku_mutex_aligned {
114 toku_mutex_t aligned_mutex __attribute__((__aligned__(64)));
115 } toku_mutex_aligned_t;
116
117 // Initializing with {} will fill in a struct with all zeros.
118 // But you may also need a pragma to suppress the warnings, as follows
119 //
120 // #pragma GCC diagnostic push
121 // #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
122 // toku_mutex_t foo = ZERO_MUTEX_INITIALIZER;
123 // #pragma GCC diagnostic pop
124 //
125 // In general it will be a lot of busy work to make this codebase compile
126 // cleanly with -Wmissing-field-initializers
127
128 #define ZERO_MUTEX_INITIALIZER \
129 {}
130
131 #if defined(TOKU_PTHREAD_DEBUG)
132 #define TOKU_MUTEX_INITIALIZER \
133 { \
134 .pmutex = PTHREAD_MUTEX_INITIALIZER, .psi_mutex = nullptr, .owner = 0, \
135 .locked = false, .valid = true, .instr_key_id = 0 \
136 }
137 #else
138 #define TOKU_MUTEX_INITIALIZER \
139 { .pmutex = PTHREAD_MUTEX_INITIALIZER, .psi_mutex = nullptr }
140 #endif // defined(TOKU_PTHREAD_DEBUG)
141
142 // Darwin doesn't provide adaptive mutexes
143 #if defined(__APPLE__)
144 #define TOKU_MUTEX_ADAPTIVE PTHREAD_MUTEX_DEFAULT
145 #if defined(TOKU_PTHREAD_DEBUG)
146 #define TOKU_ADAPTIVE_MUTEX_INITIALIZER \
147 { \
148 .pmutex = PTHREAD_MUTEX_INITIALIZER, .psi_mutex = nullptr, .owner = 0, \
149 .locked = false, .valid = true, .instr_key_id = 0 \
150 }
151 #else
152 #define TOKU_ADAPTIVE_MUTEX_INITIALIZER \
153 { .pmutex = PTHREAD_MUTEX_INITIALIZER, .psi_mutex = nullptr }
154 #endif // defined(TOKU_PTHREAD_DEBUG)
155 #else // __FreeBSD__, __linux__, at least
156 #if defined(__GLIBC__)
157 #define TOKU_MUTEX_ADAPTIVE PTHREAD_MUTEX_ADAPTIVE_NP
158 #else
159 // not all libc (e.g. musl) implement NP (Non-POSIX) attributes
160 #define TOKU_MUTEX_ADAPTIVE PTHREAD_MUTEX_DEFAULT
161 #endif
162 #if defined(TOKU_PTHREAD_DEBUG)
163 #define TOKU_ADAPTIVE_MUTEX_INITIALIZER \
164 { \
165 .pmutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP, .psi_mutex = nullptr, \
166 .owner = 0, .locked = false, .valid = true, .instr_key_id = 0 \
167 }
168 #else
169 #define TOKU_ADAPTIVE_MUTEX_INITIALIZER \
170 { .pmutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP, .psi_mutex = nullptr }
171 #endif // defined(TOKU_PTHREAD_DEBUG)
172 #endif // defined(__APPLE__)
173
174 // Different OSes implement mutexes as different amounts of nested structs.
175 // C++ will fill out all missing values with zeroes if you provide at least one
176 // zero, but it needs the right amount of nesting.
177 #if defined(__FreeBSD__)
178 #define ZERO_COND_INITIALIZER \
179 { 0 }
180 #elif defined(__APPLE__)
181 #define ZERO_COND_INITIALIZER \
182 { \
183 { 0 } \
184 }
185 #else // __linux__, at least
186 #define ZERO_COND_INITIALIZER \
187 {}
188 #endif
189
toku_mutexattr_init(toku_pthread_mutexattr_t * attr)190 static inline void toku_mutexattr_init(toku_pthread_mutexattr_t *attr) {
191 int r = pthread_mutexattr_init(attr);
192 assert_zero(r);
193 }
194
toku_mutexattr_settype(toku_pthread_mutexattr_t * attr,int type)195 static inline void toku_mutexattr_settype(toku_pthread_mutexattr_t *attr,
196 int type) {
197 int r = pthread_mutexattr_settype(attr, type);
198 assert_zero(r);
199 }
200
toku_mutexattr_destroy(toku_pthread_mutexattr_t * attr)201 static inline void toku_mutexattr_destroy(toku_pthread_mutexattr_t *attr) {
202 int r = pthread_mutexattr_destroy(attr);
203 assert_zero(r);
204 }
205
206 #if defined(TOKU_PTHREAD_DEBUG)
toku_mutex_assert_locked(const toku_mutex_t * mutex)207 static inline void toku_mutex_assert_locked(const toku_mutex_t *mutex) {
208 invariant(mutex->locked);
209 invariant(mutex->owner == pthread_self());
210 }
211 #else
toku_mutex_assert_locked(const toku_mutex_t * mutex)212 static inline void toku_mutex_assert_locked(const toku_mutex_t *mutex
213 __attribute__((unused))) {}
214 #endif // defined(TOKU_PTHREAD_DEBUG)
215
216 // asserting that a mutex is unlocked only makes sense
217 // if the calling thread can guaruntee that no other threads
218 // are trying to lock this mutex at the time of the assertion
219 //
220 // a good example of this is a tree with mutexes on each node.
221 // when a node is locked the caller knows that no other threads
222 // can be trying to lock its childrens' mutexes. the children
223 // are in one of two fixed states: locked or unlocked.
224 #if defined(TOKU_PTHREAD_DEBUG)
toku_mutex_assert_unlocked(toku_mutex_t * mutex)225 static inline void toku_mutex_assert_unlocked(toku_mutex_t *mutex) {
226 invariant(mutex->owner == 0);
227 invariant(!mutex->locked);
228 }
229 #else
toku_mutex_assert_unlocked(toku_mutex_t * mutex)230 static inline void toku_mutex_assert_unlocked(toku_mutex_t *mutex
231 __attribute__((unused))) {}
232 #endif // defined(TOKU_PTHREAD_DEBUG)
233
234 #define toku_mutex_lock(M) \
235 toku_mutex_lock_with_source_location(M, __FILE__, __LINE__)
236
toku_cond_init(toku_cond_t * cond,const toku_pthread_condattr_t * attr)237 static inline void toku_cond_init(toku_cond_t *cond,
238 const toku_pthread_condattr_t *attr) {
239 int r = pthread_cond_init(&cond->pcond, attr);
240 assert_zero(r);
241 }
242
243 #define toku_mutex_trylock(M) \
244 toku_mutex_trylock_with_source_location(M, __FILE__, __LINE__)
245
toku_mutex_unlock(toku_mutex_t * mutex)246 inline void toku_mutex_unlock(toku_mutex_t *mutex) {
247 #if defined(TOKU_PTHREAD_DEBUG)
248 invariant(mutex->owner == pthread_self());
249 invariant(mutex->valid);
250 invariant(mutex->locked);
251 mutex->locked = false;
252 mutex->owner = 0;
253 #endif // defined(TOKU_PTHREAD_DEBUG)
254 toku_instr_mutex_unlock(mutex->psi_mutex);
255 int r = pthread_mutex_unlock(&mutex->pmutex);
256 assert_zero(r);
257 }
258
toku_mutex_lock_with_source_location(toku_mutex_t * mutex,const char * src_file,int src_line)259 inline void toku_mutex_lock_with_source_location(toku_mutex_t *mutex,
260 const char *src_file,
261 int src_line) {
262 toku_mutex_instrumentation mutex_instr;
263 toku_instr_mutex_lock_start(mutex_instr, *mutex, src_file, src_line);
264
265 const int r = pthread_mutex_lock(&mutex->pmutex);
266 toku_instr_mutex_lock_end(mutex_instr, r);
267
268 assert_zero(r);
269 #if defined(TOKU_PTHREAD_DEBUG)
270 invariant(mutex->valid);
271 invariant(!mutex->locked);
272 invariant(mutex->owner == 0);
273 mutex->locked = true;
274 mutex->owner = pthread_self();
275 #endif // defined(TOKU_PTHREAD_DEBUG)
276 }
277
toku_mutex_trylock_with_source_location(toku_mutex_t * mutex,const char * src_file,int src_line)278 inline int toku_mutex_trylock_with_source_location(toku_mutex_t *mutex,
279 const char *src_file,
280 int src_line) {
281 toku_mutex_instrumentation mutex_instr;
282 toku_instr_mutex_trylock_start(mutex_instr, *mutex, src_file, src_line);
283
284 const int r = pthread_mutex_lock(&mutex->pmutex);
285 toku_instr_mutex_lock_end(mutex_instr, r);
286
287 #if defined(TOKU_PTHREAD_DEBUG)
288 if (r == 0) {
289 invariant(mutex->valid);
290 invariant(!mutex->locked);
291 invariant(mutex->owner == 0);
292 mutex->locked = true;
293 mutex->owner = pthread_self();
294 }
295 #endif // defined(TOKU_PTHREAD_DEBUG)
296 return r;
297 }
298
299 #define toku_cond_wait(C, M) \
300 toku_cond_wait_with_source_location(C, M, __FILE__, __LINE__)
301
302 #define toku_cond_timedwait(C, M, W) \
303 toku_cond_timedwait_with_source_location(C, M, W, __FILE__, __LINE__)
304
toku_cond_init(const toku_instr_key & key,toku_cond_t * cond,const pthread_condattr_t * attr)305 inline void toku_cond_init(const toku_instr_key &key, toku_cond_t *cond,
306 const pthread_condattr_t *attr) {
307 toku_instr_cond_init(key, *cond);
308 int r = pthread_cond_init(&cond->pcond, attr);
309 assert_zero(r);
310 }
311
toku_cond_destroy(toku_cond_t * cond)312 inline void toku_cond_destroy(toku_cond_t *cond) {
313 toku_instr_cond_destroy(cond->psi_cond);
314 int r = pthread_cond_destroy(&cond->pcond);
315 assert_zero(r);
316 }
317
toku_cond_wait_with_source_location(toku_cond_t * cond,toku_mutex_t * mutex,const char * src_file,int src_line)318 inline void toku_cond_wait_with_source_location(toku_cond_t *cond,
319 toku_mutex_t *mutex,
320 const char *src_file,
321 int src_line) {
322 #if defined(TOKU_PTHREAD_DEBUG)
323 invariant(mutex->locked);
324 mutex->locked = false;
325 mutex->owner = 0;
326 #endif // defined(TOKU_PTHREAD_DEBUG)
327
328 /* Instrumentation start */
329 toku_cond_instrumentation cond_instr;
330 toku_instr_cond_wait_start(cond_instr, toku_instr_cond_op::cond_wait, *cond,
331 *mutex, src_file, src_line);
332
333 /* Instrumented code */
334 const int r = pthread_cond_wait(&cond->pcond, &mutex->pmutex);
335
336 /* Instrumentation end */
337 toku_instr_cond_wait_end(cond_instr, r);
338
339 assert_zero(r);
340 #if defined(TOKU_PTHREAD_DEBUG)
341 invariant(!mutex->locked);
342 mutex->locked = true;
343 mutex->owner = pthread_self();
344 #endif // defined(TOKU_PTHREAD_DEBUG)
345 }
346
toku_cond_timedwait_with_source_location(toku_cond_t * cond,toku_mutex_t * mutex,toku_timespec_t * wakeup_at,const char * src_file,int src_line)347 inline int toku_cond_timedwait_with_source_location(toku_cond_t *cond,
348 toku_mutex_t *mutex,
349 toku_timespec_t *wakeup_at,
350 const char *src_file,
351 int src_line) {
352 #if defined(TOKU_PTHREAD_DEBUG)
353 invariant(mutex->locked);
354 mutex->locked = false;
355 mutex->owner = 0;
356 #endif // defined(TOKU_PTHREAD_DEBUG)
357
358 /* Instrumentation start */
359 toku_cond_instrumentation cond_instr;
360 toku_instr_cond_wait_start(cond_instr, toku_instr_cond_op::cond_timedwait,
361 *cond, *mutex, src_file, src_line);
362
363 /* Instrumented code */
364 const int r = pthread_cond_timedwait(&cond->pcond, &mutex->pmutex, wakeup_at);
365
366 /* Instrumentation end */
367 toku_instr_cond_wait_end(cond_instr, r);
368
369 #if defined(TOKU_PTHREAD_DEBUG)
370 invariant(!mutex->locked);
371 mutex->locked = true;
372 mutex->owner = pthread_self();
373 #endif // defined(TOKU_PTHREAD_DEBUG)
374 return r;
375 }
376
toku_cond_signal(toku_cond_t * cond)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
toku_cond_broadcast(toku_cond_t * cond)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
toku_mutex_init(const toku_instr_key & key,toku_mutex_t * mutex,const toku_pthread_mutexattr_t * attr)389 inline void toku_mutex_init(const toku_instr_key &key, toku_mutex_t *mutex,
390 const toku_pthread_mutexattr_t *attr) {
391 #if defined(TOKU_PTHREAD_DEBUG)
392 mutex->valid = true;
393 #endif // defined(TOKU_PTHREAD_DEBUG)
394 toku_instr_mutex_init(key, *mutex);
395 const int r = pthread_mutex_init(&mutex->pmutex, attr);
396 assert_zero(r);
397 #if defined(TOKU_PTHREAD_DEBUG)
398 mutex->locked = false;
399 invariant(mutex->valid);
400 mutex->valid = true;
401 mutex->owner = 0;
402 #endif // defined(TOKU_PTHREAD_DEBUG)
403 }
404
toku_mutex_destroy(toku_mutex_t * mutex)405 inline void toku_mutex_destroy(toku_mutex_t *mutex) {
406 #if defined(TOKU_PTHREAD_DEBUG)
407 invariant(mutex->valid);
408 mutex->valid = false;
409 invariant(!mutex->locked);
410 #endif // defined(TOKU_PTHREAD_DEBUG)
411 toku_instr_mutex_destroy(mutex->psi_mutex);
412 int r = pthread_mutex_destroy(&mutex->pmutex);
413 assert_zero(r);
414 }
415
416 #define toku_pthread_rwlock_rdlock(RW) \
417 toku_pthread_rwlock_rdlock_with_source_location(RW, __FILE__, __LINE__)
418
419 #define toku_pthread_rwlock_wrlock(RW) \
420 toku_pthread_rwlock_wrlock_with_source_location(RW, __FILE__, __LINE__)
421
422 #if 0
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 #endif
486
toku_pthread_join(toku_pthread_t thread,void ** value_ptr)487 static inline int toku_pthread_join(toku_pthread_t thread, void **value_ptr) {
488 return pthread_join(thread, value_ptr);
489 }
490
toku_pthread_detach(toku_pthread_t thread)491 static inline int toku_pthread_detach(toku_pthread_t thread) {
492 return pthread_detach(thread);
493 }
494
toku_pthread_key_create(toku_pthread_key_t * key,void (* destroyf)(void *))495 static inline int toku_pthread_key_create(toku_pthread_key_t *key,
496 void (*destroyf)(void *)) {
497 return pthread_key_create(key, destroyf);
498 }
499
toku_pthread_key_delete(toku_pthread_key_t key)500 static inline int toku_pthread_key_delete(toku_pthread_key_t key) {
501 return pthread_key_delete(key);
502 }
503
toku_pthread_getspecific(toku_pthread_key_t key)504 static inline void *toku_pthread_getspecific(toku_pthread_key_t key) {
505 return pthread_getspecific(key);
506 }
507
toku_pthread_setspecific(toku_pthread_key_t key,void * data)508 static inline int toku_pthread_setspecific(toku_pthread_key_t key, void *data) {
509 return pthread_setspecific(key, data);
510 }
511
512 int toku_pthread_yield(void) __attribute__((__visibility__("default")));
513
toku_pthread_self(void)514 static inline toku_pthread_t toku_pthread_self(void) { return pthread_self(); }
515
toku_pthread_done(void * exit_value)516 static inline void *toku_pthread_done(void *exit_value) {
517 toku_instr_delete_current_thread();
518 pthread_exit(exit_value);
519 return nullptr; // Avoid compiler warning
520 }
521