Skip to content

Commit 97866bf

Browse files
authored
Merge pull request #66877 from al45tair/eng/PR-110665213-5.9
[Threading][TSan] Fix TSan errors from lazy init on Linux.
2 parents 197dc4b + dae1731 commit 97866bf

File tree

20 files changed

+353
-76
lines changed

20 files changed

+353
-76
lines changed

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,11 @@ option(SWIFT_THREADING_PACKAGE
651651
Valid package names are 'pthreads', 'darwin', 'linux', 'win32', 'c11', 'none'
652652
or the empty string for the SDK default.")
653653

654+
option(SWIFT_THREADING_HAS_DLSYM
655+
"Enable the use of the dlsym() function. This gets used to provide TSan
656+
support on some platforms."
657+
TRUE)
658+
654659
option(SWIFT_ENABLE_MACCATALYST
655660
"Build the Standard Library and overlays with MacCatalyst support"
656661
FALSE)

cmake/modules/AddSwiftUnittests.cmake

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,24 @@ function(add_swift_unittest test_dirname)
4949
endif()
5050

5151
# some headers switch their inline implementations based on
52-
# SWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY and
52+
# SWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY, SWIFT_STDLIB_HAS_DLSYM and
5353
# SWIFT_THREADING_PACKAGE definitions
5454
if(SWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY)
5555
target_compile_definitions("${test_dirname}" PRIVATE
5656
SWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY)
5757
endif()
58+
if(SWIFT_STDLIB_HAS_DLSYM)
59+
target_compile_definitions("${test_dirname}" PRIVATE
60+
"SWIFT_STDLIB_HAS_DLSYM=1")
61+
else()
62+
target_compile_definitions("${test_dirname}" PRIVATE
63+
"SWIFT_STDLIB_HAS_DLSYM=0")
64+
endif()
5865

5966
string(TOUPPER "${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_THREADING_PACKAGE}" _threading_package)
6067
target_compile_definitions("${test_dirname}" PRIVATE
61-
"SWIFT_THREADING_${_threading_package}")
68+
"SWIFT_THREADING_${_threading_package}"
69+
"SWIFT_THREADING_STATIC")
6270

6371
if(NOT SWIFT_COMPILER_IS_MSVC_LIKE)
6472
if(SWIFT_USE_LINKER)

include/swift/Threading/Impl/Linux/ulock.h

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
#include <atomic>
3535
#include <cstdint>
3636

37+
#include "swift/Threading/ThreadSanitizer.h"
38+
3739
namespace swift {
3840
namespace threading_impl {
3941
namespace linux {
@@ -59,31 +61,38 @@ inline void ulock_lock(ulock_t *lock) {
5961
const ulock_t tid = ulock_get_tid();
6062
do {
6163
ulock_t zero = 0;
62-
if (ulock_fastpath(__atomic_compare_exchange_n(
63-
lock, &zero, tid, true, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)))
64-
return;
65-
64+
if (ulock_fastpath(__atomic_compare_exchange_n(lock, &zero, tid,
65+
true, __ATOMIC_ACQUIRE,
66+
__ATOMIC_RELAXED)))
67+
break;
6668
} while (ulock_futex(lock, FUTEX_LOCK_PI) != 0);
69+
70+
tsan::acquire(lock);
6771
}
6872

6973
inline bool ulock_trylock(ulock_t *lock) {
7074
ulock_t zero = 0;
7175
if (ulock_fastpath(__atomic_compare_exchange_n(lock, &zero, ulock_get_tid(),
7276
true, __ATOMIC_ACQUIRE,
73-
__ATOMIC_RELAXED)))
77+
__ATOMIC_RELAXED))
78+
|| ulock_futex(lock, FUTEX_TRYLOCK_PI) == 0) {
79+
tsan::acquire(lock);
7480
return true;
81+
}
7582

76-
return ulock_futex(lock, FUTEX_TRYLOCK_PI) == 0;
83+
return false;
7784
}
7885

