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

non-blocking versions of file open/stat #256

Merged
merged 2 commits into from
Apr 18, 2018

Conversation

weissi
Copy link
Member

@weissi weissi commented Mar 29, 2018

addresses #185. This is a first draft to discuss the API.

Motivation:

Opening a file, seeking it or querying its size (or other information)
is blocking on UNIX so NonBlockingFileIO should support that too.

Modifications:

Added a method to NonBlockingFileIO which lets the user open a file
without blocking the calling thread.

Result:

Less blocking is good :)

@weissi
Copy link
Member Author

weissi commented Mar 29, 2018

test failure should hopefully be addressed by #257

@weissi
Copy link
Member Author

weissi commented Mar 29, 2018

@swift-nio-bot please test

1 similar comment
@weissi
Copy link
Member Author

weissi commented Mar 30, 2018

@swift-nio-bot please test

@tomerd
Copy link
Member

tomerd commented Mar 30, 2018

@swift-nio-bot test this please
@wrissi you need to ask right :)

@weissi weissi force-pushed the jw-non-blocking-file-opening branch from ecacadf to af9d966 Compare March 30, 2018 14:47
@weissi weissi added the semver/minor Adds new public API. label Mar 30, 2018
Copy link
Contributor

@Lukasa Lukasa left a comment

Choose a reason for hiding this comment

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

One minor question, otherwise looks good.

guard case shouldRun = BlockingIOThreadPool.WorkItemState.active else {
p.fail(error: ChannelError.ioOnClosedChannel)
return
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we have a convenience wrapper around submit that adds this boilerplate?

Copy link
Member Author

Choose a reason for hiding this comment

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

proposed an API for that now.

@Lukasa Lukasa added this to the 1.4.0 milestone Apr 3, 2018
@weissi weissi force-pushed the jw-non-blocking-file-opening branch from af9d966 to 252339b Compare April 3, 2018 09:09
@@ -75,6 +75,8 @@ public final class BlockingIOThreadPool {

/// Submit a `WorkItem` to process.
///
/// - note: This is a low-level method, in most cases the `submit` method should be used.
Copy link
Contributor

Choose a reason for hiding this comment

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

This should presumably be runIfActive, not submit.

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed, thanks

@weissi weissi force-pushed the jw-non-blocking-file-opening branch from 252339b to f0d35ad Compare April 3, 2018 10:48
Copy link
Contributor

@Lukasa Lukasa left a comment

Choose a reason for hiding this comment

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

LGTM, let's let @normanmaurer review as well.

let promise: EventLoopPromise<T> = eventLoop.newPromise()
self.submit { shouldRun in
guard case shouldRun = BlockingIOThreadPool.WorkItemState.active else {
promise.fail(error: ChannelError.ioOnClosedChannel)
Copy link
Member

Choose a reason for hiding this comment

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

IMHO using an ChannelError here is a bit odd as the BlockingIOThreadPool has nothing to do with a Channel at all and we also have no reference to the Channel.

Copy link
Member Author

Choose a reason for hiding this comment

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

@normanmaurer yes, that is indeed very odd. However that is what the other methods do so I copied it. Happy to fix but then we're slightly inconsistent...

Copy link
Member

Choose a reason for hiding this comment

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

hmm ok... We should have spotted this before :( I guess we need to fix this in a next major

Copy link
Member Author

Choose a reason for hiding this comment

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

@normanmaurer well, we could just be different in this particular method. That wouldn't be breaking API as it's a new API. It would just behave slightly differently than the read apis.

/// - eventLoop: The `EventLoop` to create the returned `EventLoopFuture` on.
/// - body: The closure which performs some blocking work to be done on the thread pool.
/// - returns: An `EventLoopFuture` fulfilled with the result (or error) of the passed closure.
public func runIfActive<T>(eventLoop: EventLoop, _ body: @escaping () throws -> T) -> EventLoopFuture<T> {
Copy link
Member

Choose a reason for hiding this comment

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

Should we maybe better just take the EventLoopPromise<T> directly and not the EventLoop ?

Copy link
Member Author

Choose a reason for hiding this comment

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

hmm, unconvinced as there are more things that can go wrong if we ask the user to give us a promise instead of just making one. What's the benefit of taking a promise instead of the EventLoop?

Copy link
Contributor

Choose a reason for hiding this comment

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

The major benefit is that the user controls where the future fires. That's basically it though.

Copy link
Member Author

Choose a reason for hiding this comment

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

@Lukasa but that's why I take the EventLoop. I tell the user that the returned future will fired on the EventLoop passed in there.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yup, basically isomorphic here.

Copy link
Member

Choose a reason for hiding this comment

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

@weissi for me taking a EventLoop looks more like running something on the EventLoop, which is not the case here. Also taking an EventLoopPromise is more consistent with what we do for ChannelOutboundInvoker imho.

Copy link
Member

Choose a reason for hiding this comment

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

also @Lukasa

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah, let @Lukasa be the tie breaker.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, I think we should take a promise: it's more consistent with the other interfaces.

Copy link
Member Author

Choose a reason for hiding this comment

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

addressed

@weissi weissi force-pushed the jw-non-blocking-file-opening branch 2 times, most recently from 60f6164 to 64e7553 Compare April 6, 2018 10:20
Copy link
Contributor

@Lukasa Lukasa left a comment

Choose a reason for hiding this comment

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

I'm happy, waiting on @normanmaurer.

/// - promise: The `EventLoopPromise` that will be fulfilled when the block finished running (or failed).
/// - body: The closure which performs some blocking work to be done on the thread pool.
/// - returns: The `EventLoopFuture` of `promise` fulfilled with the result (or error) of the passed closure.
public func runIfActive<T>(promise: EventLoopPromise<T>, _ body: @escaping () throws -> T) -> EventLoopFuture<T> {
Copy link
Member

@normanmaurer normanmaurer Apr 9, 2018

Choose a reason for hiding this comment

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

@weissi @Lukasa sorry but I need to correct my previous suggestion here... whenever we take a promise we use Void as return type. So maybe we should do the same here as well ??

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, probably. It's likely to be non-obvious that the Future returned here is the one corresponding to the passed-in promise.

Copy link
Member Author

Choose a reason for hiding this comment

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

@Lukasa and @normanmaurer can we please go back to taking the EventLoop? It's important that we return an EventLoopFuture as the next thing you want to do is clearly work with that file. The taking-the-promise approach is just a performance improvement if you might not care about the result of the operation (usually for EventLoopFuture<Void>s). For opening files however we absolutely do care about the return value. So we will need a promise&future here and I still think we should take the EventLoop

Copy link
Member Author

Choose a reason for hiding this comment

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

@Lukasa & @normanmaurer updated this guy

@weissi weissi removed the discussion label Apr 10, 2018
@weissi weissi force-pushed the jw-non-blocking-file-opening branch from 64e7553 to 7df910d Compare April 10, 2018 14:03
Copy link
Contributor

@Lukasa Lukasa left a comment

Choose a reason for hiding this comment

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

My previous +1 still applies.

@normanmaurer
Copy link
Member

@weissi please rebase, address conflicts and merge

@weissi weissi force-pushed the jw-non-blocking-file-opening branch from 505ad73 to 388d576 Compare April 11, 2018 14:16
@Lukasa Lukasa modified the milestones: 1.4.0, 1.5.0 Apr 11, 2018
@weissi
Copy link
Member Author

weissi commented Apr 11, 2018

@swift-nio-bot test this please

@tomerd
Copy link
Member

tomerd commented Apr 11, 2018

@swift-nio-bot test this please

Motivation:

Opening a file, seeking it or querying its size (or other information)
is blocking on UNIX so `NonBlockingFileIO` should support that too.

Modifications:

Added a method to `NonBlockingFileIO` which lets the user open a file
without blocking the calling thread.

Result:

Less blocking is good :)
@weissi weissi force-pushed the jw-non-blocking-file-opening branch from 8e764e9 to 3b1a8e9 Compare April 18, 2018 13:16
@weissi
Copy link
Member Author

weissi commented Apr 18, 2018

@normanmaurer / @Lukasa I'll merge this when the tests go green as we'll release a 1.5.0 very soon, okay?

@Lukasa
Copy link
Contributor

Lukasa commented Apr 18, 2018

WFM.

@normanmaurer
Copy link
Member

same... WFM

@weissi weissi merged commit 5327cb1 into apple:master Apr 18, 2018
@weissi weissi deleted the jw-non-blocking-file-opening branch April 18, 2018 16:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
semver/minor Adds new public API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants