Skip to content
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

NIOSingletons: Use NIO in easy mode #2471

Merged
merged 6 commits into from Jul 27, 2023
Merged

Conversation

weissi
Copy link
Member

@weissi weissi commented Jul 13, 2023

Motivation:

SwiftNIO allows and encourages to precisely manage all operating system resources like file descriptors & threads. That is a very important property of the system but in many places -- especially since Swift Concurrency arrived -- many simpler SwiftNIO programs only require a single, globally shared EventLoopGroup. Often even with just one thread.

Long story short: Many, probably most users would happily trade precise control over the threads for not having to pass around EventLoopGroups. Today, many of those users resort to creating (and often leaking) threads because it's simpler. Adding a .globalSingle static var which lazily provides singleton EventLoopGroups and NIOThreadPools is IMHO a much better answer.

Finally, this aligns SwiftNIO a little more with Apple's SDKs which have a lot of global singletons that hold onto system resources (Dispatch's thread pool, URLSession.shared, ...). At least in Dispatch's case the Apple SDKs actually make it impossible to manage the resources, there can only ever be one global pool of threads. That's fine for app development but wouldn't be good enough for certain server use cases, therefore I propose to add NIOSingletons as an option to the user. Confident programmers (especially in libraries) are still free and encouraged to manage all the resources deterministically and explicitly.

Companion PRs:

Modifications:

  • Add the NIOSingletons type
  • Add MultiThreadedEventLoopGroup.singleton
  • Add NIOThreadPool.singleton

Result:

Sources/NIOSingletonResources/SingletonType.swift Outdated Show resolved Hide resolved
Sources/NIOSingletonResources/SingletonType.swift Outdated Show resolved Hide resolved
Sources/NIOSingletonResources/SingletonType.swift Outdated Show resolved Hide resolved
Sources/NIOSingletonResources/SingletonType.swift Outdated Show resolved Hide resolved
Sources/NIOSingletonsPosix/PosixSingletons.swift Outdated Show resolved Hide resolved
Sources/NIOSingletonsPosix/PosixSingletons.swift Outdated Show resolved Hide resolved
Sources/NIOSingletonsPosix/PosixSingletons.swift Outdated Show resolved Hide resolved
Sources/NIOPosix/MultiThreadedEventLoopGroup.swift Outdated Show resolved Hide resolved
Sources/NIOSingletonsPosix/PosixSingletons.swift Outdated Show resolved Hide resolved
Sources/NIOSingletonResources/SingletonType.swift Outdated Show resolved Hide resolved
Sources/NIOSingletonResources/SingletonType.swift Outdated Show resolved Hide resolved
Sources/NIOSingletonsPosix/PosixSingletons.swift Outdated Show resolved Hide resolved
private let globalMultiThreadedPosixEventLoopGroup: MultiThreadedEventLoopGroup = {
let group = MultiThreadedEventLoopGroup(_canBeShutDown: false,
numberOfThreads: NIOSingletons.suggestedMultiThreadedEventLoopGroupThreadCount)
_ = Unmanaged.passUnretained(group).retain() // Never gonna give you up,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Never gonna give you up,

🙉 ❤️

@weissi weissi force-pushed the jw-nio-singletons branch 5 times, most recently from 4ae69d0 to 651d245 Compare July 13, 2023 08:27
@weissi weissi force-pushed the jw-nio-singletons branch 2 times, most recently from 0f8494c to a148af8 Compare July 13, 2023 11:04
Copy link
Contributor

@adam-fowler adam-fowler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For libraries it would be great if we can replace the NIOEventLoopGroupProvider with an EventLoopGroup that defaults to one of the global ones. Then not have to worry about EventLoopGroup shutdown at all.

/// A globally shared, lazily initialized ``NIOThreadPool`` that can be used for blocking I/O and other blocking operations.
///
/// /// The number of threads is determined by ``NIOSingletons.suggestedBlockingPoolThreadCount``.
public static var posixBlockingPool: NIOThreadPool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

posixBlockingPool doesn't mean much to me. Can we name this something different, posixBlockingThreadPool maybe? I'm guessing posixThreadPool is out of the question

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adam-fowler The default way is now NIOThreadPool.globalSingleton which I suppose is clearer?

@weissi weissi added the 🔼 needs-minor-version-bump For PRs that when merged cause a bump of the minor version, ie. 1.x.0 -> 1.(x+1).0 label Jul 21, 2023
@weissi weissi requested a review from Lukasa July 21, 2023 13:45
@weissi weissi requested a review from glbrntt July 21, 2023 13:49
"BUG IN NIO, please report: overly big suggested loop/thread count: \(threadCount)")
}

