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