Skip to content

Commit c785ecf

Browse files
authored
Merge pull request #67017 from al45tair/eng/PR-110665213-5.8
[Threading][TSan] Fix TSan errors from lazy init on Linux.
2 parents 32b31b9 + 1573ffb commit c785ecf

File tree

17 files changed

+323
-70
lines changed

17 files changed

+323
-70
lines changed

CMakeLists.txt

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

588+
option(SWIFT_THREADING_HAS_DLSYM
589+
"Enable the use of the dlsym() function. This gets used to provide TSan
590+
support on some platforms."
591+
TRUE)
592+
588593
#
589594
# End of user-configurable options.
590595
#

cmake/modules/AddSwiftUnittests.cmake

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

4949
# some headers switch their inline implementations based on
50-
# SWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY and
50+
# SWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY, SWIFT_STDLIB_HAS_DLSYM and
5151
# SWIFT_THREADING_PACKAGE definitions
5252
if(SWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY)
5353
target_compile_definitions("${test_dirname}" PRIVATE
5454
SWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY)
5555
endif()
56+
if(SWIFT_STDLIB_HAS_DLSYM)
57+
target_compile_definitions("${test_dirname}" PRIVATE
58+
"SWIFT_STDLIB_HAS_DLSYM=1")
59+
else()
60+
target_compile_definitions("${test_dirname}" PRIVATE
61+
"SWIFT_STDLIB_HAS_DLSYM=0")
62+
endif()
5663

5764
string(TOUPPER "${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_THREADING_PACKAGE}" _threading_package)
5865
target_compile_definitions("${test_dirname}" PRIVATE
59-
"SWIFT_THREADING_${_threading_package}")
66+
"SWIFT_THREADING_${_threading_package}"
67+
"SWIFT_THREADING_STATIC")
6068

6169
if(NOT SWIFT_COMPILER_IS_MSVC_LIKE)
6270
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
@@ -323,6 +323,16 @@ function(_add_target_variant_c_compile_flags)
323323
list(APPEND result "-DSWIFT_STDLIB_HAS_DLADDR")
324324
endif()
325325

326+
if(SWIFT_STDLIB_HAS_DLSYM)
327+
list(APPEND result "-DSWIFT_STDLIB_HAS_DLSYM=1")
328+
else()
329+
list(APPEND result "-DSWIFT_STDLIB_HAS_DLSYM=0")
330+
endif()
331+
332+
if(SWIFT_STDLIB_HAS_FILESYSTEM)
333+
list(APPEND result "-DSWIFT_STDLIB_HAS_FILESYSTEM")
334+
endif()
335+
326336
if(SWIFT_RUNTIME_STATIC_IMAGE_INSPECTION)
327337
list(APPEND result "-DSWIFT_RUNTIME_STATIC_IMAGE_INSPECTION")
328338
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
@@ -117,7 +117,6 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I
117117
TaskLocal.cpp
118118
TaskLocal.swift
119119
TaskSleep.swift
120-
ThreadSanitizer.cpp
121120
ThreadingError.cpp
122121
TracingSignpost.cpp
123122
AsyncStreamBuffer.swift

stdlib/public/Concurrency/TaskPrivate.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "swift/Runtime/Exclusivity.h"
2929
#include "swift/Runtime/HeapObject.h"
3030
#include "swift/Threading/Thread.h"
31+
#include "swift/Threading/ThreadSanitizer.h"
3132
#include <atomic>
3233
#include <new>
3334

@@ -97,10 +98,14 @@ void _swift_taskGroup_cancelAllChildren(TaskGroup *group);
9798
/// should generally use a higher-level function.
9899
void _swift_taskGroup_detachChild(TaskGroup *group, AsyncTask *child);
99100

100-
/// release() establishes a happens-before relation with a preceding acquire()
101-
/// on the same address.
102-
void _swift_tsan_acquire(void *addr);
103-
void _swift_tsan_release(void *addr);
101+
/// Tell TSan about an acquiring load
102+
inline void _swift_tsan_acquire(void *addr) {
103+
swift::tsan::acquire(addr);
104+
}
105+
/// Tell TSan about a releasing store
106+
inline void _swift_tsan_release(void *addr) {
107+
swift::tsan::release(addr);
108+
}
104109

105110
/// Special values used with DispatchQueueIndex to indicate the global and main
106111
/// executors.

0 commit comments

Comments
 (0)