7986
inline void ulock_unlock(ulock_t *lock) {
87+
tsan::release(lock);
88+
8089
const ulock_t tid = ulock_get_tid();
8190
do {
8291
ulock_t expected = tid;
83-
if (ulock_fastpath(__atomic_compare_exchange_n(
84-
lock, &expected, 0, true, __ATOMIC_RELEASE, __ATOMIC_RELAXED)))
85-
return;
86-
92+
if (ulock_fastpath(__atomic_compare_exchange_n(lock, &expected, 0,
93+
true, __ATOMIC_RELEASE,
94+
__ATOMIC_RELAXED)))
95+
break;
8796
} while (ulock_futex(lock, FUTEX_UNLOCK_PI) != 0);
8897
}
8998

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
//===--- ThreadSanitizer.h - Thread Sanitizer support --------- -*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Helper functions for code that needs to integrate with the thread
14+
// sanitizer. In particular, TSan can't see inside the runtime libraries,
15+
// so we occasionally need to give it a hint that we're doing synchronization
16+
// in order to avoid false positives.
17+
//
18+
//===----------------------------------------------------------------------===//
19+
20+
#ifndef SWIFT_THREADING_THREAD_SANITIZER_H
21+
#define SWIFT_THREADING_THREAD_SANITIZER_H
22+
23+
#include "swift/shims/Visibility.h"
24+
25+
namespace swift {
26+
27+
#if SWIFT_THREADING_NONE \
28+
|| defined(_WIN32) || defined(__wasi__) \
29+
|| !__has_include(<dlfcn.h>) \
30+
|| (defined(SWIFT_STDLIB_HAS_DLSYM) && !SWIFT_STDLIB_HAS_DLSYM)
31+
32+
#define SWIFT_THREADING_TSAN_SUPPORT 0
33+
34+
namespace tsan {
35+
36+
inline bool enabled() { return false; }
37+
template <typename T> T *acquire(T *ptr) { return ptr; }
38+
template <typename T> T *release(T *ptr) { return ptr; }
39+
40+
} // namespace tsan
41+
42+
#else
43+
44+
#define SWIFT_THREADING_TSAN_SUPPORT 1
45+
46+
// If we're static linking to libswiftThreading.a, these symbols can come
47+
// from there. If, on the other hand, we're dynamically linked, we want
48+
// to get them from libswiftCore.dylib instead.
49+
#if SWIFT_THREADING_STATIC
50+
#define SWIFT_THREADING_EXPORT extern "C"
51+
#else
52+
#define SWIFT_THREADING_EXPORT SWIFT_RUNTIME_EXPORT
53+
#endif
54+
55+
namespace threading_impl {
56+
57+
SWIFT_THREADING_EXPORT bool _swift_tsan_enabled;
58+
SWIFT_THREADING_EXPORT void (*_swift_tsan_acquire)(const void *ptr);
59+
SWIFT_THREADING_EXPORT void (*_swift_tsan_release)(const void *ptr);
60+
61+
} // namespace threading_impl
62+
63+
namespace tsan {
64+
65+
/// Returns true if TSan is enabled
66+
inline bool enabled() {
67+
return threading_impl::_swift_tsan_enabled;
68+
}
69+
70+
/// Inform TSan about a synchronization operation.
71+
///
72+
/// This is used when TSan cannot see the synchronization operation, for
73+
/// example, if it is using a custom primitive for which TSan doesn't have
74+
/// a built-in interceptor. This does not necessarily mean a lock or a C(++)
75+
/// atomic operation - it could be any kind of synchronization mechanism.
76+
///
77+
/// An acquire-release pair using the same address establishes an ordering
78+
/// constraint in TSan's happens-before graph, which TSan uses to determine
79+
/// whether two memory accesses from different threads have a well-defined
80+
/// order.
81+
///
82+
/// For instance, in
83+
///
84+
/// Thread 1 Thread 2
85+
///
86+
/// access to y
87+
/// tsan::release(x)
88+
/// lock given away
89+
///
90+
/// --> sync point -->
91+
///
92+
/// lock taken
93+
/// tsan::acquire(x)
94+
/// access to y
95+
///
96+
/// the access to y from Thread 2 is safe relative to the preceding access to
97+
/// y on Thread 1 because it is preceded by an acquire of x that was itself
98+
/// preceded by a release of x.
99+
template <typename T>
100+
T *acquire(T *ptr) {
101+
if (threading_impl::_swift_tsan_acquire) {
102+
threading_impl::_swift_tsan_acquire(ptr);
103+
}
104+
return ptr;
105+
}
106+
107+
/// Inform TSan about a synchronization operation.
108+
///
109+
/// This is the counterpart to tsan::acquire.
110+
template <typename T>
111+
T *release(T *ptr) {
112+
if (threading_impl::_swift_tsan_release) {
113+
threading_impl::_swift_tsan_release(ptr);
114+
}
115+
return ptr;
116+
}
117+
118+
} // namespace tsan
119+
120+
#endif
121+
122+
} // namespace swift
123+
124+
#endif

lib/Threading/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ add_swift_host_library(swiftThreading STATIC
1010
Linux.cpp
1111
Pthreads.cpp
1212
Win32.cpp
13-
Errors.cpp)
13+
Errors.cpp
14+
ThreadSanitizer.cpp)

lib/Threading/Linux.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "swift/Threading/Impl.h"
2020
#include "swift/Threading/Errors.h"
21+
#include "swift/Threading/ThreadSanitizer.h"
2122

2223
namespace {
2324

@@ -61,7 +62,7 @@ void swift::threading_impl::once_slow(once_t &predicate, void (*fn)(void *),
6162
#endif
6263
if (predicate.flag.load(std::memory_order_acquire) == 0) {
6364
fn(context);
64-
predicate.flag.store(-1, std::memory_order_release);
65+
predicate.flag.store(tsan::enabled() ? 1 : -1, std::memory_order_release);
6566
}
6667
#if defined(__LP64__) || defined(_LP64)
6768
linux::ulock_unlock(&predicate.lock);

lib/Threading/ThreadSanitizer.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//===--- ThreadSanitizer.cpp - Thread Sanitizer support -------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Helper functions for code that needs to integrate with the thread
14+
// sanitizer. In particular, TSan can't see inside the runtime libraries,
15+
// so we occasionally need to give it a hint that we're doing synchronization
16+
// in order to avoid false positives.
17+
//
18+
//===----------------------------------------------------------------------===//
19+
20+
#include "swift/Threading/ThreadSanitizer.h"
21+
22+
#if SWIFT_THREADING_TSAN_SUPPORT
23+
24+
#include "swift/shims/Visibility.h"
25+
26+
#include <dlfcn.h>
27+
28+
#include <cstdio>
29+
30+
namespace swift {
31+
namespace threading_impl {
32+
33+
SWIFT_THREADING_EXPORT bool _swift_tsan_enabled = false;
34+
SWIFT_THREADING_EXPORT void (*_swift_tsan_acquire)(const void *) = nullptr;
35+
SWIFT_THREADING_EXPORT void (*_swift_tsan_release)(const void *) = nullptr;
36+
37+
// The TSan library code will call this function when it starts up
38+
extern "C" SWIFT_ATTRIBUTE_FOR_EXPORTS
39+
void __tsan_on_initialize() {
40+
_swift_tsan_enabled = true;
41+
_swift_tsan_acquire = (void (*)(const void *))dlsym(RTLD_DEFAULT,
42+
"__tsan_acquire");
43+
_swift_tsan_release = (void (*)(const void *))dlsym(RTLD_DEFAULT,
44+
"__tsan_release");
45+
46+
// Always call through to the next image; this won't work on macOS, but it's
47+
// important on Linux to allow others to hook into the thread sanitizer if
48+
// they wish.
49+
void (*next_init)(void);
50+
next_init = (void (*)(void))dlsym(RTLD_NEXT, "__tsan_on_initialize");
51+
if (next_init) {
52+
next_init();
53+
}
54+
}
55+
56+
} // namespace threading_impl
57+
} // namespace swift
58+
59+
#endif // SWIFT_THREADING_TSAN_SUPPORT

stdlib/cmake/modules/AddSwiftStdlib.cmake

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,16 @@ function(_add_target_variant_c_compile_flags)
352352
list(APPEND result "-DSWIFT_STDLIB_HAS_DLADDR")
353353
endif()
354354

355+
if(SWIFT_STDLIB_HAS_DLSYM)
356+
list(APPEND result "-DSWIFT_STDLIB_HAS_DLSYM=1")
357+
else()
358+
list(APPEND result "-DSWIFT_STDLIB_HAS_DLSYM=0")
359+
endif()
360+
361+
if(SWIFT_STDLIB_HAS_FILESYSTEM)
362+
list(APPEND result "-DSWIFT_STDLIB_HAS_FILESYSTEM")
363+
endif()
364+
355365
if(SWIFT_RUNTIME_STATIC_IMAGE_INSPECTION)
356366
list(APPEND result "-DSWIFT_RUNTIME_STATIC_IMAGE_INSPECTION")
357367
endif()

stdlib/cmake/modules/StdlibOptions.cmake

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,15 @@ option(SWIFT_STDLIB_BUILD_PRIVATE
135135
TRUE)
136136

137137
option(SWIFT_STDLIB_HAS_DLADDR
138-
"Build stdlib assuming the runtime environment runtime environment provides dladdr API."
138+
"Build stdlib assuming the runtime environment provides the dladdr API."
139+
TRUE)
140+
141+
option(SWIFT_STDLIB_HAS_DLSYM
142+
"Build stdlib assuming the runtime environment provides the dlsym API."
143+
TRUE)
144+
145+
option(SWIFT_STDLIB_HAS_FILESYSTEM
146+
"Build stdlib assuming the runtime environment has a filesystem."
139147
TRUE)
140148

141149
option(SWIFT_RUNTIME_STATIC_IMAGE_INSPECTION

stdlib/public/Concurrency/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,6 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I
120120
TaskLocal.cpp
121121
TaskLocal.swift
122122
TaskSleep.swift
123-
ThreadSanitizer.cpp
124123
ThreadingError.cpp
125124
TracingSignpost.cpp
126125
AsyncStreamBuffer.swift

stdlib/public/Concurrency/TaskPrivate.h

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "swift/Runtime/Exclusivity.h"
3030
#include "swift/Runtime/HeapObject.h"
3131
#include "swift/Threading/Thread.h"
32+
#include "swift/Threading/ThreadSanitizer.h"
3233
#include <atomic>
3334
#include <new>
3435

@@ -99,15 +100,23 @@ void _swift_taskGroup_cancelAllChildren(TaskGroup *group);
99100
/// should generally use a higher-level function.
100101
void _swift_taskGroup_detachChild(TaskGroup *group, AsyncTask *child);
101102

102-
/// release() establishes a happens-before relation with a preceding acquire()
103-
/// on the same address.
104-
void _swift_tsan_acquire(void *addr);
105-
void _swift_tsan_release(void *addr);
106-
/// Technically, this consume relies on implicit HW address dependency ordering
107-
/// and is paired with a corresponding release. Since TSAN doesn't know how to
108-
/// reason about this, we tell TSAN it's an acquire instead. See also
109-
/// SWIFT_MEMORY_ORDER_CONSUME definition.
110-
#define _swift_tsan_consume _swift_tsan_acquire
103+
/// Tell TSan about an acquiring load
104+
inline void _swift_tsan_acquire(void *addr) {
105+
swift::tsan::acquire(addr);
106+
}
107+
/// Tell TSan about a releasing store
108+
inline void _swift_tsan_release(void *addr) {
109+
swift::tsan::release(addr);
110+
}
111+
/// Tell TSan about a consuming load
112+
inline void _swift_tsan_consume(void *addr) {
113+
// TSan doesn't support consume, so pretend it's an acquire.
114+
//
115+
// Note that that means that TSan won't generate errors for non-dependent
116+
// reads, so this isn't entirely safe if you're relying solely on TSan to
117+
// spot bugs.
118+
swift::tsan::acquire(addr);
119+
}
111120

112121
/// Special values used with DispatchQueueIndex to indicate the global and main
113122
/// executors.

0 commit comments

Comments
 (0)