private static func getTrustworthyThreadCount(rawStorage: ManagedAtomic<Int>, environmentVariable: String) -> Int {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I lowkey love how we solved this!

Sources/NIOPosix/PosixSingletons.swift Outdated Show resolved Hide resolved
Sources/NIOPosix/PosixSingletons.swift Outdated Show resolved Hide resolved
Sources/NIOPosix/PosixSingletons.swift Outdated Show resolved Hide resolved
@weissi weissi force-pushed the jw-nio-singletons branch 2 times, most recently from ff60afa to 0f5f8b9 Compare July 26, 2023 09:59
Copy link
Contributor

@glbrntt glbrntt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly docc nits but otherwise this is looking good. Still not a big fan of doubling up global and singleton though but not a hill I'll die on.

Sources/NIOCore/GlobalSingletons.swift Outdated Show resolved Hide resolved
/// This value cannot be changed using an environment variable.
///
/// - note: This value must be set _before_ any singletons are used and must only be set once.
public static var globalSingletonsEnabledSuggestion: Bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this effectively a kill-switch for singletons? I.e. set this and blow up if singletons are used?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, is this "suggestion" because it can be true but no singletons are made?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@glbrntt It's a suggestion because we can't actually control that no code's creating singletons... We can make NIO code behave but we don't actually know :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kill switch: Yes, I think this is important to test stuff that thinks/hopes/promises to correctly wire through the group. Without the kill switch it's super duper hard to test (need to check the thread count etc)

Sources/NIOPosix/PosixSingletons.swift Outdated Show resolved Hide resolved
Sources/NIOPosix/PosixSingletons.swift Outdated Show resolved Hide resolved
@weissi
Copy link
Member Author

weissi commented Jul 26, 2023

For libraries it would be great if we can replace the NIOEventLoopGroupProvider with an EventLoopGroup that defaults to one of the global ones. Then not have to worry about EventLoopGroup shutdown at all.

@adam-fowler Yes, NIOEventLoopGroupProvider was always a bad idea. See #2480 for the deprecation of it.

@weissi
Copy link
Member Author

weissi commented Jul 26, 2023

Mostly docc nits but otherwise this is looking good. Still not a big fan of doubling up global and singleton though but not a hill I'll die on.

Just NIOSingletons and MultiThreadedEventLoopGroup.singleton ? I'm happy with that FWIW.

I just don't think we should do literally .shared because of the EventLoopGroupProvider .shared(...) and .shared(.shared) is kinda weird :P. .shared(...singleton) does make sense.

I'm definitely happy with

  • NIOGlobalSingletons / MTELG.globalSingleton
  • NIOSingletons / MTELG.singleton
  • NIO(Global)Singletons / MTELG.singletonInstance

And I can probably be convinced into

  • NIO(Global)Singletons / MTELG.global

And very maybe into

  • NIO(Global)Singletons / MTELG.sharedInstance but I still think HTTPClient(group: .shared(MTELG.sharedInstance)) is a little weird because the two "shared"s mean totally different things. The former means "shared", the latter means "global singleton".

@glbrntt
Copy link
Contributor

glbrntt commented Jul 26, 2023

Mostly docc nits but otherwise this is looking good. Still not a big fan of doubling up global and singleton though but not a hill I'll die on.

Just NIOSingletons and MultiThreadedEventLoopGroup.singleton ? I'm happy with that FWIW.

I just don't think we should do literally .shared because of the EventLoopGroupProvider .shared(...) and .shared(.shared) is kinda weird :P. .shared(...singleton) does make sense.

Yeah I'm okay with that.

Copy link
Contributor

@glbrntt glbrntt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM modulo CI failures. I think this ended up in a pretty good shape 🙂

Copy link
Contributor

@FranzBusch FranzBusch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@weissi weissi enabled auto-merge (squash) July 26, 2023 23:02
@weissi
Copy link
Member Author

weissi commented Jul 27, 2023

@swift-nio-bot test this please

@weissi weissi merged commit 6c21f51 into apple:main Jul 27, 2023
6 of 8 checks passed
@weissi weissi deleted the jw-nio-singletons branch July 27, 2023 08:01
@weissi
Copy link
Member Author

weissi commented Jul 27, 2023

ping @Lukasa, I didn't see that you actually hadn't approved this yet. Happy to make changes if there's anything. Hasn't been released yet so we've got the freedom.

1proprogrammerchant added a commit to 1proprogrammerchant/swift-nio that referenced this pull request Jul 28, 2023
NIOSingletons: Use NIO in easy mode (apple#2471)
tkrajacic added a commit to tkrajacic/postgres-nio that referenced this pull request Aug 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔼 needs-minor-version-bump For PRs that when merged cause a bump of the minor version, ie. 1.x.0 -> 1.(x+1).0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

NIOSingletons: Make EventLoopGroup shutdown throw
8 participants