Skip to content

Commit ee56a0f

Browse files
committed
Avoid abort on Linux when the current thread already owns the lock
1 parent 59165d2 commit ee56a0f

File tree

1 file changed

+27
-0
lines changed

1 file changed

+27
-0
lines changed

Sources/Testing/Support/Locked.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,16 @@ struct Locked<T> {
3030
private final class _Storage {
3131
let mutex: Mutex<T>
3232

33+
#if os(Linux) || os(Android)
34+
// The Linux implementation of Mutex terminates if `_tryLock()` is called on
35+
// the owning thread. (Other platforms just return `false`.) So, on Linux,
36+
// we also track the thread ID of the owner.
37+
let owningThreadID: Atomic<pid_t>
38+
#endif
39+
3340
init(_ rawValue: consuming sending T) {
3441
mutex = Mutex(rawValue)
42+
owningThreadID = Atomic(0)
3543
}
3644
}
3745
#endif
@@ -88,6 +96,12 @@ extension Locked {
8896
return result
8997
#else
9098
try _storage.mutex.withLock { rawValue in
99+
#if os(Linux) || os(Android)
100+
_storage.owningThreadID.store(gettid(), ordering: .sequentiallyConsistent)
101+
defer {
102+
_storage.owningThreadID.store(0, ordering: .sequentiallyConsistent)
103+
}
104+
#endif
91105
try body(&rawValue)
92106
}
93107
#endif
@@ -119,7 +133,20 @@ extension Locked {
119133
}
120134
return result
121135
#else
136+
#if os(Linux) || os(Android)
137+
let tid = gettid()
138+
if _storage.owningThreadID.load(ordering: .sequentiallyConsistent) == tid {
139+
// This thread already holds the lock.
140+
return nil
141+
}
142+
#endif
122143
try _storage.mutex.withLockIfAvailable { rawValue in
144+
#if os(Linux) || os(Android)
145+
_storage.owningThreadID.store(tid, ordering: .sequentiallyConsistent)
146+
defer {
147+
_storage.owningThreadID.store(0, ordering: .sequentiallyConsistent)
148+
}
149+
#endif
123150
try body(&rawValue)
124151
}
125152
#endif

0 commit comments

Comments
 (0)