diff options
author | Julian Lettner <jlettner@apple.com> | 2019-03-14 20:59:41 +0000 |
---|---|---|
committer | Julian Lettner <jlettner@apple.com> | 2019-03-14 20:59:41 +0000 |
commit | 4d0745cad5835866631e43d024fd61081adf3e8c (patch) | |
tree | cf2a512abc2392170c4106309085500020ae3fc9 /test/tsan/libdispatch | |
parent | c90e46e7d4ba2f556a765a5808989d1eb36fb001 (diff) | |
download | compiler-rt-4d0745cad5835866631e43d024fd61081adf3e8c.tar.gz |
[NFC][TSan] Move libdispatch tests into their own subfolder
Remove 'gcd' file prefix. GCD stands for Grand Central Dispatch, which
is another name for libdispatch.
https://apple.github.io/swift-corelibs-libdispatch/
Remove `REQUIRE: dispatch` from tests.
Also rename lit feature 'dispatch' -> 'libdispatch' to be more explicit
what this is about.
Reviewed By: kubamracek
Differential Revision: https://reviews.llvm.org/D59341
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@356202 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'test/tsan/libdispatch')
-rw-r--r-- | test/tsan/libdispatch/apply-race.c | 30 | ||||
-rw-r--r-- | test/tsan/libdispatch/apply.c | 52 | ||||
-rw-r--r-- | test/tsan/libdispatch/data.c | 41 | ||||
-rw-r--r-- | test/tsan/libdispatch/dispatch_main.c | 41 | ||||
-rw-r--r-- | test/tsan/libdispatch/dispatch_once_deadlock.c | 43 | ||||
-rw-r--r-- | test/tsan/libdispatch/groups-destructor.c | 43 | ||||
-rw-r--r-- | test/tsan/libdispatch/groups-leave.c | 56 | ||||
-rw-r--r-- | test/tsan/libdispatch/groups-stress.c | 45 | ||||
-rw-r--r-- | test/tsan/libdispatch/lit.local.cfg | 11 | ||||
-rw-r--r-- | test/tsan/libdispatch/once.c | 55 | ||||
-rw-r--r-- | test/tsan/libdispatch/semaphore-norace.c | 31 | ||||
-rw-r--r-- | test/tsan/libdispatch/source-serial.c | 38 | ||||
-rw-r--r-- | test/tsan/libdispatch/suspend.c | 47 |
13 files changed, 533 insertions, 0 deletions
diff --git a/test/tsan/libdispatch/apply-race.c b/test/tsan/libdispatch/apply-race.c new file mode 100644 index 000000000..10e954c23 --- /dev/null +++ b/test/tsan/libdispatch/apply-race.c @@ -0,0 +1,30 @@ +// RUN: %clang_tsan %s -o %t +// RUN: %deflake %run %t 2>&1 | FileCheck %s + +#include <dispatch/dispatch.h> + +#include "../test.h" + +long global; + +int main(int argc, const char *argv[]) { + barrier_init(&barrier, 2); + fprintf(stderr, "start\n"); + + // Warm up GCD (workaround for macOS Sierra where dispatch_apply might run single-threaded). + dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ }); + + dispatch_queue_t q = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + dispatch_apply(2, q, ^(size_t i) { + global = i; + barrier_wait(&barrier); + }); + + fprintf(stderr, "done\n"); + return 0; +} + +// CHECK: start +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'global' +// CHECK: done diff --git a/test/tsan/libdispatch/apply.c b/test/tsan/libdispatch/apply.c new file mode 100644 index 000000000..d5c991f32 --- /dev/null +++ b/test/tsan/libdispatch/apply.c @@ -0,0 +1,52 @@ +// RUN: %clang_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +// TODO(yln): Deadlocks while gcd-apply.mm does not. What's the difference +// between C and Obj-C compiler? +// REQUIRES: disable + +#include <dispatch/dispatch.h> + +#include "../test.h" + +long global; +long array[2]; + +void callback(void *context, size_t i) { + long n = global; + array[i] = n + i; + barrier_wait(&barrier); +} + +int main(int argc, const char *argv[]) { + barrier_init(&barrier, 2); + fprintf(stderr, "start\n"); + + // Warm up GCD (workaround for macOS Sierra where dispatch_apply might run single-threaded). + dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ }); + + dispatch_queue_t q = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + + global = 42; + + dispatch_apply(100, q, ^(size_t i) { + long n = global; + array[i] = n + i; + barrier_wait(&barrier); + }); + + for (int i = 0; i < 100; i++) { + fprintf(stderr, "array[%d] = %ld\n", i, array[i]); + } + + global = 43; + + dispatch_apply_f(100, q, NULL, &callback); + + fprintf(stderr, "done\n"); + return 0; +} + +// CHECK: start +// CHECK: done +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/libdispatch/data.c b/test/tsan/libdispatch/data.c new file mode 100644 index 000000000..7a6975bdc --- /dev/null +++ b/test/tsan/libdispatch/data.c @@ -0,0 +1,41 @@ +// RUN: %clang_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +// TODO(yln): fails on one of our bots, need to investigate +// REQUIRES: disabled + +#include <dispatch/dispatch.h> + +#include <stdio.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); + + global = 44; + dispatch_data_t data = dispatch_data_create("buffer", 6, q, ^{ + fprintf(stderr, "Data destructor.\n"); + global++; + + dispatch_semaphore_signal(sem); + }); + dispatch_release(data); + data = NULL; + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + + data = dispatch_data_create("buffer", 6, q, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + dispatch_release(data); + data = NULL; + + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK: Data destructor. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done. diff --git a/test/tsan/libdispatch/dispatch_main.c b/test/tsan/libdispatch/dispatch_main.c new file mode 100644 index 000000000..43b0d08cc --- /dev/null +++ b/test/tsan/libdispatch/dispatch_main.c @@ -0,0 +1,41 @@ +// Check that we don't crash when dispatch_main calls pthread_exit which +// quits the main thread. + +// RUN: %clang_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include <dispatch/dispatch.h> + +#include <stdio.h> +#include <stdlib.h> + +int main() { + fprintf(stderr,"Hello world"); + + dispatch_queue_t q = dispatch_queue_create("my.queue", DISPATCH_QUEUE_SERIAL); + + dispatch_async(q, ^{ + fprintf(stderr,"1"); + }); + + dispatch_async(q, ^{ + fprintf(stderr,"2"); + }); + + dispatch_async(q, ^{ + fprintf(stderr,"3"); + + dispatch_async(dispatch_get_main_queue(), ^{ + fprintf(stderr,"Done."); + sleep(1); + exit(0); + }); + }); + + dispatch_main(); +} + +// CHECK: Hello world +// CHECK: Done. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK-NOT: CHECK failed diff --git a/test/tsan/libdispatch/dispatch_once_deadlock.c b/test/tsan/libdispatch/dispatch_once_deadlock.c new file mode 100644 index 000000000..1e9e7d139 --- /dev/null +++ b/test/tsan/libdispatch/dispatch_once_deadlock.c @@ -0,0 +1,43 @@ +// Check that calling dispatch_once from a report callback works. + +// RUN: %clang_tsan %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <dispatch/dispatch.h> + +#include <pthread.h> +#include <stdio.h> + +long g = 0; +long h = 0; +void f() { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + g++; + }); + h++; +} + +void __tsan_on_report() { + fprintf(stderr, "Report.\n"); + f(); +} + +int main() { + fprintf(stderr, "Hello world.\n"); + + f(); + + pthread_mutex_t mutex = {0}; + pthread_mutex_lock(&mutex); + + fprintf(stderr, "g = %ld.\n", g); + fprintf(stderr, "h = %ld.\n", h); + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK: Report. +// CHECK: g = 1 +// CHECK: h = 2 +// CHECK: Done. diff --git a/test/tsan/libdispatch/groups-destructor.c b/test/tsan/libdispatch/groups-destructor.c new file mode 100644 index 000000000..100f6fc7c --- /dev/null +++ b/test/tsan/libdispatch/groups-destructor.c @@ -0,0 +1,43 @@ +// RUN: %clangxx_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include <dispatch/dispatch.h> + +#import <memory> +#import <stdatomic.h> + +_Atomic(long) destructor_counter = 0; + +struct MyStruct { + virtual ~MyStruct() { + usleep(10000); + atomic_fetch_add_explicit(&destructor_counter, 1, memory_order_relaxed); + } +}; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_group_t g = dispatch_group_create(); + + for (int i = 0; i < 100; i++) { + std::shared_ptr<MyStruct> shared(new MyStruct()); + + dispatch_group_async(g, q, ^{ + shared.get(); // just to make sure the object is captured by the block + }); + } + + dispatch_group_wait(g, DISPATCH_TIME_FOREVER); + + if (destructor_counter != 100) { + abort(); + } + + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done. diff --git a/test/tsan/libdispatch/groups-leave.c b/test/tsan/libdispatch/groups-leave.c new file mode 100644 index 000000000..0e487870a --- /dev/null +++ b/test/tsan/libdispatch/groups-leave.c @@ -0,0 +1,56 @@ +// RUN: %clang_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include <dispatch/dispatch.h> + +#include "../test.h" + +dispatch_semaphore_t sem; + +long global; +long global2; + +void callback(void *context) { + global2 = 48; + barrier_wait(&barrier); + + dispatch_semaphore_signal(sem); +} + +int main() { + fprintf(stderr, "Hello world.\n"); + barrier_init(&barrier, 2); + + dispatch_queue_t q = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + dispatch_group_t g = dispatch_group_create(); + sem = dispatch_semaphore_create(0); + + dispatch_group_enter(g); + dispatch_async(q, ^{ + global = 47; + dispatch_group_leave(g); + barrier_wait(&barrier); + }); + dispatch_group_notify(g, q, ^{ + global = 48; + barrier_wait(&barrier); + + dispatch_semaphore_signal(sem); + }); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + + dispatch_group_enter(g); + dispatch_async(q, ^{ + global2 = 47; + dispatch_group_leave(g); + barrier_wait(&barrier); + }); + dispatch_group_notify_f(g, q, NULL, &callback); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done. diff --git a/test/tsan/libdispatch/groups-stress.c b/test/tsan/libdispatch/groups-stress.c new file mode 100644 index 000000000..0fdaac050 --- /dev/null +++ b/test/tsan/libdispatch/groups-stress.c @@ -0,0 +1,45 @@ +// RUN: %clang_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include <dispatch/dispatch.h> + +#include <stdio.h> + +void notify_callback(void *context) { + // Do nothing. +} + +int main() { + fprintf(stderr, "Hello world."); + + dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + + for (int i = 0; i < 300000; i++) { + dispatch_group_t g = dispatch_group_create(); + dispatch_group_enter(g); + dispatch_async(q, ^{ + dispatch_group_leave(g); + }); + dispatch_group_notify(g, q, ^{ + // Do nothing. + }); + dispatch_release(g); + } + + for (int i = 0; i < 300000; i++) { + dispatch_group_t g = dispatch_group_create(); + dispatch_group_enter(g); + dispatch_async(q, ^{ + dispatch_group_leave(g); + }); + dispatch_group_notify_f(g, q, NULL, ¬ify_callback); + dispatch_release(g); + } + + fprintf(stderr, "Done."); +} + +// CHECK: Hello world. +// CHECK: Done. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK-NOT: CHECK failed diff --git a/test/tsan/libdispatch/lit.local.cfg b/test/tsan/libdispatch/lit.local.cfg new file mode 100644 index 000000000..b312a3a1f --- /dev/null +++ b/test/tsan/libdispatch/lit.local.cfg @@ -0,0 +1,11 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +if 'libdispatch' not in root.available_features: + config.unsupported = True + +config.target_cflags += ' -fblocks' diff --git a/test/tsan/libdispatch/once.c b/test/tsan/libdispatch/once.c new file mode 100644 index 000000000..00227b88e --- /dev/null +++ b/test/tsan/libdispatch/once.c @@ -0,0 +1,55 @@ +// RUN: %clang_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include <dispatch/dispatch.h> + +#include "../test.h" + +static const long kNumThreads = 4; + +long global; +long global2; + +static dispatch_once_t once_token; +static dispatch_once_t once_token2; + +void f(void *a) { + global2 = 42; + usleep(100000); +} + +void *Thread(void *a) { + barrier_wait(&barrier); + + dispatch_once(&once_token, ^{ + global = 42; + usleep(100000); + }); + long x = global; + + dispatch_once_f(&once_token2, NULL, f); + long x2 = global2; + + fprintf(stderr, "global = %ld\n", x); + fprintf(stderr, "global2 = %ld\n", x2); + return 0; +} + +int main() { + fprintf(stderr, "Hello world.\n"); + barrier_init(&barrier, kNumThreads); + + pthread_t t[kNumThreads]; + for (int i = 0; i < kNumThreads; i++) { + pthread_create(&t[i], 0, Thread, 0); + } + for (int i = 0; i < kNumThreads; i++) { + pthread_join(t[i], 0); + } + + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK: Done. +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/libdispatch/semaphore-norace.c b/test/tsan/libdispatch/semaphore-norace.c new file mode 100644 index 000000000..ce94a3cbf --- /dev/null +++ b/test/tsan/libdispatch/semaphore-norace.c @@ -0,0 +1,31 @@ +// RUN: %clang_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include <dispatch/dispatch.h> + +#include <stdio.h> + +long global; + +int main() { + fprintf(stderr, "Hello world."); + + global = 42; + + dispatch_semaphore_t sem = dispatch_semaphore_create(0); + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + + global = 43; + dispatch_semaphore_signal(sem); + }); + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + global = 44; + + fprintf(stderr, "Done."); + return 0; +} + +// CHECK: Hello world. +// CHECK: Done. +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/libdispatch/source-serial.c b/test/tsan/libdispatch/source-serial.c new file mode 100644 index 000000000..79dc1afea --- /dev/null +++ b/test/tsan/libdispatch/source-serial.c @@ -0,0 +1,38 @@ +// RUN: %clang_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +// TODO(yln): fails on one of our bots, need to investigate +// REQUIRES: disabled + +#include <dispatch/dispatch.h> + +#include <stdio.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. diff --git a/test/tsan/libdispatch/suspend.c b/test/tsan/libdispatch/suspend.c new file mode 100644 index 000000000..5566faf0c --- /dev/null +++ b/test/tsan/libdispatch/suspend.c @@ -0,0 +1,47 @@ +// RUN: %clang_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include <dispatch/dispatch.h> + +#include <stdio.h> + +long my_global = 0; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + dispatch_queue_t q1 = dispatch_queue_create("queue1", NULL); + dispatch_queue_t q2 = dispatch_queue_create("queue2", NULL); + dispatch_group_t g = dispatch_group_create(); + + dispatch_sync(q1, ^{ + dispatch_suspend(q1); + dispatch_async(q2, ^{ + my_global++; + dispatch_resume(q1); + }); + }); + + dispatch_sync(q1, ^{ + my_global++; + }); + + dispatch_sync(q1, ^{ + dispatch_suspend(q1); + dispatch_group_enter(g); + dispatch_async(q1,^{ my_global++; }); + dispatch_async(q1,^{ my_global++; }); + dispatch_async(q1,^{ my_global++; dispatch_group_leave(g); }); + my_global++; + dispatch_resume(q1); + }); + + dispatch_group_wait(g, DISPATCH_TIME_FOREVER); + + fprintf(stderr, "Done.\n"); + return 0; +} + +// CHECK: Hello world. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done. |