Files
swift-mirror/stdlib/public/Concurrency/ThreadSanitizer.cpp
Julian Lettner 558a0cc605 Add tsan_release edge on task creation (#38074)
* Synchronize both versions of actor_counters.swift test

* Synchronize on Job address

Make sure to synchronize on Job address (AsyncTasks are Jobs, but not
all Jobs are AsyncTasks).

* Add fprintf debug output for TSan acquire/release

* Add tsan_release edge on task creation

without this, we are getting false data races between when a task
is created and immediately scheduled on a different thread.

False positive for `Sanitizers/tsan/actor_counters.swift` test:
```
WARNING: ThreadSanitizer: data race (pid=81452)
  Read of size 8 at 0x7b2000000560 by thread T5:
    #0 Counter.next() <null>:2 (a.out:x86_64+0x1000047f8)
    #1 (1) suspend resume partial function for worker(identity:counters:numIterations:) <null>:2 (a.out:x86_64+0x100005961)
    #2 swift::runJobInEstablishedExecutorContext(swift::Job*) <null>:2 (libswift_Concurrency.dylib:x86_64+0x280ef)

  Previous write of size 8 at 0x7b2000000560 by main thread:
    #0 Counter.init(maxCount:) <null>:2 (a.out:x86_64+0x1000046af)
    #1 Counter.__allocating_init(maxCount:) <null>:2 (a.out:x86_64+0x100004619)
    #2 runTest(numCounters:numWorkers:numIterations:) <null>:2 (a.out:x86_64+0x100006d2e)
    #3 swift::runJobInEstablishedExecutorContext(swift::Job*) <null>:2 (libswift_Concurrency.dylib:x86_64+0x280ef)
    #4 main <null>:2 (a.out:x86_64+0x10000a175)
```

New edge with this change:
```
[4357150208] allocate task 0x7b3800000000, parent = 0x0
[4357150208] creating task 0x7b3800000000 with parent 0x0
[4357150208] tsan_release on 0x7b3800000000    <<< new release edge
[139088221442048] tsan_acquire on 0x7b3800000000
[139088221442048] trying to switch from executor 0x0 to 0x7ff85e2d9a00
[139088221442048] switch failed, task 0x7b3800000000 enqueued on executor 0x7ff85e2d9a00
[139088221442048] enqueue job 0x7b3800000000 on executor 0x7ff85e2d9a00
[139088221442048] tsan_release on 0x7b3800000000
[139088221442048] tsan_release on 0x7b3800000000
[4357150208] tsan_acquire on 0x7b3800000000
counters: 1, workers: 1, iterations: 1
[4357150208] allocate task 0x7b3c00000000, parent = 0x0
[4357150208] creating task 0x7b3c00000000 with parent 0x0
[4357150208] tsan_release on 0x7b3c00000000    <<< new release edge
[139088221442048] tsan_acquire on 0x7b3c00000000
[4357150208] task 0x7b3800000000 waiting on task 0x7b3c00000000, going to sleep
[4357150208] tsan_release on 0x7b3800000000
[4357150208] tsan_release on 0x7b3800000000
[139088221442048] getting current executor 0x0
[139088221442048] tsan_release on 0x7b3c00000000
...
```

rdar://78932849

* Add static_cast<Job *>()

* Move TSan release edge to swift_task_enqueueGlobal()

Move the TSan release edge from `swift_task_create_commonImpl()` to
`swift_task_enqueueGlobalImpl()`.  Task creation itself is not an event
that needs synchronization, but rather that task creation "happens
before" execution of that task on another thread.

This edge is usually added when the task is scheduled via
`swift_task_enqueue()` (which then usually calls
`swift_task_enqueueGlobal()`).  However, not all task scheduling goes
through the `swift_task_enqueue()` funnel as some places call the more
specific `swift_task_enqueueGlobal()` directly.  So let's annotate this
function (duplicate edges aren't harmful) to ensure we cover all
schedule events, including newly-created tasks (our original problem
here).

rdar://78932849

Co-authored-by: Julian Lettner <julian.lettner@apple.com>
2021-06-25 18:40:53 -07:00

55 lines
1.6 KiB
C++

//===--- ThreadSanitizer.cpp - Thread Sanitizer support -------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Thread Sanitizer support for the Swift Task runtime.
//
//===----------------------------------------------------------------------===//
#include "TaskPrivate.h"
// Thread Sanitizer is not supported on Windows.
#if defined(_WIN32)
void swift::_swift_tsan_acquire(void *addr) {}
void swift::_swift_tsan_release(void *addr) {}
#else
#include <dlfcn.h>
namespace {
using TSanFunc = void(void *);
TSanFunc *tsan_acquire, *tsan_release;
} // anonymous namespace
void swift::_swift_tsan_acquire(void *addr) {
if (tsan_acquire) {
tsan_acquire(addr);
#if SWIFT_TASK_PRINTF_DEBUG
fprintf(stderr, "[%lu] tsan_acquire on %p\n", _swift_get_thread_id(), addr);
#endif
}
}
void swift::_swift_tsan_release(void *addr) {
if (tsan_release) {
tsan_release(addr);
#if SWIFT_TASK_PRINTF_DEBUG
fprintf(stderr, "[%lu] tsan_release on %p\n", _swift_get_thread_id(), addr);
#endif
}
}
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(c)
void __tsan_on_initialize() {
tsan_acquire = (TSanFunc *)dlsym(RTLD_DEFAULT, "__tsan_acquire");
tsan_release = (TSanFunc *)dlsym(RTLD_DEFAULT, "__tsan_release");
}
#endif