There are two problems with EventLoopGroup as currently concretely implemented:
MultiThreadedEventLoopGroup.threadSpecificEventLoop is a ThreadSpecificVariable, which has unpredictable behavior when used with dispatch queues (which have their own mechanism for the purpose).
- There is no way for the generic
EventLoopGroup protocol to provide an API for expressing a currentEventLoop concept without making that API public.
This has some unfortunate results when any alternate EventLoopGroup - most especially NIOTSEventLoopGroup is in use:
- Invoking
EventLoopGroup.syncShutdownGracefully() while still on an event loop will fail to diagnose the problem via precondition as it normally would.
- Misuse of
EventLoopFuture.wait() while executing on any event loop will incorrectly report the "current" event loop as nil. In addition, the misuse will only be correctly detected when it takes on the future's own event loop, and even then will suffer even further from NIOTSEventLoop.inEventLoop's "false negative" problem.
NIOPipeBootstrap.withPipes() similarly fails to diagnose an attempt to bootstrap while on an event loop.
To make matters even more confusing, it's not clear that NIOTSEventLoop could provide a satisfactory implementation of an API for the appropriate check in the first place, given the aforementioned problems experienced by NIOTSEventLoop.inEventLoop. (See https://github.com/apple/swift-nio-transport-services/blob/master/Sources/NIOTransportServices/NIOTSEventLoop.swift#L84-L94 for details on that issue.)
While all of these uses of currentEventLoop are limited to expressing precondition()s (which is exactly as it should be), and thus these failures are not critical, the lack of these diagnostics tends to make debugging more difficult and delays the detection of such issues until after the problem has become worse. This is not a satisfactory state of affairs, given the recommendation that NIOTSEventLoopGroup be preferred over MultiThreadedEventLoopGroup whenever available - users are left with a recommended default which suffers from (admittedly minor) reduced usability.
It is also possible for other alternative EventLoopGroup implementations to exist, such as a threading model specialized for Windows support, or just for the sheer ridiculousness of it, an EventLoop based on Mach threads (yikes). This suggests that the problem would ideally be solved in a fully generic fashion, rather than providing some form of special-case behavior for NIOTS (if that were possible to begin with).
I have not yet come up with any kind of solution which would not require one of:
- Exposing the
currentEventLoop as public API (which is obviously undesirable; it would only be misused in the same way dispatch_get_current_queue() historically has been)
- Providing some form of technically
public but definitionally private API (as the stdlib does with underscored functions which do not appear in generated interfaces but are nonetheless usable if their names are known)
- Making NIO nominally aware of NIOTransportServices, which would be a massive layering violation and probably impossible anyway given the obvious circular dependency problem. Upwards dependencies are the bane of sensible build systems, ask anyone who's ever complained that they can't switch away from PBXBuild in their legacy Xcode project 🙃.
I would love to discuss alternatives, if anyone has any thoughts.
There are two problems with
EventLoopGroupas currently concretely implemented:MultiThreadedEventLoopGroup.threadSpecificEventLoopis aThreadSpecificVariable, which has unpredictable behavior when used with dispatch queues (which have their own mechanism for the purpose).EventLoopGroupprotocol to provide an API for expressing acurrentEventLoopconcept without making that API public.This has some unfortunate results when any alternate
EventLoopGroup- most especiallyNIOTSEventLoopGroupis in use:EventLoopGroup.syncShutdownGracefully()while still on an event loop will fail to diagnose the problem via precondition as it normally would.EventLoopFuture.wait()while executing on any event loop will incorrectly report the "current" event loop asnil. In addition, the misuse will only be correctly detected when it takes on the future's own event loop, and even then will suffer even further fromNIOTSEventLoop.inEventLoop's "false negative" problem.NIOPipeBootstrap.withPipes()similarly fails to diagnose an attempt to bootstrap while on an event loop.To make matters even more confusing, it's not clear that
NIOTSEventLoopcould provide a satisfactory implementation of an API for the appropriate check in the first place, given the aforementioned problems experienced byNIOTSEventLoop.inEventLoop. (See https://github.com/apple/swift-nio-transport-services/blob/master/Sources/NIOTransportServices/NIOTSEventLoop.swift#L84-L94 for details on that issue.)While all of these uses of
currentEventLoopare limited to expressingprecondition()s (which is exactly as it should be), and thus these failures are not critical, the lack of these diagnostics tends to make debugging more difficult and delays the detection of such issues until after the problem has become worse. This is not a satisfactory state of affairs, given the recommendation thatNIOTSEventLoopGroupbe preferred overMultiThreadedEventLoopGroupwhenever available - users are left with a recommended default which suffers from (admittedly minor) reduced usability.It is also possible for other alternative
EventLoopGroupimplementations to exist, such as a threading model specialized for Windows support, or just for the sheer ridiculousness of it, anEventLoopbased on Mach threads (yikes). This suggests that the problem would ideally be solved in a fully generic fashion, rather than providing some form of special-case behavior for NIOTS (if that were possible to begin with).I have not yet come up with any kind of solution which would not require one of:
currentEventLoopas public API (which is obviously undesirable; it would only be misused in the same waydispatch_get_current_queue()historically has been)publicbut definitionally private API (as the stdlib does with underscored functions which do not appear in generated interfaces but are nonetheless usable if their names are known)I would love to discuss alternatives, if anyone has any thoughts.