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

Adopt NIOThrowingAsyncSequenceProducer 2nd try #2917

Merged
merged 4 commits into from
Oct 15, 2024

Conversation

rnro
Copy link
Contributor

@rnro rnro commented Oct 11, 2024

Motivation:

Adopt NIOThrowingAsyncSequenceProducer in NIOFileSystem to reduce code
duplication.

Modifications:

Adopt NIOThrowingAsyncSequenceProducer in NIOFileSystem DirectoryEntryProducer and FileChunkProducer.

This change was previously merged and then backed out due to issues (#2879). The original change is in the first commit, the second commit contains additional changes.

In the original adoption of NIOThrowingAsyncSequenceProducer the code did not deal with backpressure being applied very well, in some cases causes hangs.

A key bug was that it was not protected against re-entrant calls to produceMore. Such calls may happen e.g. when the producer has been asked to pause producing and then resume again before the initial read had completed. This resulted in overlapping reads and re-ordered data.

This change introduces a new activity state which is protected by a lock and keeps track of if we are in the critical section to serialize access.

DirectoryEntryProducer performs most of its logic within a lock and so
doesn't seem to be impacted in the same way.

Result:

No functional changes. Internal changes reduce code duplication.

@rnro rnro added the semver/patch No public API change. label Oct 14, 2024
@rnro rnro requested a review from glbrntt October 14, 2024 09:16
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.

This looks great aside from a couple of small things to fix.

Comment on lines 118 to 119
lowWatermark: 4,
highWatermark: 8
Copy link
Contributor

Choose a reason for hiding this comment

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

We pass in the watermarks to makeFileChunksStream so presumably we should use them here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch!

Comment on lines 454 to 457
internal enum RangeUpdateOutcome {
case moreToRead
case cannotReadMore
}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can you move this to the extension where its used?

return .success((descriptor, state.range))
} else {
case .producing(let producingState), .pausedProducing(let producingState):
guard let descriptor = producingState.handle.descriptorIfAvailable() else {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I don't think a guard is any clearer here as the exit on the failure path isn't earlier than on the success path. IMO if both paths immediately return then if/else is clearer.

/// Updates the range (the offsets to read from and up to) to reflect the number of bytes which have been read.
/// - Parameter count: The number of bytes which have been read.
/// - Returns: Returns `True` if there are no remaining bytes to read, `False` otherwise.
mutating func updateRangeWithReadBytes(_ count: Int) -> ProducerState.RangeUpdateOutcome {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is definitely clearer than the existing in-line range updates 🙂

// update range, we are not done
self.range = newLowerBound..<currentRange.upperBound
if currentRange.upperBound - newLowerBound < 1 {
fatalError("here \(newLowerBound) \(currentRange.upperBound)")
Copy link
Contributor

Choose a reason for hiding this comment

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

Leftover debugging?

Motivation:

Adopt `NIOThrowingAsyncSequenceProducer` in NIOFileSystem to reduce code
duplication.

Modifications:

Adopt `NIOThrowingAsyncSequenceProducer` in NIOFileSystem
`DirectoryEntryProducer` and `FileChunkProducer`

Result:

No functional changes. Internal changes reduce code duplication.
In the original adoption of `NIOThrowingAsyncSequenceProducer` the code
did not deal with backpressure being applied very well.

A key bug was that it was not protected against re-entrant calls to `produceMore`. Such calls
may happen e.g. when the producer has been asked to pause producing and
then resume again before the initial read had completed. This resulted
in overlapping reads and re-ordered data.

This change introduces a new activity state which is protected by a lock
and keeps track of if we are in the critical section to serialize
access.

`DirectoryEntryProducer` performs most of its logic within a lock and so
doesn't seem to be impacted in the same way.
@rnro rnro force-pushed the adopt_niothrowingasyncsequenceproducer_2nd_try branch from b83970c to 8e8bf85 Compare October 15, 2024 08:06
@rnro rnro requested a review from glbrntt October 15, 2024 08:07
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.

Awesome, thanks @rnro!

@rnro rnro enabled auto-merge (squash) October 15, 2024 09:58
@rnro rnro force-pushed the adopt_niothrowingasyncsequenceproducer_2nd_try branch from 2ae78e6 to 51720a8 Compare October 15, 2024 09:59
@rnro rnro merged commit fcdb6c3 into apple:main Oct 15, 2024
27 of 31 checks passed
@rnro rnro deleted the adopt_niothrowingasyncsequenceproducer_2nd_try branch October 15, 2024 12:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
semver/patch No public API change.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants