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