Index: lib/tsan/rtl/tsan_libdispatch_mac.cc =================================================================== --- lib/tsan/rtl/tsan_libdispatch_mac.cc +++ lib/tsan/rtl/tsan_libdispatch_mac.cc @@ -447,6 +447,147 @@ WRAP(dispatch_apply)(iterations, queue, new_block); } +typedef void (^fd_handler_t)(dispatch_data_t data, int error); +typedef void (^cleanup_handler_t)(int error); + +#define DISPATCH_CALLBACK_WITH_BLOCK(q, b) \ + dispatch_block_t new_block = ^(void) { \ + b; \ + }; \ + tsan_block_context_t ctx = { q, new_block, &invoke_and_release_block, \ + false, false, false}; \ + dispatch_callback_wrap(&ctx) + +TSAN_INTERCEPTOR(void, dispatch_read, dispatch_fd_t fd, size_t length, + dispatch_queue_t q, fd_handler_t h) { + SCOPED_TSAN_INTERCEPTOR(dispatch_read, fd, length, q, h); + fd_handler_t new_h = ^(dispatch_data_t data, int error) { + { + SCOPED_INTERCEPTOR_RAW(dispatch_read_callback); + Acquire(thr, pc, (uptr)q); + } + DISPATCH_CALLBACK_WITH_BLOCK(q, h(data, error)); + }; + Release(thr, pc, (uptr)q); + REAL(dispatch_read)(fd, length, q, new_h); +} + +TSAN_INTERCEPTOR(void, dispatch_write, dispatch_fd_t fd, dispatch_data_t data, + dispatch_queue_t q, fd_handler_t h) { + SCOPED_TSAN_INTERCEPTOR(dispatch_write, fd, data, q, h); + fd_handler_t new_h = ^(dispatch_data_t data, int error) { + { + SCOPED_INTERCEPTOR_RAW(dispatch_write_callback); + Acquire(thr, pc, (uptr)q); + } + DISPATCH_CALLBACK_WITH_BLOCK(q, h(data, error)); + }; + Release(thr, pc, (uptr)q); + REAL(dispatch_write)(fd, data, q, new_h); +} + +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) { + SCOPED_TSAN_INTERCEPTOR(dispatch_io_read, channel, offset, length, q, h); + dispatch_io_handler_t new_h = ^(bool done, dispatch_data_t data, int error) { + { + SCOPED_INTERCEPTOR_RAW(dispatch_io_read_callback); + Acquire(thr, pc, (uptr)channel); + } + DISPATCH_CALLBACK_WITH_BLOCK(q, h(done, data, error)); + }; + Release(thr, pc, (uptr)channel); + REAL(dispatch_io_read)(channel, offset, length, q, new_h); +} + +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) { + SCOPED_TSAN_INTERCEPTOR(dispatch_io_write, channel, offset, data, q, h); + dispatch_io_handler_t new_h = ^(bool done, dispatch_data_t data, int error) { + { + SCOPED_INTERCEPTOR_RAW(dispatch_io_write_callback); + Acquire(thr, pc, (uptr)channel); + } + DISPATCH_CALLBACK_WITH_BLOCK(q, h(done, data, error)); + }; + Release(thr, pc, (uptr)channel); + REAL(dispatch_io_write)(channel, offset, data, q, new_h); +} + +TSAN_INTERCEPTOR(void, dispatch_io_barrier, dispatch_io_t channel, + dispatch_block_t barrier) { + SCOPED_TSAN_INTERCEPTOR(dispatch_io_barrier, channel, barrier); + dispatch_block_t new_block = ^(void) { + { + SCOPED_INTERCEPTOR_RAW(dispatch_io_barrier_callback); + Acquire(thr, pc, (uptr)channel); + } + barrier(); + }; + Release(thr, pc, (uptr)channel); + REAL(dispatch_io_barrier)(channel, new_block); +} + +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) { + SCOPED_TSAN_INTERCEPTOR(dispatch_io_create, type, fd, q, h); + __block dispatch_io_t new_channel = nullptr; + cleanup_handler_t new_h = ^(int error) { + { + SCOPED_INTERCEPTOR_RAW(dispatch_io_create_callback); + Acquire(thr, pc, (uptr)new_channel); + } + DISPATCH_CALLBACK_WITH_BLOCK(q, h(error)); + }; + Release(thr, pc, (uptr)q); + new_channel = REAL(dispatch_io_create)(type, fd, q, new_h); + return new_channel; +} + +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) { + SCOPED_TSAN_INTERCEPTOR(dispatch_io_create_with_path, type, path, oflag, mode, + q, h); + __block dispatch_io_t new_channel = nullptr; + cleanup_handler_t new_h = ^(int error) { + { + SCOPED_INTERCEPTOR_RAW(dispatch_io_create_with_path_callback); + Acquire(thr, pc, (uptr)new_channel); + } + DISPATCH_CALLBACK_WITH_BLOCK(q, h(error)); + }; + Release(thr, pc, (uptr)q); + new_channel = + REAL(dispatch_io_create_with_path)(type, path, oflag, mode, q, new_h); + return new_channel; +} + +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) { + SCOPED_TSAN_INTERCEPTOR(dispatch_io_create_with_io, type, io, q, h); + __block dispatch_io_t new_channel = nullptr; + cleanup_handler_t new_h = ^(int error) { + { + SCOPED_INTERCEPTOR_RAW(dispatch_io_create_with_io_callback); + Acquire(thr, pc, (uptr)new_channel); + } + DISPATCH_CALLBACK_WITH_BLOCK(q, h(error)); + }; + Release(thr, pc, (uptr)q); + new_channel = REAL(dispatch_io_create_with_io)(type, io, q, new_h); + return new_channel; +} + +TSAN_INTERCEPTOR(void, dispatch_io_close, dispatch_io_t channel, + dispatch_io_close_flags_t flags) { + SCOPED_TSAN_INTERCEPTOR(dispatch_io_close, channel, flags); + Release(thr, pc, (uptr)channel); + return REAL(dispatch_io_close)(channel, flags); +} + } // namespace __tsan #endif // SANITIZER_MAC Index: test/tsan/Darwin/gcd-fd.mm =================================================================== --- test/tsan/Darwin/gcd-fd.mm +++ test/tsan/Darwin/gcd-fd.mm @@ -0,0 +1,60 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import + +long my_global = 0; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + dispatch_queue_t queue = dispatch_queue_create("my.queue", DISPATCH_QUEUE_SERIAL); + dispatch_semaphore_t sem = dispatch_semaphore_create(0); + + NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"temp-gcd-io.%d", getpid()]]; + + dispatch_io_t channel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, path.fileSystemRepresentation, O_CREAT | O_WRONLY, + 0666, queue, ^(int error) { }); + dispatch_io_set_high_water(channel, 1); + + NSData *ns_data = [NSMutableData dataWithLength:1000]; + dispatch_data_t data = dispatch_data_create(ns_data.bytes, ns_data.length, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + + my_global++; + dispatch_io_write(channel, 0, data, queue, ^(bool done, dispatch_data_t remainingData, int error) { + my_global++; + dispatch_async(queue, ^{ + my_global++; + if (done) { + dispatch_semaphore_signal(sem); + } + }); + }); + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + my_global++; + dispatch_io_close(channel, 0); + channel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, path.fileSystemRepresentation, O_RDONLY, + 0, queue, ^(int error) { }); + dispatch_io_set_high_water(channel, 1); + + my_global++; + dispatch_io_read(channel, 0, SIZE_MAX, queue, ^(bool done, dispatch_data_t remainingData, int error) { + my_global++; + dispatch_async(queue, ^{ + my_global++; + if (done) { + dispatch_semaphore_signal(sem); + } + }); + }); + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + my_global++; + fprintf(stderr, "Done.\n"); + return 0; +} + +// CHECK: Hello world. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done. Index: test/tsan/Darwin/gcd-io-cleanup.mm =================================================================== --- test/tsan/Darwin/gcd-io-cleanup.mm +++ test/tsan/Darwin/gcd-io-cleanup.mm @@ -0,0 +1,56 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import + +long my_global = 0; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + dispatch_queue_t queue = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + dispatch_semaphore_t sem = dispatch_semaphore_create(0); + NSString *ns_path = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"temp-gcd-io.%d", getpid()]]; + const char *path = ns_path.fileSystemRepresentation; + dispatch_io_t channel; + + dispatch_fd_t fd = open(path, O_CREAT | O_WRONLY, 0666); + my_global++; + channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue, ^(int error) { + my_global++; + dispatch_semaphore_signal(sem); + }); + if (! channel) abort(); + my_global++; + dispatch_io_close(channel, 0); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + + my_global++; + channel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, path, O_CREAT | O_WRONLY, 0666, queue, ^(int error) { + my_global++; + dispatch_semaphore_signal(sem); + }); + if (! channel) abort(); + my_global++; + dispatch_io_close(channel, 0); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + + my_global++; + dispatch_io_t other_channel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, path, O_CREAT | O_WRONLY, 0666, queue, ^(int error) { }); + channel = dispatch_io_create_with_io(DISPATCH_IO_STREAM, other_channel, queue, ^(int error) { + my_global++; + dispatch_semaphore_signal(sem); + }); + if (! channel) abort(); + my_global++; + dispatch_io_close(channel, 0); + dispatch_io_close(other_channel, 0); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + + fprintf(stderr, "Done.\n"); + return 0; +} + +// CHECK: Hello world. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done. Index: test/tsan/Darwin/gcd-io-race.mm =================================================================== --- test/tsan/Darwin/gcd-io-race.mm +++ test/tsan/Darwin/gcd-io-race.mm @@ -0,0 +1,54 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %deflake %run %t 2>&1 | FileCheck %s + +#import + +#import "../test.h" + +dispatch_queue_t queue; +dispatch_data_t data; +dispatch_semaphore_t sem; +const char *path; + +long my_global = 0; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + print_address("addr=", 1, &my_global); + barrier_init(&barrier, 2); + + queue = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + sem = dispatch_semaphore_create(0); + NSString *ns_path = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"temp-gcd-io.%d", getpid()]]; + path = ns_path.fileSystemRepresentation; + NSData *ns_data = [NSMutableData dataWithLength:1000]; + data = dispatch_data_create(ns_data.bytes, ns_data.length, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + + dispatch_io_t channel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, path, O_CREAT | O_WRONLY, 0666, queue, ^(int error) { }); + if (! channel) abort(); + dispatch_io_set_high_water(channel, 1); + + dispatch_io_write(channel, 0, data, queue, ^(bool done, dispatch_data_t remainingData, int error) { + my_global = 42; + barrier_wait(&barrier); + }); + + dispatch_io_write(channel, 0, data, queue, ^(bool done, dispatch_data_t remainingData, int error) { + barrier_wait(&barrier); + my_global = 42; + + dispatch_semaphore_signal(sem); + }); + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + dispatch_io_close(channel, 0); + + fprintf(stderr, "Done.\n"); + return 0; +} + +// CHECK: Hello world. +// CHECK: addr=[[ADDR:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'my_global' {{(of size 8 )?}}at [[ADDR]] (gcd-io-race.mm.tmp+0x{{[0-9,a-f]+}}) +// CHECK: Done. Index: test/tsan/Darwin/gcd-io.mm =================================================================== --- test/tsan/Darwin/gcd-io.mm +++ test/tsan/Darwin/gcd-io.mm @@ -0,0 +1,117 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import + +dispatch_queue_t queue; +dispatch_data_t data; +dispatch_semaphore_t sem; +const char *path; + +long my_global = 0; + +void test_dispatch_io_write() { + dispatch_io_t channel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, path, O_CREAT | O_WRONLY, 0666, queue, ^(int error) { }); + if (! channel) abort(); + dispatch_io_set_high_water(channel, 1); + + my_global++; + dispatch_io_write(channel, 0, data, queue, ^(bool done, dispatch_data_t remainingData, int error) { + if (error) abort(); + my_global++; + dispatch_async(queue, ^{ + my_global++; + if (done) { + dispatch_semaphore_signal(sem); + } + }); + }); + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + my_global++; + dispatch_io_close(channel, 0); +} + +void test_dispatch_write() { + dispatch_fd_t fd = open(path, O_CREAT | O_WRONLY, 0666); + if (fd == -1) abort(); + + my_global++; + dispatch_write(fd, data, queue, ^(dispatch_data_t data, int error) { + if (error) abort(); + my_global++; + dispatch_async(queue, ^{ + my_global++; + + dispatch_semaphore_signal(sem); + }); + }); + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + my_global++; + close(fd); +} + +void test_dispatch_io_read() { + dispatch_io_t channel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, path, O_RDONLY, + 0, queue, ^(int error) { }); + dispatch_io_set_high_water(channel, 1); + + my_global++; + dispatch_io_read(channel, 0, SIZE_MAX, queue, ^(bool done, dispatch_data_t remainingData, int error) { + if (error) abort(); + my_global++; + dispatch_async(queue, ^{ + my_global++; + if (done) { + dispatch_semaphore_signal(sem); + } + }); + }); + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + my_global++; + dispatch_io_close(channel, 0); +} + +void test_dispatch_read() { + dispatch_fd_t fd = open(path, O_RDONLY, 0); + if (fd == -1) abort(); + + my_global++; + dispatch_read(fd, SIZE_MAX, queue, ^(dispatch_data_t data, int error) { + if (error) abort(); + my_global++; + dispatch_async(queue, ^{ + my_global++; + dispatch_semaphore_signal(sem); + }); + }); + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + my_global++; + close(fd); +} + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + queue = dispatch_queue_create("my.queue", DISPATCH_QUEUE_SERIAL); + sem = dispatch_semaphore_create(0); + NSString *ns_path = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"temp-gcd-io.%d", getpid()]]; + path = ns_path.fileSystemRepresentation; + NSData *ns_data = [NSMutableData dataWithLength:1000]; + data = dispatch_data_create(ns_data.bytes, ns_data.length, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + + test_dispatch_io_write(); + test_dispatch_write(); + test_dispatch_io_read(); + test_dispatch_read(); + + fprintf(stderr, "Done.\n"); + return 0; +} + +// CHECK: Hello world. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done.