xref: /openbsd-src/gnu/llvm/compiler-rt/lib/tsan/rtl/tsan_interceptors_libdispatch.cpp (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- tsan_interceptors_libdispatch.cpp ---------------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is a part of ThreadSanitizer (TSan), a race detector.
103cab2bb3Spatrick //
113cab2bb3Spatrick // Support for intercepting libdispatch (GCD).
123cab2bb3Spatrick //===----------------------------------------------------------------------===//
133cab2bb3Spatrick 
143cab2bb3Spatrick #include "sanitizer_common/sanitizer_common.h"
153cab2bb3Spatrick #include "interception/interception.h"
163cab2bb3Spatrick #include "tsan_interceptors.h"
173cab2bb3Spatrick #include "tsan_rtl.h"
183cab2bb3Spatrick 
193cab2bb3Spatrick #include "BlocksRuntime/Block.h"
203cab2bb3Spatrick #include "tsan_dispatch_defs.h"
213cab2bb3Spatrick 
22*810390e3Srobert #if SANITIZER_APPLE
23d89ec533Spatrick # include <Availability.h>
24d89ec533Spatrick #endif
25d89ec533Spatrick 
263cab2bb3Spatrick namespace __tsan {
273cab2bb3Spatrick   typedef u16 uint16_t;
283cab2bb3Spatrick 
293cab2bb3Spatrick typedef struct {
303cab2bb3Spatrick   dispatch_queue_t queue;
313cab2bb3Spatrick   void *orig_context;
323cab2bb3Spatrick   dispatch_function_t orig_work;
333cab2bb3Spatrick   bool free_context_in_callback;
343cab2bb3Spatrick   bool submitted_synchronously;
353cab2bb3Spatrick   bool is_barrier_block;
363cab2bb3Spatrick   uptr non_queue_sync_object;
373cab2bb3Spatrick } block_context_t;
383cab2bb3Spatrick 
393cab2bb3Spatrick // The offsets of different fields of the dispatch_queue_t structure, exported
403cab2bb3Spatrick // by libdispatch.dylib.
413cab2bb3Spatrick extern "C" struct dispatch_queue_offsets_s {
423cab2bb3Spatrick   const uint16_t dqo_version;
433cab2bb3Spatrick   const uint16_t dqo_label;
443cab2bb3Spatrick   const uint16_t dqo_label_size;
453cab2bb3Spatrick   const uint16_t dqo_flags;
463cab2bb3Spatrick   const uint16_t dqo_flags_size;
473cab2bb3Spatrick   const uint16_t dqo_serialnum;
483cab2bb3Spatrick   const uint16_t dqo_serialnum_size;
493cab2bb3Spatrick   const uint16_t dqo_width;
503cab2bb3Spatrick   const uint16_t dqo_width_size;
513cab2bb3Spatrick   const uint16_t dqo_running;
523cab2bb3Spatrick   const uint16_t dqo_running_size;
533cab2bb3Spatrick   const uint16_t dqo_suspend_cnt;
543cab2bb3Spatrick   const uint16_t dqo_suspend_cnt_size;
553cab2bb3Spatrick   const uint16_t dqo_target_queue;
563cab2bb3Spatrick   const uint16_t dqo_target_queue_size;
573cab2bb3Spatrick   const uint16_t dqo_priority;
583cab2bb3Spatrick   const uint16_t dqo_priority_size;
593cab2bb3Spatrick } dispatch_queue_offsets;
603cab2bb3Spatrick 
IsQueueSerial(dispatch_queue_t q)613cab2bb3Spatrick static bool IsQueueSerial(dispatch_queue_t q) {
623cab2bb3Spatrick   CHECK_EQ(dispatch_queue_offsets.dqo_width_size, 2);
633cab2bb3Spatrick   uptr width = *(uint16_t *)(((uptr)q) + dispatch_queue_offsets.dqo_width);
643cab2bb3Spatrick   CHECK_NE(width, 0);
653cab2bb3Spatrick   return width == 1;
663cab2bb3Spatrick }
673cab2bb3Spatrick 
GetTargetQueueFromQueue(dispatch_queue_t q)683cab2bb3Spatrick static dispatch_queue_t GetTargetQueueFromQueue(dispatch_queue_t q) {
693cab2bb3Spatrick   CHECK_EQ(dispatch_queue_offsets.dqo_target_queue_size, 8);
703cab2bb3Spatrick   dispatch_queue_t tq = *(
713cab2bb3Spatrick       dispatch_queue_t *)(((uptr)q) + dispatch_queue_offsets.dqo_target_queue);
723cab2bb3Spatrick   return tq;
733cab2bb3Spatrick }
743cab2bb3Spatrick 
GetTargetQueueFromSource(dispatch_source_t source)753cab2bb3Spatrick static dispatch_queue_t GetTargetQueueFromSource(dispatch_source_t source) {
763cab2bb3Spatrick   dispatch_queue_t tq = GetTargetQueueFromQueue((dispatch_queue_t)source);
773cab2bb3Spatrick   CHECK_NE(tq, 0);
783cab2bb3Spatrick   return tq;
793cab2bb3Spatrick }
803cab2bb3Spatrick 
AllocContext(ThreadState * thr,uptr pc,dispatch_queue_t queue,void * orig_context,dispatch_function_t orig_work)813cab2bb3Spatrick static block_context_t *AllocContext(ThreadState *thr, uptr pc,
823cab2bb3Spatrick                                      dispatch_queue_t queue, void *orig_context,
833cab2bb3Spatrick                                      dispatch_function_t orig_work) {
843cab2bb3Spatrick   block_context_t *new_context =
853cab2bb3Spatrick       (block_context_t *)user_alloc_internal(thr, pc, sizeof(block_context_t));
863cab2bb3Spatrick   new_context->queue = queue;
873cab2bb3Spatrick   new_context->orig_context = orig_context;
883cab2bb3Spatrick   new_context->orig_work = orig_work;
893cab2bb3Spatrick   new_context->free_context_in_callback = true;
903cab2bb3Spatrick   new_context->submitted_synchronously = false;
913cab2bb3Spatrick   new_context->is_barrier_block = false;
923cab2bb3Spatrick   new_context->non_queue_sync_object = 0;
933cab2bb3Spatrick   return new_context;
943cab2bb3Spatrick }
953cab2bb3Spatrick 
963cab2bb3Spatrick #define GET_QUEUE_SYNC_VARS(context, q)                                  \
973cab2bb3Spatrick   bool is_queue_serial = q && IsQueueSerial(q);                          \
983cab2bb3Spatrick   uptr sync_ptr = (uptr)q ?: context->non_queue_sync_object;             \
993cab2bb3Spatrick   uptr serial_sync = (uptr)sync_ptr;                                     \
1003cab2bb3Spatrick   uptr concurrent_sync = sync_ptr ? ((uptr)sync_ptr) + sizeof(uptr) : 0; \
1013cab2bb3Spatrick   bool serial_task = context->is_barrier_block || is_queue_serial
1023cab2bb3Spatrick 
dispatch_sync_pre_execute(ThreadState * thr,uptr pc,block_context_t * context)1033cab2bb3Spatrick static void dispatch_sync_pre_execute(ThreadState *thr, uptr pc,
1043cab2bb3Spatrick                                       block_context_t *context) {
1053cab2bb3Spatrick   uptr submit_sync = (uptr)context;
1063cab2bb3Spatrick   Acquire(thr, pc, submit_sync);
1073cab2bb3Spatrick 
1083cab2bb3Spatrick   dispatch_queue_t q = context->queue;
1093cab2bb3Spatrick   do {
1103cab2bb3Spatrick     GET_QUEUE_SYNC_VARS(context, q);
1113cab2bb3Spatrick     if (serial_sync) Acquire(thr, pc, serial_sync);
1123cab2bb3Spatrick     if (serial_task && concurrent_sync) Acquire(thr, pc, concurrent_sync);
1133cab2bb3Spatrick 
1143cab2bb3Spatrick     if (q) q = GetTargetQueueFromQueue(q);
1153cab2bb3Spatrick   } while (q);
1163cab2bb3Spatrick }
1173cab2bb3Spatrick 
dispatch_sync_post_execute(ThreadState * thr,uptr pc,block_context_t * context)1183cab2bb3Spatrick static void dispatch_sync_post_execute(ThreadState *thr, uptr pc,
1193cab2bb3Spatrick                                        block_context_t *context) {
1203cab2bb3Spatrick   uptr submit_sync = (uptr)context;
1213cab2bb3Spatrick   if (context->submitted_synchronously) Release(thr, pc, submit_sync);
1223cab2bb3Spatrick 
1233cab2bb3Spatrick   dispatch_queue_t q = context->queue;
1243cab2bb3Spatrick   do {
1253cab2bb3Spatrick     GET_QUEUE_SYNC_VARS(context, q);
1263cab2bb3Spatrick     if (serial_task && serial_sync) Release(thr, pc, serial_sync);
1273cab2bb3Spatrick     if (!serial_task && concurrent_sync) Release(thr, pc, concurrent_sync);
1283cab2bb3Spatrick 
1293cab2bb3Spatrick     if (q) q = GetTargetQueueFromQueue(q);
1303cab2bb3Spatrick   } while (q);
1313cab2bb3Spatrick }
1323cab2bb3Spatrick 
dispatch_callback_wrap(void * param)1333cab2bb3Spatrick static void dispatch_callback_wrap(void *param) {
1343cab2bb3Spatrick   SCOPED_INTERCEPTOR_RAW(dispatch_callback_wrap);
1353cab2bb3Spatrick   block_context_t *context = (block_context_t *)param;
1363cab2bb3Spatrick 
1373cab2bb3Spatrick   dispatch_sync_pre_execute(thr, pc, context);
1383cab2bb3Spatrick 
1393cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
1403cab2bb3Spatrick   context->orig_work(context->orig_context);
1413cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
1423cab2bb3Spatrick 
1433cab2bb3Spatrick   dispatch_sync_post_execute(thr, pc, context);
1443cab2bb3Spatrick 
1453cab2bb3Spatrick   if (context->free_context_in_callback) user_free(thr, pc, context);
1463cab2bb3Spatrick }
1473cab2bb3Spatrick 
invoke_block(void * param)1483cab2bb3Spatrick static void invoke_block(void *param) {
1493cab2bb3Spatrick   dispatch_block_t block = (dispatch_block_t)param;
1503cab2bb3Spatrick   block();
1513cab2bb3Spatrick }
1523cab2bb3Spatrick 
invoke_and_release_block(void * param)1533cab2bb3Spatrick static void invoke_and_release_block(void *param) {
1543cab2bb3Spatrick   dispatch_block_t block = (dispatch_block_t)param;
1553cab2bb3Spatrick   block();
1563cab2bb3Spatrick   Block_release(block);
1573cab2bb3Spatrick }
1583cab2bb3Spatrick 
1593cab2bb3Spatrick #define DISPATCH_INTERCEPT_ASYNC_B(name, barrier)                            \
1603cab2bb3Spatrick   TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, dispatch_block_t block) { \
1613cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR(name, q, block);                                 \
1623cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();                           \
1633cab2bb3Spatrick     dispatch_block_t heap_block = Block_copy(block);                         \
1643cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();                             \
1653cab2bb3Spatrick     block_context_t *new_context =                                           \
1663cab2bb3Spatrick         AllocContext(thr, pc, q, heap_block, &invoke_and_release_block);     \
1673cab2bb3Spatrick     new_context->is_barrier_block = barrier;                                 \
1683cab2bb3Spatrick     Release(thr, pc, (uptr)new_context);                                     \
1693cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();                           \
1703cab2bb3Spatrick     REAL(name##_f)(q, new_context, dispatch_callback_wrap);                  \
1713cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();                             \
1723cab2bb3Spatrick   }
1733cab2bb3Spatrick 
1743cab2bb3Spatrick #define DISPATCH_INTERCEPT_SYNC_B(name, barrier)                             \
1753cab2bb3Spatrick   TSAN_INTERCEPTOR(void, name, dispatch_queue_t q,                           \
1763cab2bb3Spatrick                    DISPATCH_NOESCAPE dispatch_block_t block) {               \
1773cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR(name, q, block);                                 \
1783cab2bb3Spatrick     block_context_t new_context = {                                          \
1793cab2bb3Spatrick         q, block, &invoke_block, false, true, barrier, 0};                   \
1803cab2bb3Spatrick     Release(thr, pc, (uptr)&new_context);                                    \
1813cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();                           \
1823cab2bb3Spatrick     REAL(name##_f)(q, &new_context, dispatch_callback_wrap);                 \
1833cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();                             \
1843cab2bb3Spatrick     Acquire(thr, pc, (uptr)&new_context);                                    \
1853cab2bb3Spatrick   }
1863cab2bb3Spatrick 
1873cab2bb3Spatrick #define DISPATCH_INTERCEPT_ASYNC_F(name, barrier)                 \
1883cab2bb3Spatrick   TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, void *context, \
1893cab2bb3Spatrick                    dispatch_function_t work) {                    \
1903cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR(name, q, context, work);              \
1913cab2bb3Spatrick     block_context_t *new_context =                                \
1923cab2bb3Spatrick         AllocContext(thr, pc, q, context, work);                  \
1933cab2bb3Spatrick     new_context->is_barrier_block = barrier;                      \
1943cab2bb3Spatrick     Release(thr, pc, (uptr)new_context);                          \
1953cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();                \
1963cab2bb3Spatrick     REAL(name)(q, new_context, dispatch_callback_wrap);           \
1973cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();                  \
1983cab2bb3Spatrick   }
1993cab2bb3Spatrick 
2003cab2bb3Spatrick #define DISPATCH_INTERCEPT_SYNC_F(name, barrier)                              \
2013cab2bb3Spatrick   TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, void *context,             \
2023cab2bb3Spatrick                    dispatch_function_t work) {                                \
2033cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR(name, q, context, work);                          \
2043cab2bb3Spatrick     block_context_t new_context = {                                           \
2053cab2bb3Spatrick         q, context, work, false, true, barrier, 0};                           \
2063cab2bb3Spatrick     Release(thr, pc, (uptr)&new_context);                                     \
2073cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();                            \
2083cab2bb3Spatrick     REAL(name)(q, &new_context, dispatch_callback_wrap);                      \
2093cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();                              \
2103cab2bb3Spatrick     Acquire(thr, pc, (uptr)&new_context);                                     \
2113cab2bb3Spatrick   }
2123cab2bb3Spatrick 
2133cab2bb3Spatrick #define DISPATCH_INTERCEPT(name, barrier)             \
2143cab2bb3Spatrick   DISPATCH_INTERCEPT_ASYNC_F(name##_async_f, barrier) \
2153cab2bb3Spatrick   DISPATCH_INTERCEPT_ASYNC_B(name##_async, barrier)   \
2163cab2bb3Spatrick   DISPATCH_INTERCEPT_SYNC_F(name##_sync_f, barrier)   \
2173cab2bb3Spatrick   DISPATCH_INTERCEPT_SYNC_B(name##_sync, barrier)
2183cab2bb3Spatrick 
2193cab2bb3Spatrick // We wrap dispatch_async, dispatch_sync and friends where we allocate a new
2203cab2bb3Spatrick // context, which is used to synchronize (we release the context before
2213cab2bb3Spatrick // submitting, and the callback acquires it before executing the original
2223cab2bb3Spatrick // callback).
2233cab2bb3Spatrick DISPATCH_INTERCEPT(dispatch, false)
2243cab2bb3Spatrick DISPATCH_INTERCEPT(dispatch_barrier, true)
2253cab2bb3Spatrick 
226d89ec533Spatrick // dispatch_async_and_wait() and friends were introduced in macOS 10.14.
227d89ec533Spatrick // Linking of these interceptors fails when using an older SDK.
228*810390e3Srobert #if !SANITIZER_APPLE || defined(__MAC_10_14)
229d89ec533Spatrick // macOS 10.14 is greater than our minimal deployment target.  To ensure we
230d89ec533Spatrick // generate a weak reference so the TSan dylib continues to work on older
231d89ec533Spatrick // systems, we need to forward declare the intercepted functions as "weak
232d89ec533Spatrick // imports".   Note that this file is multi-platform, so we cannot include the
233d89ec533Spatrick // actual header file (#include <dispatch/dispatch.h>).
234d89ec533Spatrick SANITIZER_WEAK_IMPORT void dispatch_async_and_wait(
235d89ec533Spatrick     dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
236d89ec533Spatrick SANITIZER_WEAK_IMPORT void dispatch_async_and_wait_f(
237d89ec533Spatrick     dispatch_queue_t queue, void *context, dispatch_function_t work);
238d89ec533Spatrick SANITIZER_WEAK_IMPORT void dispatch_barrier_async_and_wait(
239d89ec533Spatrick     dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
240d89ec533Spatrick SANITIZER_WEAK_IMPORT void dispatch_barrier_async_and_wait_f(
241d89ec533Spatrick     dispatch_queue_t queue, void *context, dispatch_function_t work);
242d89ec533Spatrick 
DISPATCH_INTERCEPT_SYNC_F(dispatch_async_and_wait_f,false)243d89ec533Spatrick DISPATCH_INTERCEPT_SYNC_F(dispatch_async_and_wait_f, false)
244d89ec533Spatrick DISPATCH_INTERCEPT_SYNC_B(dispatch_async_and_wait, false)
245d89ec533Spatrick DISPATCH_INTERCEPT_SYNC_F(dispatch_barrier_async_and_wait_f, true)
246d89ec533Spatrick DISPATCH_INTERCEPT_SYNC_B(dispatch_barrier_async_and_wait, true)
247d89ec533Spatrick #endif
248d89ec533Spatrick 
249d89ec533Spatrick 
2503cab2bb3Spatrick DECLARE_REAL(void, dispatch_after_f, dispatch_time_t when,
2513cab2bb3Spatrick              dispatch_queue_t queue, void *context, dispatch_function_t work)
2523cab2bb3Spatrick 
2533cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_after, dispatch_time_t when,
2543cab2bb3Spatrick                  dispatch_queue_t queue, dispatch_block_t block) {
2553cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_after, when, queue, block);
2563cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
2573cab2bb3Spatrick   dispatch_block_t heap_block = Block_copy(block);
2583cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
2593cab2bb3Spatrick   block_context_t *new_context =
2603cab2bb3Spatrick       AllocContext(thr, pc, queue, heap_block, &invoke_and_release_block);
2613cab2bb3Spatrick   Release(thr, pc, (uptr)new_context);
2623cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
2633cab2bb3Spatrick   REAL(dispatch_after_f)(when, queue, new_context, dispatch_callback_wrap);
2643cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
2653cab2bb3Spatrick }
2663cab2bb3Spatrick 
TSAN_INTERCEPTOR(void,dispatch_after_f,dispatch_time_t when,dispatch_queue_t queue,void * context,dispatch_function_t work)2673cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when,
2683cab2bb3Spatrick                  dispatch_queue_t queue, void *context,
2693cab2bb3Spatrick                  dispatch_function_t work) {
2703cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_after_f, when, queue, context, work);
2713cab2bb3Spatrick   WRAP(dispatch_after)(when, queue, ^(void) {
2723cab2bb3Spatrick     work(context);
2733cab2bb3Spatrick   });
2743cab2bb3Spatrick }
2753cab2bb3Spatrick 
2763cab2bb3Spatrick // GCD's dispatch_once implementation has a fast path that contains a racy read
2773cab2bb3Spatrick // and it's inlined into user's code. Furthermore, this fast path doesn't
2783cab2bb3Spatrick // establish a proper happens-before relations between the initialization and
2793cab2bb3Spatrick // code following the call to dispatch_once. We could deal with this in
2803cab2bb3Spatrick // instrumented code, but there's not much we can do about it in system
2813cab2bb3Spatrick // libraries. Let's disable the fast path (by never storing the value ~0 to
2823cab2bb3Spatrick // predicate), so the interceptor is always called, and let's add proper release
2833cab2bb3Spatrick // and acquire semantics. Since TSan does not see its own atomic stores, the
2843cab2bb3Spatrick // race on predicate won't be reported - the only accesses to it that TSan sees
2853cab2bb3Spatrick // are the loads on the fast path. Loads don't race. Secondly, dispatch_once is
2863cab2bb3Spatrick // both a macro and a real function, we want to intercept the function, so we
2873cab2bb3Spatrick // need to undefine the macro.
2883cab2bb3Spatrick #undef dispatch_once
TSAN_INTERCEPTOR(void,dispatch_once,dispatch_once_t * predicate,DISPATCH_NOESCAPE dispatch_block_t block)2893cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_once, dispatch_once_t *predicate,
2903cab2bb3Spatrick                  DISPATCH_NOESCAPE dispatch_block_t block) {
2913cab2bb3Spatrick   SCOPED_INTERCEPTOR_RAW(dispatch_once, predicate, block);
2923cab2bb3Spatrick   atomic_uint32_t *a = reinterpret_cast<atomic_uint32_t *>(predicate);
2933cab2bb3Spatrick   u32 v = atomic_load(a, memory_order_acquire);
2943cab2bb3Spatrick   if (v == 0 &&
2953cab2bb3Spatrick       atomic_compare_exchange_strong(a, &v, 1, memory_order_relaxed)) {
2963cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
2973cab2bb3Spatrick     block();
2983cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
2993cab2bb3Spatrick     Release(thr, pc, (uptr)a);
3003cab2bb3Spatrick     atomic_store(a, 2, memory_order_release);
3013cab2bb3Spatrick   } else {
3023cab2bb3Spatrick     while (v != 2) {
3033cab2bb3Spatrick       internal_sched_yield();
3043cab2bb3Spatrick       v = atomic_load(a, memory_order_acquire);
3053cab2bb3Spatrick     }
3063cab2bb3Spatrick     Acquire(thr, pc, (uptr)a);
3073cab2bb3Spatrick   }
3083cab2bb3Spatrick }
3093cab2bb3Spatrick 
3103cab2bb3Spatrick #undef dispatch_once_f
TSAN_INTERCEPTOR(void,dispatch_once_f,dispatch_once_t * predicate,void * context,dispatch_function_t function)3113cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_once_f, dispatch_once_t *predicate,
3123cab2bb3Spatrick                  void *context, dispatch_function_t function) {
3133cab2bb3Spatrick   SCOPED_INTERCEPTOR_RAW(dispatch_once_f, predicate, context, function);
3143cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
3153cab2bb3Spatrick   WRAP(dispatch_once)(predicate, ^(void) {
3163cab2bb3Spatrick     function(context);
3173cab2bb3Spatrick   });
3183cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
3193cab2bb3Spatrick }
3203cab2bb3Spatrick 
TSAN_INTERCEPTOR(long_t,dispatch_semaphore_signal,dispatch_semaphore_t dsema)3213cab2bb3Spatrick TSAN_INTERCEPTOR(long_t, dispatch_semaphore_signal,
3223cab2bb3Spatrick                  dispatch_semaphore_t dsema) {
3233cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_semaphore_signal, dsema);
3243cab2bb3Spatrick   Release(thr, pc, (uptr)dsema);
3253cab2bb3Spatrick   return REAL(dispatch_semaphore_signal)(dsema);
3263cab2bb3Spatrick }
3273cab2bb3Spatrick 
TSAN_INTERCEPTOR(long_t,dispatch_semaphore_wait,dispatch_semaphore_t dsema,dispatch_time_t timeout)3283cab2bb3Spatrick TSAN_INTERCEPTOR(long_t, dispatch_semaphore_wait, dispatch_semaphore_t dsema,
3293cab2bb3Spatrick                  dispatch_time_t timeout) {
3303cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_semaphore_wait, dsema, timeout);
3313cab2bb3Spatrick   long_t result = REAL(dispatch_semaphore_wait)(dsema, timeout);
3323cab2bb3Spatrick   if (result == 0) Acquire(thr, pc, (uptr)dsema);
3333cab2bb3Spatrick   return result;
3343cab2bb3Spatrick }
3353cab2bb3Spatrick 
TSAN_INTERCEPTOR(long_t,dispatch_group_wait,dispatch_group_t group,dispatch_time_t timeout)3363cab2bb3Spatrick TSAN_INTERCEPTOR(long_t, dispatch_group_wait, dispatch_group_t group,
3373cab2bb3Spatrick                  dispatch_time_t timeout) {
3383cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_group_wait, group, timeout);
3393cab2bb3Spatrick   long_t result = REAL(dispatch_group_wait)(group, timeout);
3403cab2bb3Spatrick   if (result == 0) Acquire(thr, pc, (uptr)group);
3413cab2bb3Spatrick   return result;
3423cab2bb3Spatrick }
3433cab2bb3Spatrick 
3443cab2bb3Spatrick // Used, but not intercepted.
3453cab2bb3Spatrick extern "C" void dispatch_group_enter(dispatch_group_t group);
3463cab2bb3Spatrick 
TSAN_INTERCEPTOR(void,dispatch_group_leave,dispatch_group_t group)3473cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_group_leave, dispatch_group_t group) {
3483cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_group_leave, group);
3493cab2bb3Spatrick   // Acquired in the group notification callback in dispatch_group_notify[_f].
3503cab2bb3Spatrick   Release(thr, pc, (uptr)group);
3513cab2bb3Spatrick   REAL(dispatch_group_leave)(group);
3523cab2bb3Spatrick }
3533cab2bb3Spatrick 
TSAN_INTERCEPTOR(void,dispatch_group_async,dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block)3543cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_group_async, dispatch_group_t group,
3553cab2bb3Spatrick                  dispatch_queue_t queue, dispatch_block_t block) {
3563cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_group_async, group, queue, block);
3573cab2bb3Spatrick   dispatch_retain(group);
3583cab2bb3Spatrick   dispatch_group_enter(group);
3593cab2bb3Spatrick   __block dispatch_block_t block_copy = (dispatch_block_t)Block_copy(block);
3603cab2bb3Spatrick   WRAP(dispatch_async)(queue, ^(void) {
3613cab2bb3Spatrick     block_copy();
3623cab2bb3Spatrick     Block_release(block_copy);
3633cab2bb3Spatrick     WRAP(dispatch_group_leave)(group);
3643cab2bb3Spatrick     dispatch_release(group);
3653cab2bb3Spatrick   });
3663cab2bb3Spatrick }
3673cab2bb3Spatrick 
TSAN_INTERCEPTOR(void,dispatch_group_async_f,dispatch_group_t group,dispatch_queue_t queue,void * context,dispatch_function_t work)3683cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
3693cab2bb3Spatrick                  dispatch_queue_t queue, void *context,
3703cab2bb3Spatrick                  dispatch_function_t work) {
3713cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_group_async_f, group, queue, context, work);
3723cab2bb3Spatrick   dispatch_retain(group);
3733cab2bb3Spatrick   dispatch_group_enter(group);
3743cab2bb3Spatrick   WRAP(dispatch_async)(queue, ^(void) {
3753cab2bb3Spatrick     work(context);
3763cab2bb3Spatrick     WRAP(dispatch_group_leave)(group);
3773cab2bb3Spatrick     dispatch_release(group);
3783cab2bb3Spatrick   });
3793cab2bb3Spatrick }
3803cab2bb3Spatrick 
DECLARE_REAL(void,dispatch_group_notify_f,dispatch_group_t group,dispatch_queue_t q,void * context,dispatch_function_t work)3813cab2bb3Spatrick DECLARE_REAL(void, dispatch_group_notify_f, dispatch_group_t group,
3823cab2bb3Spatrick              dispatch_queue_t q, void *context, dispatch_function_t work)
3833cab2bb3Spatrick 
3843cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_group_notify, dispatch_group_t group,
3853cab2bb3Spatrick                  dispatch_queue_t q, dispatch_block_t block) {
3863cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_group_notify, group, q, block);
3873cab2bb3Spatrick 
3883cab2bb3Spatrick   // To make sure the group is still available in the callback (otherwise
3893cab2bb3Spatrick   // it can be already destroyed).  Will be released in the callback.
3903cab2bb3Spatrick   dispatch_retain(group);
3913cab2bb3Spatrick 
3923cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
3933cab2bb3Spatrick   dispatch_block_t heap_block = Block_copy(^(void) {
3943cab2bb3Spatrick     {
3953cab2bb3Spatrick       SCOPED_INTERCEPTOR_RAW(dispatch_read_callback);
3963cab2bb3Spatrick       // Released when leaving the group (dispatch_group_leave).
3973cab2bb3Spatrick       Acquire(thr, pc, (uptr)group);
3983cab2bb3Spatrick     }
3993cab2bb3Spatrick     dispatch_release(group);
4003cab2bb3Spatrick     block();
4013cab2bb3Spatrick   });
4023cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
4033cab2bb3Spatrick   block_context_t *new_context =
4043cab2bb3Spatrick       AllocContext(thr, pc, q, heap_block, &invoke_and_release_block);
4053cab2bb3Spatrick   new_context->is_barrier_block = true;
4063cab2bb3Spatrick   Release(thr, pc, (uptr)new_context);
4073cab2bb3Spatrick   REAL(dispatch_group_notify_f)(group, q, new_context, dispatch_callback_wrap);
4083cab2bb3Spatrick }
4093cab2bb3Spatrick 
TSAN_INTERCEPTOR(void,dispatch_group_notify_f,dispatch_group_t group,dispatch_queue_t q,void * context,dispatch_function_t work)4103cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_group_notify_f, dispatch_group_t group,
4113cab2bb3Spatrick                  dispatch_queue_t q, void *context, dispatch_function_t work) {
4123cab2bb3Spatrick   WRAP(dispatch_group_notify)(group, q, ^(void) { work(context); });
4133cab2bb3Spatrick }
4143cab2bb3Spatrick 
TSAN_INTERCEPTOR(void,dispatch_source_set_event_handler,dispatch_source_t source,dispatch_block_t handler)4153cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_source_set_event_handler,
4163cab2bb3Spatrick                  dispatch_source_t source, dispatch_block_t handler) {
4173cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_event_handler, source, handler);
4183cab2bb3Spatrick   if (handler == nullptr)
4193cab2bb3Spatrick     return REAL(dispatch_source_set_event_handler)(source, nullptr);
4203cab2bb3Spatrick   dispatch_queue_t q = GetTargetQueueFromSource(source);
4213cab2bb3Spatrick   __block block_context_t new_context = {
4223cab2bb3Spatrick       q, handler, &invoke_block, false, false, false, 0 };
4233cab2bb3Spatrick   dispatch_block_t new_handler = Block_copy(^(void) {
4243cab2bb3Spatrick     new_context.orig_context = handler;  // To explicitly capture "handler".
4253cab2bb3Spatrick     dispatch_callback_wrap(&new_context);
4263cab2bb3Spatrick   });
4273cab2bb3Spatrick   uptr submit_sync = (uptr)&new_context;
4283cab2bb3Spatrick   Release(thr, pc, submit_sync);
4293cab2bb3Spatrick   REAL(dispatch_source_set_event_handler)(source, new_handler);
4303cab2bb3Spatrick   Block_release(new_handler);
4313cab2bb3Spatrick }
4323cab2bb3Spatrick 
TSAN_INTERCEPTOR(void,dispatch_source_set_event_handler_f,dispatch_source_t source,dispatch_function_t handler)4333cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_source_set_event_handler_f,
4343cab2bb3Spatrick                  dispatch_source_t source, dispatch_function_t handler) {
4353cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_event_handler_f, source, handler);
4363cab2bb3Spatrick   if (handler == nullptr)
4373cab2bb3Spatrick     return REAL(dispatch_source_set_event_handler)(source, nullptr);
4383cab2bb3Spatrick   dispatch_block_t block = ^(void) {
4393cab2bb3Spatrick     handler(dispatch_get_context(source));
4403cab2bb3Spatrick   };
4413cab2bb3Spatrick   WRAP(dispatch_source_set_event_handler)(source, block);
4423cab2bb3Spatrick }
4433cab2bb3Spatrick 
TSAN_INTERCEPTOR(void,dispatch_source_set_cancel_handler,dispatch_source_t source,dispatch_block_t handler)4443cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_source_set_cancel_handler,
4453cab2bb3Spatrick                  dispatch_source_t source, dispatch_block_t handler) {
4463cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_cancel_handler, source, handler);
4473cab2bb3Spatrick   if (handler == nullptr)
4483cab2bb3Spatrick     return REAL(dispatch_source_set_cancel_handler)(source, nullptr);
4493cab2bb3Spatrick   dispatch_queue_t q = GetTargetQueueFromSource(source);
4503cab2bb3Spatrick   __block block_context_t new_context = {
4513cab2bb3Spatrick       q, handler, &invoke_block, false, false, false, 0};
4523cab2bb3Spatrick   dispatch_block_t new_handler = Block_copy(^(void) {
4533cab2bb3Spatrick     new_context.orig_context = handler;  // To explicitly capture "handler".
4543cab2bb3Spatrick     dispatch_callback_wrap(&new_context);
4553cab2bb3Spatrick   });
4563cab2bb3Spatrick   uptr submit_sync = (uptr)&new_context;
4573cab2bb3Spatrick   Release(thr, pc, submit_sync);
4583cab2bb3Spatrick   REAL(dispatch_source_set_cancel_handler)(source, new_handler);
4593cab2bb3Spatrick   Block_release(new_handler);
4603cab2bb3Spatrick }
4613cab2bb3Spatrick 
TSAN_INTERCEPTOR(void,dispatch_source_set_cancel_handler_f,dispatch_source_t source,dispatch_function_t handler)4623cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_source_set_cancel_handler_f,
4633cab2bb3Spatrick                  dispatch_source_t source, dispatch_function_t handler) {
4643cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_cancel_handler_f, source,
4653cab2bb3Spatrick                           handler);
4663cab2bb3Spatrick   if (handler == nullptr)
4673cab2bb3Spatrick     return REAL(dispatch_source_set_cancel_handler)(source, nullptr);
4683cab2bb3Spatrick   dispatch_block_t block = ^(void) {
4693cab2bb3Spatrick     handler(dispatch_get_context(source));
4703cab2bb3Spatrick   };
4713cab2bb3Spatrick   WRAP(dispatch_source_set_cancel_handler)(source, block);
4723cab2bb3Spatrick }
4733cab2bb3Spatrick 
TSAN_INTERCEPTOR(void,dispatch_source_set_registration_handler,dispatch_source_t source,dispatch_block_t handler)4743cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_source_set_registration_handler,
4753cab2bb3Spatrick                  dispatch_source_t source, dispatch_block_t handler) {
4763cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_registration_handler, source,
4773cab2bb3Spatrick                           handler);
4783cab2bb3Spatrick   if (handler == nullptr)
4793cab2bb3Spatrick     return REAL(dispatch_source_set_registration_handler)(source, nullptr);
4803cab2bb3Spatrick   dispatch_queue_t q = GetTargetQueueFromSource(source);
4813cab2bb3Spatrick   __block block_context_t new_context = {
4823cab2bb3Spatrick       q, handler, &invoke_block, false, false, false, 0};
4833cab2bb3Spatrick   dispatch_block_t new_handler = Block_copy(^(void) {
4843cab2bb3Spatrick     new_context.orig_context = handler;  // To explicitly capture "handler".
4853cab2bb3Spatrick     dispatch_callback_wrap(&new_context);
4863cab2bb3Spatrick   });
4873cab2bb3Spatrick   uptr submit_sync = (uptr)&new_context;
4883cab2bb3Spatrick   Release(thr, pc, submit_sync);
4893cab2bb3Spatrick   REAL(dispatch_source_set_registration_handler)(source, new_handler);
4903cab2bb3Spatrick   Block_release(new_handler);
4913cab2bb3Spatrick }
4923cab2bb3Spatrick 
TSAN_INTERCEPTOR(void,dispatch_source_set_registration_handler_f,dispatch_source_t source,dispatch_function_t handler)4933cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_source_set_registration_handler_f,
4943cab2bb3Spatrick                  dispatch_source_t source, dispatch_function_t handler) {
4953cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_registration_handler_f, source,
4963cab2bb3Spatrick                           handler);
4973cab2bb3Spatrick   if (handler == nullptr)
4983cab2bb3Spatrick     return REAL(dispatch_source_set_registration_handler)(source, nullptr);
4993cab2bb3Spatrick   dispatch_block_t block = ^(void) {
5003cab2bb3Spatrick     handler(dispatch_get_context(source));
5013cab2bb3Spatrick   };
5023cab2bb3Spatrick   WRAP(dispatch_source_set_registration_handler)(source, block);
5033cab2bb3Spatrick }
5043cab2bb3Spatrick 
5053cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_apply, size_t iterations,
5063cab2bb3Spatrick                  dispatch_queue_t queue,
5073cab2bb3Spatrick                  DISPATCH_NOESCAPE void (^block)(size_t)) {
5083cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_apply, iterations, queue, block);
5093cab2bb3Spatrick 
5103cab2bb3Spatrick   u8 sync1, sync2;
5113cab2bb3Spatrick   uptr parent_to_child_sync = (uptr)&sync1;
5123cab2bb3Spatrick   uptr child_to_parent_sync = (uptr)&sync2;
5133cab2bb3Spatrick 
5143cab2bb3Spatrick   Release(thr, pc, parent_to_child_sync);
5153cab2bb3Spatrick   void (^new_block)(size_t) = ^(size_t iteration) {
5163cab2bb3Spatrick     SCOPED_INTERCEPTOR_RAW(dispatch_apply);
5173cab2bb3Spatrick     Acquire(thr, pc, parent_to_child_sync);
5183cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
5193cab2bb3Spatrick     block(iteration);
5203cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
5213cab2bb3Spatrick     Release(thr, pc, child_to_parent_sync);
5223cab2bb3Spatrick   };
5233cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
5243cab2bb3Spatrick   REAL(dispatch_apply)(iterations, queue, new_block);
5253cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
5263cab2bb3Spatrick   Acquire(thr, pc, child_to_parent_sync);
5273cab2bb3Spatrick }
5283cab2bb3Spatrick 
invoke_block_iteration(void * param,size_t iteration)5293cab2bb3Spatrick static void invoke_block_iteration(void *param, size_t iteration) {
5303cab2bb3Spatrick   auto block = (void (^)(size_t)) param;
5313cab2bb3Spatrick   block(iteration);
5323cab2bb3Spatrick }
5333cab2bb3Spatrick 
TSAN_INTERCEPTOR(void,dispatch_apply_f,size_t iterations,dispatch_queue_t queue,void * context,void (* work)(void *,size_t))5343cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_apply_f, size_t iterations,
5353cab2bb3Spatrick                  dispatch_queue_t queue, void *context,
5363cab2bb3Spatrick                  void (*work)(void *, size_t)) {
5373cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_apply_f, iterations, queue, context, work);
5383cab2bb3Spatrick 
5393cab2bb3Spatrick   // Unfortunately, we cannot delegate to dispatch_apply, since libdispatch
5403cab2bb3Spatrick   // implements dispatch_apply in terms of dispatch_apply_f.
5413cab2bb3Spatrick   u8 sync1, sync2;
5423cab2bb3Spatrick   uptr parent_to_child_sync = (uptr)&sync1;
5433cab2bb3Spatrick   uptr child_to_parent_sync = (uptr)&sync2;
5443cab2bb3Spatrick 
5453cab2bb3Spatrick   Release(thr, pc, parent_to_child_sync);
5463cab2bb3Spatrick   void (^new_block)(size_t) = ^(size_t iteration) {
5473cab2bb3Spatrick     SCOPED_INTERCEPTOR_RAW(dispatch_apply_f);
5483cab2bb3Spatrick     Acquire(thr, pc, parent_to_child_sync);
5493cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
5503cab2bb3Spatrick     work(context, iteration);
5513cab2bb3Spatrick     SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
5523cab2bb3Spatrick     Release(thr, pc, child_to_parent_sync);
5533cab2bb3Spatrick   };
5543cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
5553cab2bb3Spatrick   REAL(dispatch_apply_f)(iterations, queue, new_block, invoke_block_iteration);
5563cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
5573cab2bb3Spatrick   Acquire(thr, pc, child_to_parent_sync);
5583cab2bb3Spatrick }
5593cab2bb3Spatrick 
DECLARE_REAL_AND_INTERCEPTOR(void,free,void * ptr)5603cab2bb3Spatrick DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
5613cab2bb3Spatrick DECLARE_REAL_AND_INTERCEPTOR(int, munmap, void *addr, long_t sz)
5623cab2bb3Spatrick 
5633cab2bb3Spatrick TSAN_INTERCEPTOR(dispatch_data_t, dispatch_data_create, const void *buffer,
5643cab2bb3Spatrick                  size_t size, dispatch_queue_t q, dispatch_block_t destructor) {
5653cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_data_create, buffer, size, q, destructor);
5663cab2bb3Spatrick   if ((q == nullptr) || (destructor == DISPATCH_DATA_DESTRUCTOR_DEFAULT))
5673cab2bb3Spatrick     return REAL(dispatch_data_create)(buffer, size, q, destructor);
5683cab2bb3Spatrick 
5693cab2bb3Spatrick   if (destructor == DISPATCH_DATA_DESTRUCTOR_FREE)
5703cab2bb3Spatrick     destructor = ^(void) { WRAP(free)((void *)(uintptr_t)buffer); };
5713cab2bb3Spatrick   else if (destructor == DISPATCH_DATA_DESTRUCTOR_MUNMAP)
5723cab2bb3Spatrick     destructor = ^(void) { WRAP(munmap)((void *)(uintptr_t)buffer, size); };
5733cab2bb3Spatrick 
5743cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
5753cab2bb3Spatrick   dispatch_block_t heap_block = Block_copy(destructor);
5763cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
5773cab2bb3Spatrick   block_context_t *new_context =
5783cab2bb3Spatrick       AllocContext(thr, pc, q, heap_block, &invoke_and_release_block);
5793cab2bb3Spatrick   uptr submit_sync = (uptr)new_context;
5803cab2bb3Spatrick   Release(thr, pc, submit_sync);
5813cab2bb3Spatrick   return REAL(dispatch_data_create)(buffer, size, q, ^(void) {
5823cab2bb3Spatrick     dispatch_callback_wrap(new_context);
5833cab2bb3Spatrick   });
5843cab2bb3Spatrick }
5853cab2bb3Spatrick 
5863cab2bb3Spatrick typedef void (^fd_handler_t)(dispatch_data_t data, int error);
5873cab2bb3Spatrick typedef void (^cleanup_handler_t)(int error);
5883cab2bb3Spatrick 
TSAN_INTERCEPTOR(void,dispatch_read,dispatch_fd_t fd,size_t length,dispatch_queue_t q,fd_handler_t h)5893cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_read, dispatch_fd_t fd, size_t length,
5903cab2bb3Spatrick                  dispatch_queue_t q, fd_handler_t h) {
5913cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_read, fd, length, q, h);
5923cab2bb3Spatrick   __block block_context_t new_context = {
5933cab2bb3Spatrick       q, nullptr, &invoke_block, false, false, false, 0};
5943cab2bb3Spatrick   fd_handler_t new_h = Block_copy(^(dispatch_data_t data, int error) {
5953cab2bb3Spatrick     new_context.orig_context = ^(void) {
5963cab2bb3Spatrick       h(data, error);
5973cab2bb3Spatrick     };
5983cab2bb3Spatrick     dispatch_callback_wrap(&new_context);
5993cab2bb3Spatrick   });
6003cab2bb3Spatrick   uptr submit_sync = (uptr)&new_context;
6013cab2bb3Spatrick   Release(thr, pc, submit_sync);
6023cab2bb3Spatrick   REAL(dispatch_read)(fd, length, q, new_h);
6033cab2bb3Spatrick   Block_release(new_h);
6043cab2bb3Spatrick }
6053cab2bb3Spatrick 
TSAN_INTERCEPTOR(void,dispatch_write,dispatch_fd_t fd,dispatch_data_t data,dispatch_queue_t q,fd_handler_t h)6063cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_write, dispatch_fd_t fd, dispatch_data_t data,
6073cab2bb3Spatrick                  dispatch_queue_t q, fd_handler_t h) {
6083cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_write, fd, data, q, h);
6093cab2bb3Spatrick   __block block_context_t new_context = {
6103cab2bb3Spatrick       q, nullptr, &invoke_block, false, false, false, 0};
6113cab2bb3Spatrick   fd_handler_t new_h = Block_copy(^(dispatch_data_t data, int error) {
6123cab2bb3Spatrick     new_context.orig_context = ^(void) {
6133cab2bb3Spatrick       h(data, error);
6143cab2bb3Spatrick     };
6153cab2bb3Spatrick     dispatch_callback_wrap(&new_context);
6163cab2bb3Spatrick   });
6173cab2bb3Spatrick   uptr submit_sync = (uptr)&new_context;
6183cab2bb3Spatrick   Release(thr, pc, submit_sync);
6193cab2bb3Spatrick   REAL(dispatch_write)(fd, data, q, new_h);
6203cab2bb3Spatrick   Block_release(new_h);
6213cab2bb3Spatrick }
6223cab2bb3Spatrick 
TSAN_INTERCEPTOR(void,dispatch_io_read,dispatch_io_t channel,off_t offset,size_t length,dispatch_queue_t q,dispatch_io_handler_t h)6233cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_io_read, dispatch_io_t channel, off_t offset,
6243cab2bb3Spatrick                  size_t length, dispatch_queue_t q, dispatch_io_handler_t h) {
6253cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_io_read, channel, offset, length, q, h);
6263cab2bb3Spatrick   __block block_context_t new_context = {
6273cab2bb3Spatrick       q, nullptr, &invoke_block, false, false, false, 0};
6283cab2bb3Spatrick   dispatch_io_handler_t new_h =
6293cab2bb3Spatrick       Block_copy(^(bool done, dispatch_data_t data, int error) {
6303cab2bb3Spatrick         new_context.orig_context = ^(void) {
6313cab2bb3Spatrick           h(done, data, error);
6323cab2bb3Spatrick         };
6333cab2bb3Spatrick         dispatch_callback_wrap(&new_context);
6343cab2bb3Spatrick       });
6353cab2bb3Spatrick   uptr submit_sync = (uptr)&new_context;
6363cab2bb3Spatrick   Release(thr, pc, submit_sync);
6373cab2bb3Spatrick   REAL(dispatch_io_read)(channel, offset, length, q, new_h);
6383cab2bb3Spatrick   Block_release(new_h);
6393cab2bb3Spatrick }
6403cab2bb3Spatrick 
TSAN_INTERCEPTOR(void,dispatch_io_write,dispatch_io_t channel,off_t offset,dispatch_data_t data,dispatch_queue_t q,dispatch_io_handler_t h)6413cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_io_write, dispatch_io_t channel, off_t offset,
6423cab2bb3Spatrick                  dispatch_data_t data, dispatch_queue_t q,
6433cab2bb3Spatrick                  dispatch_io_handler_t h) {
6443cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_io_write, channel, offset, data, q, h);
6453cab2bb3Spatrick   __block block_context_t new_context = {
6463cab2bb3Spatrick       q, nullptr, &invoke_block, false, false, false, 0};
6473cab2bb3Spatrick   dispatch_io_handler_t new_h =
6483cab2bb3Spatrick       Block_copy(^(bool done, dispatch_data_t data, int error) {
6493cab2bb3Spatrick         new_context.orig_context = ^(void) {
6503cab2bb3Spatrick           h(done, data, error);
6513cab2bb3Spatrick         };
6523cab2bb3Spatrick         dispatch_callback_wrap(&new_context);
6533cab2bb3Spatrick       });
6543cab2bb3Spatrick   uptr submit_sync = (uptr)&new_context;
6553cab2bb3Spatrick   Release(thr, pc, submit_sync);
6563cab2bb3Spatrick   REAL(dispatch_io_write)(channel, offset, data, q, new_h);
6573cab2bb3Spatrick   Block_release(new_h);
6583cab2bb3Spatrick }
6593cab2bb3Spatrick 
TSAN_INTERCEPTOR(void,dispatch_io_barrier,dispatch_io_t channel,dispatch_block_t barrier)6603cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_io_barrier, dispatch_io_t channel,
6613cab2bb3Spatrick                  dispatch_block_t barrier) {
6623cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_io_barrier, channel, barrier);
6633cab2bb3Spatrick   __block block_context_t new_context = {
6643cab2bb3Spatrick       nullptr, nullptr, &invoke_block, false, false, false, 0};
6653cab2bb3Spatrick   new_context.non_queue_sync_object = (uptr)channel;
6663cab2bb3Spatrick   new_context.is_barrier_block = true;
6673cab2bb3Spatrick   dispatch_block_t new_block = Block_copy(^(void) {
6683cab2bb3Spatrick     new_context.orig_context = ^(void) {
6693cab2bb3Spatrick       barrier();
6703cab2bb3Spatrick     };
6713cab2bb3Spatrick     dispatch_callback_wrap(&new_context);
6723cab2bb3Spatrick   });
6733cab2bb3Spatrick   uptr submit_sync = (uptr)&new_context;
6743cab2bb3Spatrick   Release(thr, pc, submit_sync);
6753cab2bb3Spatrick   REAL(dispatch_io_barrier)(channel, new_block);
6763cab2bb3Spatrick   Block_release(new_block);
6773cab2bb3Spatrick }
6783cab2bb3Spatrick 
TSAN_INTERCEPTOR(dispatch_io_t,dispatch_io_create,dispatch_io_type_t type,dispatch_fd_t fd,dispatch_queue_t q,cleanup_handler_t h)6793cab2bb3Spatrick TSAN_INTERCEPTOR(dispatch_io_t, dispatch_io_create, dispatch_io_type_t type,
6803cab2bb3Spatrick                  dispatch_fd_t fd, dispatch_queue_t q, cleanup_handler_t h) {
6813cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_io_create, type, fd, q, h);
6823cab2bb3Spatrick   __block dispatch_io_t new_channel = nullptr;
6833cab2bb3Spatrick   __block block_context_t new_context = {
6843cab2bb3Spatrick       q, nullptr, &invoke_block, false, false, false, 0};
6853cab2bb3Spatrick   cleanup_handler_t new_h = Block_copy(^(int error) {
6863cab2bb3Spatrick     {
6873cab2bb3Spatrick       SCOPED_INTERCEPTOR_RAW(dispatch_io_create_callback);
6883cab2bb3Spatrick       Acquire(thr, pc, (uptr)new_channel);  // Release() in dispatch_io_close.
6893cab2bb3Spatrick     }
6903cab2bb3Spatrick     new_context.orig_context = ^(void) {
6913cab2bb3Spatrick       h(error);
6923cab2bb3Spatrick     };
6933cab2bb3Spatrick     dispatch_callback_wrap(&new_context);
6943cab2bb3Spatrick   });
6953cab2bb3Spatrick   uptr submit_sync = (uptr)&new_context;
6963cab2bb3Spatrick   Release(thr, pc, submit_sync);
6973cab2bb3Spatrick   new_channel = REAL(dispatch_io_create)(type, fd, q, new_h);
6983cab2bb3Spatrick   Block_release(new_h);
6993cab2bb3Spatrick   return new_channel;
7003cab2bb3Spatrick }
7013cab2bb3Spatrick 
TSAN_INTERCEPTOR(dispatch_io_t,dispatch_io_create_with_path,dispatch_io_type_t type,const char * path,int oflag,mode_t mode,dispatch_queue_t q,cleanup_handler_t h)7023cab2bb3Spatrick TSAN_INTERCEPTOR(dispatch_io_t, dispatch_io_create_with_path,
7033cab2bb3Spatrick                  dispatch_io_type_t type, const char *path, int oflag,
7043cab2bb3Spatrick                  mode_t mode, dispatch_queue_t q, cleanup_handler_t h) {
7053cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_io_create_with_path, type, path, oflag, mode,
7063cab2bb3Spatrick                           q, h);
7073cab2bb3Spatrick   __block dispatch_io_t new_channel = nullptr;
7083cab2bb3Spatrick   __block block_context_t new_context = {
7093cab2bb3Spatrick       q, nullptr, &invoke_block, false, false, false, 0};
7103cab2bb3Spatrick   cleanup_handler_t new_h = Block_copy(^(int error) {
7113cab2bb3Spatrick     {
7123cab2bb3Spatrick       SCOPED_INTERCEPTOR_RAW(dispatch_io_create_callback);
7133cab2bb3Spatrick       Acquire(thr, pc, (uptr)new_channel);  // Release() in dispatch_io_close.
7143cab2bb3Spatrick     }
7153cab2bb3Spatrick     new_context.orig_context = ^(void) {
7163cab2bb3Spatrick       h(error);
7173cab2bb3Spatrick     };
7183cab2bb3Spatrick     dispatch_callback_wrap(&new_context);
7193cab2bb3Spatrick   });
7203cab2bb3Spatrick   uptr submit_sync = (uptr)&new_context;
7213cab2bb3Spatrick   Release(thr, pc, submit_sync);
7223cab2bb3Spatrick   new_channel =
7233cab2bb3Spatrick       REAL(dispatch_io_create_with_path)(type, path, oflag, mode, q, new_h);
7243cab2bb3Spatrick   Block_release(new_h);
7253cab2bb3Spatrick   return new_channel;
7263cab2bb3Spatrick }
7273cab2bb3Spatrick 
TSAN_INTERCEPTOR(dispatch_io_t,dispatch_io_create_with_io,dispatch_io_type_t type,dispatch_io_t io,dispatch_queue_t q,cleanup_handler_t h)7283cab2bb3Spatrick TSAN_INTERCEPTOR(dispatch_io_t, dispatch_io_create_with_io,
7293cab2bb3Spatrick                  dispatch_io_type_t type, dispatch_io_t io, dispatch_queue_t q,
7303cab2bb3Spatrick                  cleanup_handler_t h) {
7313cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_io_create_with_io, type, io, q, h);
7323cab2bb3Spatrick   __block dispatch_io_t new_channel = nullptr;
7333cab2bb3Spatrick   __block block_context_t new_context = {
7343cab2bb3Spatrick       q, nullptr, &invoke_block, false, false, false, 0};
7353cab2bb3Spatrick   cleanup_handler_t new_h = Block_copy(^(int error) {
7363cab2bb3Spatrick     {
7373cab2bb3Spatrick       SCOPED_INTERCEPTOR_RAW(dispatch_io_create_callback);
7383cab2bb3Spatrick       Acquire(thr, pc, (uptr)new_channel);  // Release() in dispatch_io_close.
7393cab2bb3Spatrick     }
7403cab2bb3Spatrick     new_context.orig_context = ^(void) {
7413cab2bb3Spatrick       h(error);
7423cab2bb3Spatrick     };
7433cab2bb3Spatrick     dispatch_callback_wrap(&new_context);
7443cab2bb3Spatrick   });
7453cab2bb3Spatrick   uptr submit_sync = (uptr)&new_context;
7463cab2bb3Spatrick   Release(thr, pc, submit_sync);
7473cab2bb3Spatrick   new_channel = REAL(dispatch_io_create_with_io)(type, io, q, new_h);
7483cab2bb3Spatrick   Block_release(new_h);
7493cab2bb3Spatrick   return new_channel;
7503cab2bb3Spatrick }
7513cab2bb3Spatrick 
TSAN_INTERCEPTOR(void,dispatch_io_close,dispatch_io_t channel,dispatch_io_close_flags_t flags)7523cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_io_close, dispatch_io_t channel,
7533cab2bb3Spatrick                  dispatch_io_close_flags_t flags) {
7543cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_io_close, channel, flags);
7553cab2bb3Spatrick   Release(thr, pc, (uptr)channel);  // Acquire() in dispatch_io_create[_*].
7563cab2bb3Spatrick   return REAL(dispatch_io_close)(channel, flags);
7573cab2bb3Spatrick }
7583cab2bb3Spatrick 
7593cab2bb3Spatrick // Resuming a suspended queue needs to synchronize with all subsequent
7603cab2bb3Spatrick // executions of blocks in that queue.
TSAN_INTERCEPTOR(void,dispatch_resume,dispatch_object_t o)7613cab2bb3Spatrick TSAN_INTERCEPTOR(void, dispatch_resume, dispatch_object_t o) {
7623cab2bb3Spatrick   SCOPED_TSAN_INTERCEPTOR(dispatch_resume, o);
7633cab2bb3Spatrick   Release(thr, pc, (uptr)o);  // Synchronizes with the Acquire() on serial_sync
7643cab2bb3Spatrick                               // in dispatch_sync_pre_execute
7653cab2bb3Spatrick   return REAL(dispatch_resume)(o);
7663cab2bb3Spatrick }
7673cab2bb3Spatrick 
InitializeLibdispatchInterceptors()7683cab2bb3Spatrick void InitializeLibdispatchInterceptors() {
7693cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_async);
7703cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_async_f);
7713cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_sync);
7723cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_sync_f);
7733cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_barrier_async);
7743cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_barrier_async_f);
7753cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_barrier_sync);
7763cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_barrier_sync_f);
777d89ec533Spatrick   INTERCEPT_FUNCTION(dispatch_async_and_wait);
778d89ec533Spatrick   INTERCEPT_FUNCTION(dispatch_async_and_wait_f);
779d89ec533Spatrick   INTERCEPT_FUNCTION(dispatch_barrier_async_and_wait);
780d89ec533Spatrick   INTERCEPT_FUNCTION(dispatch_barrier_async_and_wait_f);
7813cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_after);
7823cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_after_f);
7833cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_once);
7843cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_once_f);
7853cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_semaphore_signal);
7863cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_semaphore_wait);
7873cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_group_wait);
7883cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_group_leave);
7893cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_group_async);
7903cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_group_async_f);
7913cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_group_notify);
7923cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_group_notify_f);
7933cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_source_set_event_handler);
7943cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_source_set_event_handler_f);
7953cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_source_set_cancel_handler);
7963cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_source_set_cancel_handler_f);
7973cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_source_set_registration_handler);
7983cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_source_set_registration_handler_f);
7993cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_apply);
8003cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_apply_f);
8013cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_data_create);
8023cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_read);
8033cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_write);
8043cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_io_read);
8053cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_io_write);
8063cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_io_barrier);
8073cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_io_create);
8083cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_io_create_with_path);
8093cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_io_create_with_io);
8103cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_io_close);
8113cab2bb3Spatrick   INTERCEPT_FUNCTION(dispatch_resume);
8123cab2bb3Spatrick }
8133cab2bb3Spatrick 
8143cab2bb3Spatrick }  // namespace __tsan
815