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
allow setting MTELG.singleton as Swift Concurrency executor #2564
Conversation
a6c8df3
to
d9bb991
Compare
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.
Thanks for all the checks before allowing the hooking. This looks as okey as it can be heh 😉
let’s get task executors in soon! :-)
|
||
private protocol SilenceWarning { | ||
@available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) | ||
func enqueue(_ job: UnownedJob) |
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.
Yeah huh fun workaround. Okey
Yes please |
78269e2
to
7067f30
Compare
_haveWeTakenOverTheConcurrencyPool.compareExchange( | ||
expected: false, | ||
desired: true, | ||
ordering: .relaxed |
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.
ordering: .relaxed | |
ordering: .acquiringAndReleasing |
This is probably safe without using an acquiring load here due to the dlsym
call that follows it, but semantically this should be an acquiring load. Logically then it also has to be a releasing store because we're synchronising with ourselves!
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 I think you're mixing up the two variables. This is just the guard, no other memory reads/writes are ordered with this guy. This is correct not because of the dlsym
but because of the guard
.
You need non-.relaxed
if you care about the order of other memory reads/writes but we don't
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.
Actually, we don't even need the guard
. So yeah, any order of memory operations is completely fine here.
guard concurrencyEnqueueGlobalHookAtomic.compareExchange( | ||
expected: nil, | ||
desired: enqueueOnNIOPtr.pointee, | ||
ordering: .relaxed |
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.
Similarly this should probably be a releasing
ordering. Again, .enqueue
should be an appropriate compiler barrier so arguably unnecessary, but I'd rather we did this right.
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.
This one's a little more tricky but still, this is a singular memory operation, it doesn't matter when we do to the other (non-dependent) ones here: This can be .relaxed
. Any order of non-dependent memory operations works for us here so this is fine too
a30cdbf
to
554ab71
Compare
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.
LGTM!
@swift-nio-bot test this please |
Motivation:
There are situations where the currently necessary thread switches between Swift Concurrency's default global executor and a place that can do I/O (such as Swift NIO's EventLoops) can be very detrimental to the overall system performance.
The right solution is something akin to upcoming the Task Executor preference proposal but for now (Swift 5.9) there is another solution. Instead of separating the threads of Swift Concurrency & SwiftNIO entirely we can replace Swift Concurrency's default global executor by a more powerful executor: SwiftNIO's EventLoopGroups.
The result is that almost all non-
MainActor
async
functions will then run on SwiftNIO's EventLoops. That means if those have to perform I/O they don't necessarily have to switch to a separate I/O thread (such as NIO's EventLoops) anymore because they might be already there.Be mindful however: This approach might not work well for all workloads and is a global setting.
Some more information about this can be found in:
Modifications:
Provide a
NIOSingletons.unsafeTryInstallSingletonPosixEventLoopGroupAsConcurrencyGlobalExecutor()
method that installsMultiThreadedEventLoopGroup.singleton
as Swift Concurrency's global executor.Result:
Faster I/O on Swift Concurrency.