2018-01-30 Thomas Koenig <tkoenig@gcc.gnu.org>
[official-gcc.git] / libsanitizer / tsan / tsan_libdispatch_mac.cc
blob5200a791fc2f2a2c19ff2570870dfba9c2719bb2
1 //===-- tsan_libdispatch_mac.cc -------------------------------------------===//
2 //
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
5 //
6 //===----------------------------------------------------------------------===//
7 //
8 // This file is a part of ThreadSanitizer (TSan), a race detector.
9 //
10 // Mac-specific libdispatch (GCD) support.
11 //===----------------------------------------------------------------------===//
13 #include "sanitizer_common/sanitizer_platform.h"
14 #if SANITIZER_MAC
16 #include "sanitizer_common/sanitizer_common.h"
17 #include "interception/interception.h"
18 #include "tsan_interceptors.h"
19 #include "tsan_platform.h"
20 #include "tsan_rtl.h"
22 #include <Block.h>
23 #include <dispatch/dispatch.h>
24 #include <pthread.h>
26 typedef long long_t; // NOLINT
28 namespace __tsan {
30 typedef struct {
31 dispatch_queue_t queue;
32 void *orig_context;
33 dispatch_function_t orig_work;
34 bool free_context_in_callback;
35 bool submitted_synchronously;
36 bool is_barrier_block;
37 uptr non_queue_sync_object;
38 } tsan_block_context_t;
40 // The offsets of different fields of the dispatch_queue_t structure, exported
41 // by libdispatch.dylib.
42 extern "C" struct dispatch_queue_offsets_s {
43 const uint16_t dqo_version;
44 const uint16_t dqo_label;
45 const uint16_t dqo_label_size;
46 const uint16_t dqo_flags;
47 const uint16_t dqo_flags_size;
48 const uint16_t dqo_serialnum;
49 const uint16_t dqo_serialnum_size;
50 const uint16_t dqo_width;
51 const uint16_t dqo_width_size;
52 const uint16_t dqo_running;
53 const uint16_t dqo_running_size;
54 const uint16_t dqo_suspend_cnt;
55 const uint16_t dqo_suspend_cnt_size;
56 const uint16_t dqo_target_queue;
57 const uint16_t dqo_target_queue_size;
58 const uint16_t dqo_priority;
59 const uint16_t dqo_priority_size;
60 } dispatch_queue_offsets;
62 static bool IsQueueSerial(dispatch_queue_t q) {
63 CHECK_EQ(dispatch_queue_offsets.dqo_width_size, 2);
64 uptr width = *(uint16_t *)(((uptr)q) + dispatch_queue_offsets.dqo_width);
65 CHECK_NE(width, 0);
66 return width == 1;
69 static dispatch_queue_t GetTargetQueueFromQueue(dispatch_queue_t q) {
70 CHECK_EQ(dispatch_queue_offsets.dqo_target_queue_size, 8);
71 dispatch_queue_t tq = *(
72 dispatch_queue_t *)(((uptr)q) + dispatch_queue_offsets.dqo_target_queue);
73 return tq;
76 static dispatch_queue_t GetTargetQueueFromSource(dispatch_source_t source) {
77 dispatch_queue_t tq = GetTargetQueueFromQueue((dispatch_queue_t)source);
78 CHECK_NE(tq, 0);
79 return tq;
82 static tsan_block_context_t *AllocContext(ThreadState *thr, uptr pc,
83 dispatch_queue_t queue,
84 void *orig_context,
85 dispatch_function_t orig_work) {
86 tsan_block_context_t *new_context =
87 (tsan_block_context_t *)user_alloc_internal(thr, pc,
88 sizeof(tsan_block_context_t));
89 new_context->queue = queue;
90 new_context->orig_context = orig_context;
91 new_context->orig_work = orig_work;
92 new_context->free_context_in_callback = true;
93 new_context->submitted_synchronously = false;
94 new_context->is_barrier_block = false;
95 new_context->non_queue_sync_object = 0;
96 return new_context;
99 #define GET_QUEUE_SYNC_VARS(context, q) \
100 bool is_queue_serial = q && IsQueueSerial(q); \
101 uptr sync_ptr = (uptr)q ?: context->non_queue_sync_object; \
102 uptr serial_sync = (uptr)sync_ptr; \
103 uptr concurrent_sync = sync_ptr ? ((uptr)sync_ptr) + sizeof(uptr) : 0; \
104 bool serial_task = context->is_barrier_block || is_queue_serial
106 static void dispatch_sync_pre_execute(ThreadState *thr, uptr pc,
107 tsan_block_context_t *context) {
108 uptr submit_sync = (uptr)context;
109 Acquire(thr, pc, submit_sync);
111 dispatch_queue_t q = context->queue;
112 do {
113 GET_QUEUE_SYNC_VARS(context, q);
114 if (serial_sync) Acquire(thr, pc, serial_sync);
115 if (serial_task && concurrent_sync) Acquire(thr, pc, concurrent_sync);
117 if (q) q = GetTargetQueueFromQueue(q);
118 } while (q);
121 static void dispatch_sync_post_execute(ThreadState *thr, uptr pc,
122 tsan_block_context_t *context) {
123 uptr submit_sync = (uptr)context;
124 if (context->submitted_synchronously) Release(thr, pc, submit_sync);
126 dispatch_queue_t q = context->queue;
127 do {
128 GET_QUEUE_SYNC_VARS(context, q);
129 if (serial_task && serial_sync) Release(thr, pc, serial_sync);
130 if (!serial_task && concurrent_sync) Release(thr, pc, concurrent_sync);
132 if (q) q = GetTargetQueueFromQueue(q);
133 } while (q);
136 static void dispatch_callback_wrap(void *param) {
137 SCOPED_INTERCEPTOR_RAW(dispatch_callback_wrap);
138 tsan_block_context_t *context = (tsan_block_context_t *)param;
140 dispatch_sync_pre_execute(thr, pc, context);
142 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
143 context->orig_work(context->orig_context);
144 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
146 dispatch_sync_post_execute(thr, pc, context);
148 if (context->free_context_in_callback) user_free(thr, pc, context);
151 static void invoke_block(void *param) {
152 dispatch_block_t block = (dispatch_block_t)param;
153 block();
156 static void invoke_and_release_block(void *param) {
157 dispatch_block_t block = (dispatch_block_t)param;
158 block();
159 Block_release(block);
162 #define DISPATCH_INTERCEPT_B(name, barrier) \
163 TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, dispatch_block_t block) { \
164 SCOPED_TSAN_INTERCEPTOR(name, q, block); \
165 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
166 dispatch_block_t heap_block = Block_copy(block); \
167 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
168 tsan_block_context_t *new_context = \
169 AllocContext(thr, pc, q, heap_block, &invoke_and_release_block); \
170 new_context->is_barrier_block = barrier; \
171 Release(thr, pc, (uptr)new_context); \
172 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
173 REAL(name##_f)(q, new_context, dispatch_callback_wrap); \
174 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
177 #define DISPATCH_INTERCEPT_SYNC_B(name, barrier) \
178 TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, \
179 DISPATCH_NOESCAPE dispatch_block_t block) { \
180 SCOPED_TSAN_INTERCEPTOR(name, q, block); \
181 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
182 dispatch_block_t heap_block = Block_copy(block); \
183 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
184 tsan_block_context_t new_context = { \
185 q, heap_block, &invoke_and_release_block, false, true, barrier, 0}; \
186 Release(thr, pc, (uptr)&new_context); \
187 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
188 REAL(name##_f)(q, &new_context, dispatch_callback_wrap); \
189 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
190 Acquire(thr, pc, (uptr)&new_context); \
193 #define DISPATCH_INTERCEPT_F(name, barrier) \
194 TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, void *context, \
195 dispatch_function_t work) { \
196 SCOPED_TSAN_INTERCEPTOR(name, q, context, work); \
197 tsan_block_context_t *new_context = \
198 AllocContext(thr, pc, q, context, work); \
199 new_context->is_barrier_block = barrier; \
200 Release(thr, pc, (uptr)new_context); \
201 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
202 REAL(name)(q, new_context, dispatch_callback_wrap); \
203 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
206 #define DISPATCH_INTERCEPT_SYNC_F(name, barrier) \
207 TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, void *context, \
208 dispatch_function_t work) { \
209 SCOPED_TSAN_INTERCEPTOR(name, q, context, work); \
210 tsan_block_context_t new_context = { \
211 q, context, work, false, true, barrier, 0}; \
212 Release(thr, pc, (uptr)&new_context); \
213 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
214 REAL(name)(q, &new_context, dispatch_callback_wrap); \
215 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
216 Acquire(thr, pc, (uptr)&new_context); \
219 // We wrap dispatch_async, dispatch_sync and friends where we allocate a new
220 // context, which is used to synchronize (we release the context before
221 // submitting, and the callback acquires it before executing the original
222 // callback).
223 DISPATCH_INTERCEPT_B(dispatch_async, false)
224 DISPATCH_INTERCEPT_B(dispatch_barrier_async, true)
225 DISPATCH_INTERCEPT_F(dispatch_async_f, false)
226 DISPATCH_INTERCEPT_F(dispatch_barrier_async_f, true)
227 DISPATCH_INTERCEPT_SYNC_B(dispatch_sync, false)
228 DISPATCH_INTERCEPT_SYNC_B(dispatch_barrier_sync, true)
229 DISPATCH_INTERCEPT_SYNC_F(dispatch_sync_f, false)
230 DISPATCH_INTERCEPT_SYNC_F(dispatch_barrier_sync_f, true)
232 TSAN_INTERCEPTOR(void, dispatch_after, dispatch_time_t when,
233 dispatch_queue_t queue, dispatch_block_t block) {
234 SCOPED_TSAN_INTERCEPTOR(dispatch_after, when, queue, block);
235 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
236 dispatch_block_t heap_block = Block_copy(block);
237 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
238 tsan_block_context_t *new_context =
239 AllocContext(thr, pc, queue, heap_block, &invoke_and_release_block);
240 Release(thr, pc, (uptr)new_context);
241 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
242 REAL(dispatch_after_f)(when, queue, new_context, dispatch_callback_wrap);
243 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
246 TSAN_INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when,
247 dispatch_queue_t queue, void *context,
248 dispatch_function_t work) {
249 SCOPED_TSAN_INTERCEPTOR(dispatch_after_f, when, queue, context, work);
250 WRAP(dispatch_after)(when, queue, ^(void) {
251 work(context);
255 // GCD's dispatch_once implementation has a fast path that contains a racy read
256 // and it's inlined into user's code. Furthermore, this fast path doesn't
257 // establish a proper happens-before relations between the initialization and
258 // code following the call to dispatch_once. We could deal with this in
259 // instrumented code, but there's not much we can do about it in system
260 // libraries. Let's disable the fast path (by never storing the value ~0 to
261 // predicate), so the interceptor is always called, and let's add proper release
262 // and acquire semantics. Since TSan does not see its own atomic stores, the
263 // race on predicate won't be reported - the only accesses to it that TSan sees
264 // are the loads on the fast path. Loads don't race. Secondly, dispatch_once is
265 // both a macro and a real function, we want to intercept the function, so we
266 // need to undefine the macro.
267 #undef dispatch_once
268 TSAN_INTERCEPTOR(void, dispatch_once, dispatch_once_t *predicate,
269 DISPATCH_NOESCAPE dispatch_block_t block) {
270 SCOPED_INTERCEPTOR_RAW(dispatch_once, predicate, block);
271 atomic_uint32_t *a = reinterpret_cast<atomic_uint32_t *>(predicate);
272 u32 v = atomic_load(a, memory_order_acquire);
273 if (v == 0 &&
274 atomic_compare_exchange_strong(a, &v, 1, memory_order_relaxed)) {
275 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
276 block();
277 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
278 Release(thr, pc, (uptr)a);
279 atomic_store(a, 2, memory_order_release);
280 } else {
281 while (v != 2) {
282 internal_sched_yield();
283 v = atomic_load(a, memory_order_acquire);
285 Acquire(thr, pc, (uptr)a);
289 #undef dispatch_once_f
290 TSAN_INTERCEPTOR(void, dispatch_once_f, dispatch_once_t *predicate,
291 void *context, dispatch_function_t function) {
292 SCOPED_INTERCEPTOR_RAW(dispatch_once_f, predicate, context, function);
293 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
294 WRAP(dispatch_once)(predicate, ^(void) {
295 function(context);
297 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
300 TSAN_INTERCEPTOR(long_t, dispatch_semaphore_signal,
301 dispatch_semaphore_t dsema) {
302 SCOPED_TSAN_INTERCEPTOR(dispatch_semaphore_signal, dsema);
303 Release(thr, pc, (uptr)dsema);
304 return REAL(dispatch_semaphore_signal)(dsema);
307 TSAN_INTERCEPTOR(long_t, dispatch_semaphore_wait, dispatch_semaphore_t dsema,
308 dispatch_time_t timeout) {
309 SCOPED_TSAN_INTERCEPTOR(dispatch_semaphore_wait, dsema, timeout);
310 long_t result = REAL(dispatch_semaphore_wait)(dsema, timeout);
311 if (result == 0) Acquire(thr, pc, (uptr)dsema);
312 return result;
315 TSAN_INTERCEPTOR(long_t, dispatch_group_wait, dispatch_group_t group,
316 dispatch_time_t timeout) {
317 SCOPED_TSAN_INTERCEPTOR(dispatch_group_wait, group, timeout);
318 long_t result = REAL(dispatch_group_wait)(group, timeout);
319 if (result == 0) Acquire(thr, pc, (uptr)group);
320 return result;
323 TSAN_INTERCEPTOR(void, dispatch_group_leave, dispatch_group_t group) {
324 SCOPED_TSAN_INTERCEPTOR(dispatch_group_leave, group);
325 // Acquired in the group noticifaction callback in dispatch_group_notify[_f].
326 Release(thr, pc, (uptr)group);
327 REAL(dispatch_group_leave)(group);
330 TSAN_INTERCEPTOR(void, dispatch_group_async, dispatch_group_t group,
331 dispatch_queue_t queue, dispatch_block_t block) {
332 SCOPED_TSAN_INTERCEPTOR(dispatch_group_async, group, queue, block);
333 dispatch_retain(group);
334 dispatch_group_enter(group);
335 __block dispatch_block_t block_copy = (dispatch_block_t)_Block_copy(block);
336 WRAP(dispatch_async)(queue, ^(void) {
337 block_copy();
338 _Block_release(block_copy);
339 WRAP(dispatch_group_leave)(group);
340 dispatch_release(group);
344 TSAN_INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
345 dispatch_queue_t queue, void *context,
346 dispatch_function_t work) {
347 SCOPED_TSAN_INTERCEPTOR(dispatch_group_async_f, group, queue, context, work);
348 dispatch_retain(group);
349 dispatch_group_enter(group);
350 WRAP(dispatch_async)(queue, ^(void) {
351 work(context);
352 WRAP(dispatch_group_leave)(group);
353 dispatch_release(group);
357 TSAN_INTERCEPTOR(void, dispatch_group_notify, dispatch_group_t group,
358 dispatch_queue_t q, dispatch_block_t block) {
359 SCOPED_TSAN_INTERCEPTOR(dispatch_group_notify, group, q, block);
361 // To make sure the group is still available in the callback (otherwise
362 // it can be already destroyed). Will be released in the callback.
363 dispatch_retain(group);
365 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
366 dispatch_block_t heap_block = Block_copy(^(void) {
368 SCOPED_INTERCEPTOR_RAW(dispatch_read_callback);
369 // Released when leaving the group (dispatch_group_leave).
370 Acquire(thr, pc, (uptr)group);
372 dispatch_release(group);
373 block();
375 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
376 tsan_block_context_t *new_context =
377 AllocContext(thr, pc, q, heap_block, &invoke_and_release_block);
378 new_context->is_barrier_block = true;
379 Release(thr, pc, (uptr)new_context);
380 REAL(dispatch_group_notify_f)(group, q, new_context, dispatch_callback_wrap);
383 TSAN_INTERCEPTOR(void, dispatch_group_notify_f, dispatch_group_t group,
384 dispatch_queue_t q, void *context, dispatch_function_t work) {
385 WRAP(dispatch_group_notify)(group, q, ^(void) { work(context); });
388 TSAN_INTERCEPTOR(void, dispatch_source_set_event_handler,
389 dispatch_source_t source, dispatch_block_t handler) {
390 SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_event_handler, source, handler);
391 if (handler == nullptr)
392 return REAL(dispatch_source_set_event_handler)(source, nullptr);
393 dispatch_queue_t q = GetTargetQueueFromSource(source);
394 __block tsan_block_context_t new_context = {
395 q, handler, &invoke_block, false, false, false, 0 };
396 dispatch_block_t new_handler = Block_copy(^(void) {
397 new_context.orig_context = handler; // To explicitly capture "handler".
398 dispatch_callback_wrap(&new_context);
400 uptr submit_sync = (uptr)&new_context;
401 Release(thr, pc, submit_sync);
402 REAL(dispatch_source_set_event_handler)(source, new_handler);
403 Block_release(new_handler);
406 TSAN_INTERCEPTOR(void, dispatch_source_set_event_handler_f,
407 dispatch_source_t source, dispatch_function_t handler) {
408 SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_event_handler_f, source, handler);
409 if (handler == nullptr)
410 return REAL(dispatch_source_set_event_handler)(source, nullptr);
411 dispatch_block_t block = ^(void) {
412 handler(dispatch_get_context(source));
414 WRAP(dispatch_source_set_event_handler)(source, block);
417 TSAN_INTERCEPTOR(void, dispatch_source_set_cancel_handler,
418 dispatch_source_t source, dispatch_block_t handler) {
419 SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_cancel_handler, source, handler);
420 if (handler == nullptr)
421 return REAL(dispatch_source_set_cancel_handler)(source, nullptr);
422 dispatch_queue_t q = GetTargetQueueFromSource(source);
423 __block tsan_block_context_t new_context = {
424 q, handler, &invoke_block, false, false, false, 0};
425 dispatch_block_t new_handler = Block_copy(^(void) {
426 new_context.orig_context = handler; // To explicitly capture "handler".
427 dispatch_callback_wrap(&new_context);
429 uptr submit_sync = (uptr)&new_context;
430 Release(thr, pc, submit_sync);
431 REAL(dispatch_source_set_cancel_handler)(source, new_handler);
432 Block_release(new_handler);
435 TSAN_INTERCEPTOR(void, dispatch_source_set_cancel_handler_f,
436 dispatch_source_t source, dispatch_function_t handler) {
437 SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_cancel_handler_f, source,
438 handler);
439 if (handler == nullptr)
440 return REAL(dispatch_source_set_cancel_handler)(source, nullptr);
441 dispatch_block_t block = ^(void) {
442 handler(dispatch_get_context(source));
444 WRAP(dispatch_source_set_cancel_handler)(source, block);
447 TSAN_INTERCEPTOR(void, dispatch_source_set_registration_handler,
448 dispatch_source_t source, dispatch_block_t handler) {
449 SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_registration_handler, source,
450 handler);
451 if (handler == nullptr)
452 return REAL(dispatch_source_set_registration_handler)(source, nullptr);
453 dispatch_queue_t q = GetTargetQueueFromSource(source);
454 __block tsan_block_context_t new_context = {
455 q, handler, &invoke_block, false, false, false, 0};
456 dispatch_block_t new_handler = Block_copy(^(void) {
457 new_context.orig_context = handler; // To explicitly capture "handler".
458 dispatch_callback_wrap(&new_context);
460 uptr submit_sync = (uptr)&new_context;
461 Release(thr, pc, submit_sync);
462 REAL(dispatch_source_set_registration_handler)(source, new_handler);
463 Block_release(new_handler);
466 TSAN_INTERCEPTOR(void, dispatch_source_set_registration_handler_f,
467 dispatch_source_t source, dispatch_function_t handler) {
468 SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_registration_handler_f, source,
469 handler);
470 if (handler == nullptr)
471 return REAL(dispatch_source_set_registration_handler)(source, nullptr);
472 dispatch_block_t block = ^(void) {
473 handler(dispatch_get_context(source));
475 WRAP(dispatch_source_set_registration_handler)(source, block);
478 TSAN_INTERCEPTOR(void, dispatch_apply, size_t iterations,
479 dispatch_queue_t queue,
480 DISPATCH_NOESCAPE void (^block)(size_t)) {
481 SCOPED_TSAN_INTERCEPTOR(dispatch_apply, iterations, queue, block);
483 void *parent_to_child_sync = nullptr;
484 uptr parent_to_child_sync_uptr = (uptr)&parent_to_child_sync;
485 void *child_to_parent_sync = nullptr;
486 uptr child_to_parent_sync_uptr = (uptr)&child_to_parent_sync;
488 Release(thr, pc, parent_to_child_sync_uptr);
489 void (^new_block)(size_t) = ^(size_t iteration) {
490 SCOPED_INTERCEPTOR_RAW(dispatch_apply);
491 Acquire(thr, pc, parent_to_child_sync_uptr);
492 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
493 block(iteration);
494 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
495 Release(thr, pc, child_to_parent_sync_uptr);
497 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
498 REAL(dispatch_apply)(iterations, queue, new_block);
499 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
500 Acquire(thr, pc, child_to_parent_sync_uptr);
503 TSAN_INTERCEPTOR(void, dispatch_apply_f, size_t iterations,
504 dispatch_queue_t queue, void *context,
505 void (*work)(void *, size_t)) {
506 SCOPED_TSAN_INTERCEPTOR(dispatch_apply_f, iterations, queue, context, work);
507 void (^new_block)(size_t) = ^(size_t iteration) {
508 work(context, iteration);
510 WRAP(dispatch_apply)(iterations, queue, new_block);
513 DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
514 DECLARE_REAL_AND_INTERCEPTOR(int, munmap, void *addr, long_t sz)
516 TSAN_INTERCEPTOR(dispatch_data_t, dispatch_data_create, const void *buffer,
517 size_t size, dispatch_queue_t q, dispatch_block_t destructor) {
518 SCOPED_TSAN_INTERCEPTOR(dispatch_data_create, buffer, size, q, destructor);
519 if ((q == nullptr) || (destructor == DISPATCH_DATA_DESTRUCTOR_DEFAULT))
520 return REAL(dispatch_data_create)(buffer, size, q, destructor);
522 if (destructor == DISPATCH_DATA_DESTRUCTOR_FREE)
523 destructor = ^(void) { WRAP(free)((void *)buffer); };
524 else if (destructor == DISPATCH_DATA_DESTRUCTOR_MUNMAP)
525 destructor = ^(void) { WRAP(munmap)((void *)buffer, size); };
527 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
528 dispatch_block_t heap_block = Block_copy(destructor);
529 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
530 tsan_block_context_t *new_context =
531 AllocContext(thr, pc, q, heap_block, &invoke_and_release_block);
532 uptr submit_sync = (uptr)new_context;
533 Release(thr, pc, submit_sync);
534 return REAL(dispatch_data_create)(buffer, size, q, ^(void) {
535 dispatch_callback_wrap(new_context);
539 typedef void (^fd_handler_t)(dispatch_data_t data, int error);
540 typedef void (^cleanup_handler_t)(int error);
542 TSAN_INTERCEPTOR(void, dispatch_read, dispatch_fd_t fd, size_t length,
543 dispatch_queue_t q, fd_handler_t h) {
544 SCOPED_TSAN_INTERCEPTOR(dispatch_read, fd, length, q, h);
545 __block tsan_block_context_t new_context = {
546 q, nullptr, &invoke_block, false, false, false, 0};
547 fd_handler_t new_h = Block_copy(^(dispatch_data_t data, int error) {
548 new_context.orig_context = ^(void) {
549 h(data, error);
551 dispatch_callback_wrap(&new_context);
553 uptr submit_sync = (uptr)&new_context;
554 Release(thr, pc, submit_sync);
555 REAL(dispatch_read)(fd, length, q, new_h);
556 Block_release(new_h);
559 TSAN_INTERCEPTOR(void, dispatch_write, dispatch_fd_t fd, dispatch_data_t data,
560 dispatch_queue_t q, fd_handler_t h) {
561 SCOPED_TSAN_INTERCEPTOR(dispatch_write, fd, data, q, h);
562 __block tsan_block_context_t new_context = {
563 q, nullptr, &invoke_block, false, false, false, 0};
564 fd_handler_t new_h = Block_copy(^(dispatch_data_t data, int error) {
565 new_context.orig_context = ^(void) {
566 h(data, error);
568 dispatch_callback_wrap(&new_context);
570 uptr submit_sync = (uptr)&new_context;
571 Release(thr, pc, submit_sync);
572 REAL(dispatch_write)(fd, data, q, new_h);
573 Block_release(new_h);
576 TSAN_INTERCEPTOR(void, dispatch_io_read, dispatch_io_t channel, off_t offset,
577 size_t length, dispatch_queue_t q, dispatch_io_handler_t h) {
578 SCOPED_TSAN_INTERCEPTOR(dispatch_io_read, channel, offset, length, q, h);
579 __block tsan_block_context_t new_context = {
580 q, nullptr, &invoke_block, false, false, false, 0};
581 dispatch_io_handler_t new_h =
582 Block_copy(^(bool done, dispatch_data_t data, int error) {
583 new_context.orig_context = ^(void) {
584 h(done, data, error);
586 dispatch_callback_wrap(&new_context);
588 uptr submit_sync = (uptr)&new_context;
589 Release(thr, pc, submit_sync);
590 REAL(dispatch_io_read)(channel, offset, length, q, new_h);
591 Block_release(new_h);
594 TSAN_INTERCEPTOR(void, dispatch_io_write, dispatch_io_t channel, off_t offset,
595 dispatch_data_t data, dispatch_queue_t q,
596 dispatch_io_handler_t h) {
597 SCOPED_TSAN_INTERCEPTOR(dispatch_io_write, channel, offset, data, q, h);
598 __block tsan_block_context_t new_context = {
599 q, nullptr, &invoke_block, false, false, false, 0};
600 dispatch_io_handler_t new_h =
601 Block_copy(^(bool done, dispatch_data_t data, int error) {
602 new_context.orig_context = ^(void) {
603 h(done, data, error);
605 dispatch_callback_wrap(&new_context);
607 uptr submit_sync = (uptr)&new_context;
608 Release(thr, pc, submit_sync);
609 REAL(dispatch_io_write)(channel, offset, data, q, new_h);
610 Block_release(new_h);
613 TSAN_INTERCEPTOR(void, dispatch_io_barrier, dispatch_io_t channel,
614 dispatch_block_t barrier) {
615 SCOPED_TSAN_INTERCEPTOR(dispatch_io_barrier, channel, barrier);
616 __block tsan_block_context_t new_context = {
617 nullptr, nullptr, &invoke_block, false, false, false, 0};
618 new_context.non_queue_sync_object = (uptr)channel;
619 new_context.is_barrier_block = true;
620 dispatch_block_t new_block = Block_copy(^(void) {
621 new_context.orig_context = ^(void) {
622 barrier();
624 dispatch_callback_wrap(&new_context);
626 uptr submit_sync = (uptr)&new_context;
627 Release(thr, pc, submit_sync);
628 REAL(dispatch_io_barrier)(channel, new_block);
629 Block_release(new_block);
632 TSAN_INTERCEPTOR(dispatch_io_t, dispatch_io_create, dispatch_io_type_t type,
633 dispatch_fd_t fd, dispatch_queue_t q, cleanup_handler_t h) {
634 SCOPED_TSAN_INTERCEPTOR(dispatch_io_create, type, fd, q, h);
635 __block dispatch_io_t new_channel = nullptr;
636 __block tsan_block_context_t new_context = {
637 q, nullptr, &invoke_block, false, false, false, 0};
638 cleanup_handler_t new_h = Block_copy(^(int error) {
640 SCOPED_INTERCEPTOR_RAW(dispatch_io_create_callback);
641 Acquire(thr, pc, (uptr)new_channel); // Release() in dispatch_io_close.
643 new_context.orig_context = ^(void) {
644 h(error);
646 dispatch_callback_wrap(&new_context);
648 uptr submit_sync = (uptr)&new_context;
649 Release(thr, pc, submit_sync);
650 new_channel = REAL(dispatch_io_create)(type, fd, q, new_h);
651 Block_release(new_h);
652 return new_channel;
655 TSAN_INTERCEPTOR(dispatch_io_t, dispatch_io_create_with_path,
656 dispatch_io_type_t type, const char *path, int oflag,
657 mode_t mode, dispatch_queue_t q, cleanup_handler_t h) {
658 SCOPED_TSAN_INTERCEPTOR(dispatch_io_create_with_path, type, path, oflag, mode,
659 q, h);
660 __block dispatch_io_t new_channel = nullptr;
661 __block tsan_block_context_t new_context = {
662 q, nullptr, &invoke_block, false, false, false, 0};
663 cleanup_handler_t new_h = Block_copy(^(int error) {
665 SCOPED_INTERCEPTOR_RAW(dispatch_io_create_callback);
666 Acquire(thr, pc, (uptr)new_channel); // Release() in dispatch_io_close.
668 new_context.orig_context = ^(void) {
669 h(error);
671 dispatch_callback_wrap(&new_context);
673 uptr submit_sync = (uptr)&new_context;
674 Release(thr, pc, submit_sync);
675 new_channel =
676 REAL(dispatch_io_create_with_path)(type, path, oflag, mode, q, new_h);
677 Block_release(new_h);
678 return new_channel;
681 TSAN_INTERCEPTOR(dispatch_io_t, dispatch_io_create_with_io,
682 dispatch_io_type_t type, dispatch_io_t io, dispatch_queue_t q,
683 cleanup_handler_t h) {
684 SCOPED_TSAN_INTERCEPTOR(dispatch_io_create_with_io, type, io, q, h);
685 __block dispatch_io_t new_channel = nullptr;
686 __block tsan_block_context_t new_context = {
687 q, nullptr, &invoke_block, false, false, false, 0};
688 cleanup_handler_t new_h = Block_copy(^(int error) {
690 SCOPED_INTERCEPTOR_RAW(dispatch_io_create_callback);
691 Acquire(thr, pc, (uptr)new_channel); // Release() in dispatch_io_close.
693 new_context.orig_context = ^(void) {
694 h(error);
696 dispatch_callback_wrap(&new_context);
698 uptr submit_sync = (uptr)&new_context;
699 Release(thr, pc, submit_sync);
700 new_channel = REAL(dispatch_io_create_with_io)(type, io, q, new_h);
701 Block_release(new_h);
702 return new_channel;
705 TSAN_INTERCEPTOR(void, dispatch_io_close, dispatch_io_t channel,
706 dispatch_io_close_flags_t flags) {
707 SCOPED_TSAN_INTERCEPTOR(dispatch_io_close, channel, flags);
708 Release(thr, pc, (uptr)channel); // Acquire() in dispatch_io_create[_*].
709 return REAL(dispatch_io_close)(channel, flags);
712 // Resuming a suspended queue needs to synchronize with all subsequent
713 // executions of blocks in that queue.
714 TSAN_INTERCEPTOR(void, dispatch_resume, dispatch_object_t o) {
715 SCOPED_TSAN_INTERCEPTOR(dispatch_resume, o);
716 Release(thr, pc, (uptr)o); // Synchronizes with the Acquire() on serial_sync
717 // in dispatch_sync_pre_execute
718 return REAL(dispatch_resume)(o);
721 } // namespace __tsan
723 #endif // SANITIZER_MAC