Skip to content

Commit

Permalink
[tsan] Fix false positives with GCD dispatch_source_*
Browse files Browse the repository at this point in the history
We already have interceptors for dispatch_source API (e.g. dispatch_source_set_event_handler), but they currently only handle submission synchronization. We also need to synchronize based on the target queue (serial, concurrent), in other words, we need to use dispatch_callback_wrap. This patch implements that.

Differential Revision: http://reviews.llvm.org/D21999

llvm-svn: 274619
  • Loading branch information
kubamracek committed Jul 6, 2016
1 parent 9c6c8e9 commit fd995fe
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 24 deletions.
68 changes: 44 additions & 24 deletions compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc
Expand Up @@ -67,6 +67,15 @@ static bool IsQueueSerial(dispatch_queue_t q) {
return width == 1;
}

static dispatch_queue_t GetTargetQueueFromSource(dispatch_source_t source) {
CHECK_EQ(dispatch_queue_offsets.dqo_target_queue_size, 8);
dispatch_queue_t target_queue =
*(dispatch_queue_t *)(((uptr)source) +
dispatch_queue_offsets.dqo_target_queue);
CHECK_NE(target_queue, 0);
return target_queue;
}

static tsan_block_context_t *AllocContext(ThreadState *thr, uptr pc,
dispatch_queue_t queue,
void *orig_context,
Expand Down Expand Up @@ -106,6 +115,11 @@ static void dispatch_callback_wrap(void *param) {
if (context->free_context_in_callback) user_free(thr, pc, context);
}

static void invoke_block(void *param) {
dispatch_block_t block = (dispatch_block_t)param;
block();
}

static void invoke_and_release_block(void *param) {
dispatch_block_t block = (dispatch_block_t)param;
block();
Expand Down Expand Up @@ -342,15 +356,17 @@ TSAN_INTERCEPTOR(void, dispatch_source_set_event_handler,
SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_event_handler, source, handler);
if (handler == nullptr)
return REAL(dispatch_source_set_event_handler)(source, nullptr);
dispatch_block_t new_handler = ^(void) {
{
SCOPED_INTERCEPTOR_RAW(dispatch_source_set_event_handler_callback);
Acquire(thr, pc, (uptr)source);
}
handler();
};
Release(thr, pc, (uptr)source);
dispatch_queue_t q = GetTargetQueueFromSource(source);
__block tsan_block_context_t new_context = {
q, handler, &invoke_block, false, false, false };
dispatch_block_t new_handler = Block_copy(^(void) {
new_context.orig_context = handler; // To explicitly capture "handler".
dispatch_callback_wrap(&new_context);
});
uptr submit_sync = (uptr)&new_context;
Release(thr, pc, submit_sync);
REAL(dispatch_source_set_event_handler)(source, new_handler);
Block_release(new_handler);
}

TSAN_INTERCEPTOR(void, dispatch_source_set_event_handler_f,
Expand All @@ -369,15 +385,17 @@ TSAN_INTERCEPTOR(void, dispatch_source_set_cancel_handler,
SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_cancel_handler, source, handler);
if (handler == nullptr)
return REAL(dispatch_source_set_cancel_handler)(source, nullptr);
dispatch_block_t new_handler = ^(void) {
{
SCOPED_INTERCEPTOR_RAW(dispatch_source_set_cancel_handler_callback);
Acquire(thr, pc, (uptr)source);
}
handler();
};
Release(thr, pc, (uptr)source);
dispatch_queue_t q = GetTargetQueueFromSource(source);
__block tsan_block_context_t new_context = {
q, handler, &invoke_block, false, false, false };
dispatch_block_t new_handler = Block_copy(^(void) {
new_context.orig_context = handler; // To explicitly capture "handler".
dispatch_callback_wrap(&new_context);
});
uptr submit_sync = (uptr)&new_context;
Release(thr, pc, submit_sync);
REAL(dispatch_source_set_cancel_handler)(source, new_handler);
Block_release(new_handler);
}

TSAN_INTERCEPTOR(void, dispatch_source_set_cancel_handler_f,
Expand All @@ -398,15 +416,17 @@ TSAN_INTERCEPTOR(void, dispatch_source_set_registration_handler,
handler);
if (handler == nullptr)
return REAL(dispatch_source_set_registration_handler)(source, nullptr);
dispatch_block_t new_handler = ^(void) {
{
SCOPED_INTERCEPTOR_RAW(dispatch_source_set_registration_handler_callback);
Acquire(thr, pc, (uptr)source);
}
handler();
};
Release(thr, pc, (uptr)source);
dispatch_queue_t q = GetTargetQueueFromSource(source);
__block tsan_block_context_t new_context = {
q, handler, &invoke_block, false, false, false };
dispatch_block_t new_handler = Block_copy(^(void) {
new_context.orig_context = handler; // To explicitly capture "handler".
dispatch_callback_wrap(&new_context);
});
uptr submit_sync = (uptr)&new_context;
Release(thr, pc, submit_sync);
REAL(dispatch_source_set_registration_handler)(source, new_handler);
Block_release(new_handler);
}

TSAN_INTERCEPTOR(void, dispatch_source_set_registration_handler_f,
Expand Down
33 changes: 33 additions & 0 deletions compiler-rt/test/tsan/Darwin/gcd-source-serial.mm
@@ -0,0 +1,33 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s

#import <Foundation/Foundation.h>

long global;

int main(int argc, const char *argv[]) {
fprintf(stderr, "Hello world.\n");

dispatch_queue_t q = dispatch_queue_create("my.queue", DISPATCH_QUEUE_SERIAL);
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, q);
long long interval_ms = 10;
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 0), interval_ms * NSEC_PER_MSEC, 0);
dispatch_source_set_event_handler(timer, ^{
fprintf(stderr, "timer\n");
global++;

if (global > 50) {
dispatch_semaphore_signal(sem);
}
});
dispatch_resume(timer);

dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);

fprintf(stderr, "Done.\n");
}

// CHECK: Hello world.
// CHECK-NOT: WARNING: ThreadSanitizer
// CHECK: Done.

0 comments on commit fd995fe

Please sign in to comment.