1 //===-- tsan_libdispatch_mac.cc -------------------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
8 // This file is a part of ThreadSanitizer (TSan), a race detector.
10 // Mac-specific libdispatch (GCD) support.
11 //===----------------------------------------------------------------------===//
13 #include "sanitizer_common/sanitizer_platform.h"
16 #include "sanitizer_common/sanitizer_common.h"
17 #include "interception/interception.h"
18 #include "tsan_interceptors.h"
19 #include "tsan_platform.h"
23 #include <dispatch/dispatch.h>
26 typedef long long_t
; // NOLINT
31 dispatch_queue_t queue
;
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
);
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
);
76 static dispatch_queue_t
GetTargetQueueFromSource(dispatch_source_t source
) {
77 dispatch_queue_t tq
= GetTargetQueueFromQueue((dispatch_queue_t
)source
);
82 static tsan_block_context_t
*AllocContext(ThreadState
*thr
, uptr pc
,
83 dispatch_queue_t queue
,
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;
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
;
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
);
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
;
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
);
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
;
156 static void invoke_and_release_block(void *param
) {
157 dispatch_block_t block
= (dispatch_block_t
)param
;
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
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) {
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.
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
);
274 atomic_compare_exchange_strong(a
, &v
, 1, memory_order_relaxed
)) {
275 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
277 SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
278 Release(thr
, pc
, (uptr
)a
);
279 atomic_store(a
, 2, memory_order_release
);
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) {
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
);
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
);
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) {
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) {
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
);
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
,
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
,
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
,
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();
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) {
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) {
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) {
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) {
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
);
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
,
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) {
671 dispatch_callback_wrap(&new_context
);
673 uptr submit_sync
= (uptr
)&new_context
;
674 Release(thr
, pc
, submit_sync
);
676 REAL(dispatch_io_create_with_path
)(type
, path
, oflag
, mode
, q
, new_h
);
677 Block_release(new_h
);
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) {
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
);
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