-
Notifications
You must be signed in to change notification settings - Fork 698
NIOThread refactor: pthread_t lifetimes #3297
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
21622e1
to
50c018f
Compare
} | ||
""" | ||
} else { | ||
return self.externalStateLock.withLock { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the lock-holding concern not as relevant here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- it already holds a lock, so I'm not making things worse I think :P
- it's
debugDescription
so maybe less likely to be called?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically replicating prior art in the existing impl
@@ -42,41 +44,74 @@ protocol ThreadOps { | |||
/// All methods exposed are thread-safe. | |||
@usableFromInline | |||
final class NIOThread: Sendable { | |||
internal typealias ThreadBoxValue = (body: (NIOThread) -> Void, name: String?) | |||
internal typealias ThreadBoxValue = (body: (NIOThread) -> Void, name: String?, isDetached: Bool) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we tweak this API to try to discourage creating detached threads outside of tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Lukasa Honestly, I'm planning a follow-up to remove the ability to use detached threads at all, we don't use them outside of tests and we don't need them in tests.
### Motivation: PR #3297 is changing `inEventLoop` and `inEventLoop` is very much on the perf path. So let's add a benchmark ### Modifications: - Add `(!)inEventLoop` benchmark - Fix a guaranteed deadlock in `TCPThroughputBenchmark` to make the benchmarks runnable ### Result: - More benchmarks - Fewer hangs
### Motivation: `inEventLoop` is very much in the performance path of SwiftNIO, especially these days with Concurrency, `NIOLoopBound` and friends. Previously, we relied on `pthread_equal(pthread_self(), myPthread)`, however, this could cause a number of issues. 1. Holding onto a `pthread_t` after `.join` is actually illegal (fixed in #3297) 2. Potential ABA issues when `pthread_t` pointer are re-used for new pthreads 3. Fix would require a lock around `myPthread` which makes things (2x slower, even without contention) ### Modifications: - New type `SelectableEventLoopUniqueID` which can be packed into a `UInt` - Attach them into a C thread local ### Result: - Even faster than the old, incorrect version - old: `measuring: el_in_eventloop_100M: 0.257395375, 0.241049208, 0.243188792, 0.259125916, 0.24843225, 0.229690125, 0.244281541, 0.225078834, 0.236395, 0.233305167` - new: `measuring: el_in_eventloop_100M: 0.175561125, 0.187225625, 0.199269375, 0.19740975, 0.1922695, 0.179850958, 0.177612458, 0.17665125, 0.17897475, 0.18038775` - More correct - Groundwork to make #3297 not make things slower
112eb96
to
d22b9c5
Compare
d22b9c5
to
98ab4ea
Compare
f9e0e30
to
78dc04a
Compare
78dc04a
to
5c4f499
Compare
### Motivation: `inEventLoop` is very much in the performance path of SwiftNIO, especially these days with Concurrency, `NIOLoopBound` and friends. Previously, we relied on `pthread_equal(pthread_self(), myPthread)`, however, this could cause a number of issues. 1. Holding onto a `pthread_t` after `.join` is actually illegal (fixed in apple#3297) 2. Potential ABA issues when `pthread_t` pointer are re-used for new pthreads 3. Fix would require a lock around `myPthread` which makes things (2x slower, even without contention) ### Modifications: - New type `SelectableEventLoopUniqueID` which can be packed into a `UInt` - Attach them into a C thread local ### Result: - Even faster than the old, incorrect version - old: `measuring: el_in_eventloop_100M: 0.257395375, 0.241049208, 0.243188792, 0.259125916, 0.24843225, 0.229690125, 0.244281541, 0.225078834, 0.236395, 0.233305167` - new: `measuring: el_in_eventloop_100M: 0.175561125, 0.187225625, 0.199269375, 0.19740975, 0.1922695, 0.179850958, 0.177612458, 0.17665125, 0.17897475, 0.18038775` - More correct - Groundwork to make apple#3297 not make things slower
### Motivation: NIOThread assumed it can hold onto `pthread_t` forever. Unfortunately, on Linux, `pthread_t` is dead when the thread got joined (if not detached) or if it exits (if detached) _which is at a random point in time_. ### Modifications: - Only assume ownership owner the `pthread_t` if not detached - `nil` out the `pthread_t` on `join()` ### Result: - Fewer `pthread_t` lifecycle bugs.
### Motivation: Structured Concurrency is helpful but so far, MTELG was very difficult to use with it mostly because Swift is still missing `async` in `defer`s. This builds on - apple#3297 - apple#3302 - apple#3304 ### Modifications: - Add MTELG.withELG { ... } ### Result: MTELG + SC = <3
…3360) ### Motivation: In #3297, I introduced a deadlock involving `SelectableEventLoop`'s `debugDescription`. I was under the impression that it's not possible to call this from outside the `NIO` module so I deemed it safe. That was a mistake :). ### Modifications: - Make it impossible to deadlock around `SelectableEventLoop.debugDescription`. ### Result: - Fewer deadlocks
Motivation:
NIOThread assumed it can hold onto
pthread_t
forever. Unfortunately, on Linux,pthread_t
is dead when the thread got joined (if not detached) or if it exits (if detached) which is at a random point in time.Modifications:
pthread_t
if not detachednil
out thepthread_t
onjoin()
Result:
pthread_t
lifecycle bugs.