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 #2879

Merged
merged 2 commits into from
Sep 13, 2024

Conversation

rnro
Copy link
Contributor

@rnro rnro commented Sep 10, 2024

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.

@rnro rnro requested a review from glbrntt September 10, 2024 14:23
@FranzBusch FranzBusch added the 🔨 semver/patch No public API change. label Sep 11, 2024
Copy link
Member

@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.

In general this looks good to me. Have you run some benchmarks between the two to make sure we still have the same performance?

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 good! I left a couple of small comments which need addressing.

@@ -135,52 +136,85 @@ extension DirectoryEntries.Batched.AsyncIterator: Sendable {}
// MARK: - Internal

@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
extension BufferedStream where Element == [DirectoryEntry] {
extension NIOThrowingAsyncSequenceProducer where Element == [DirectoryEntry], Failure == Error,
Copy link
Contributor

Choose a reason for hiding this comment

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

nit

Suggested change
extension NIOThrowingAsyncSequenceProducer where Element == [DirectoryEntry], Failure == Error,
extension NIOThrowingAsyncSequenceProducer where Element == [DirectoryEntry], Failure == (any Error),

switch self.state {
case let .idle(handle, _):
return handle.threadPool
case let .open(threadPool, _, _):
return threadPool
case .openPausedProducing(let threadPool, let source, let array):
self.state = .modifying
Copy link
Contributor

Choose a reason for hiding this comment

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

.modifying isn't required here because we aren't modifying any of the state held in the openPausedProducing case

internal mutating func pauseProducing() {
switch self.state {
case .open(let nIOThreadPool, let source, let array):
self.state = .modifying
Copy link
Contributor

Choose a reason for hiding this comment

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

same here, no need to do .modifying

case .done:
return nil
case .modifying:
fatalError()
}
}

internal mutating func pauseProducing() {
switch self.state {
case .open(let nIOThreadPool, let source, let array):
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: threadPool

Comment on lines 131 to 132
init(range: FileChunks.ChunkRange, handle: SystemFileHandle, length: Int64) {

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: no blank lines at the start of funcs

Suggested change
init(range: FileChunks.ChunkRange, handle: SystemFileHandle, length: Int64) {
init(range: FileChunks.ChunkRange, handle: SystemFileHandle, length: Int64) {

Comment on lines 133 to 145
let state: ProducerState = switch range {
case .entireFile:
.init(handle: handle, range: nil)
case .partial(let partialRange):
.init(handle: handle, range: partialRange)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, I don't think our minimum Swift version is new enough to support this? Also the indentation is way off for the body of each case.

Copy link
Contributor

Choose a reason for hiding this comment

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

(Also I really don't like this syntax but maybe I'm just a curmudgeon...)


@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
extension ProducerState.Producing {
mutating func updateRange(count: Int) -> Bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

It's not obvious how the range is updated without looking at the implementation, also the return value is quite misleading: if updateRange(count: blah) { ... } looks a whole lot like "if the range was updated then ...".

Would probably be clearer if you call this didReadBytes(_ count: Int) and documented the return value (you could use an enum for the return type but that's probably overkill)

@glbrntt
Copy link
Contributor

glbrntt commented Sep 11, 2024

In general this looks good to me. Have you run some benchmarks between the two to make sure we still have the same performance?

I would be surprised if there was a substantial difference. You'd think the syscalls would dominate here but it's worth doing some validation nonetheless.

@rnro rnro force-pushed the adopt_NIOThrowingAsyncSequenceProducer branch 4 times, most recently from 7d74670 to 41fe2de Compare September 12, 2024 15:58
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.

Looks good modulo some failing CI!

case .produceMore:
self.produceMore()
case .stopProducing:
self.state.withLockedValue { state in state.pauseProducing()}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
self.state.withLockedValue { state in state.pauseProducing()}
self.state.withLockedValue { state in state.pauseProducing() }

@rnro rnro force-pushed the adopt_NIOThrowingAsyncSequenceProducer branch from 41fe2de to b61146f Compare September 13, 2024 10:18
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.
@rnro rnro force-pushed the adopt_NIOThrowingAsyncSequenceProducer branch from b61146f to eac6c2a Compare September 13, 2024 10:48
@rnro
Copy link
Contributor Author

rnro commented Sep 13, 2024

Performance-wise there doesn’t seem to be much of a difference.

I tested listing the files on disk, exercising the DirectoryEntries path. I listed a directory I happened to have with 772492 files in it making up 23GiB.

For the existing code:
3 runs of: 27.51s, 26.53s, 26.34s
With an average of: 26.8s

For the new code:
3 runs of: 26.08s, 27.71s, 27.35s
With an average of: 27.05s

@rnro rnro merged commit 282f593 into apple:main Sep 13, 2024
27 of 29 checks passed
@rnro rnro deleted the adopt_NIOThrowingAsyncSequenceProducer branch September 13, 2024 13:21
ali-ahsan-ali pushed a commit to ali-ahsan-ali/swift-nio that referenced this pull request Sep 15, 2024
### 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.
@finagolfin
Copy link
Contributor

This pull consistently causes hanging FileHandle tests in my Android CI, that runs them in an Android x86_64 emulator on both linux and macOS built with 5.10, 6.0, and trunk 6.1. I can reproduce locally on Android 13 AArch64 with a recently-built trunk 6.1 toolchain from the Sep. 4 source snapshot and the Termux 5.10.1 toolchain.

I'll see if I can track it down and file an issue.

rnro added a commit to rnro/swift-nio that referenced this pull request Sep 20, 2024
Lukasa pushed a commit that referenced this pull request Sep 20, 2024
This reverts commit 282f593.

### Motivation:

Repeated testing in linux containers has surfaced issues with the
implementation which require some rethinking of the code, let's revert
it for now and fix it up.

### Modifications:

Revert the adoption commit.

### Result:

The change is backed out.
cgrindel-self-hosted-renovate bot referenced this pull request in cgrindel/rules_swift_package_manager Sep 25, 2024
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [apple/swift-nio](https://redirect.github.com/apple/swift-nio) | minor
| `2.72.0` -> `2.73.0` |

---

### Release Notes

<details>
<summary>apple/swift-nio (apple/swift-nio)</summary>

###
[`v2.73.0`](https://redirect.github.com/apple/swift-nio/releases/tag/2.73.0)

[Compare
Source](https://redirect.github.com/apple/swift-nio/compare/2.72.0...2.73.0)

<!-- Release notes generated using configuration in .github/release.yml
at main -->

#### What's Changed

##### SemVer Minor

- Make `ByteBuffer`'s description more useful by
[@&#8203;supersonicbyte](https://redirect.github.com/supersonicbyte) in
[https://github.com/apple/swift-nio/pull/2864](https://redirect.github.com/apple/swift-nio/pull/2864)
- Expose `UDP_MAX_SEGMENTS` via System by
[@&#8203;rnro](https://redirect.github.com/rnro) in
[https://github.com/apple/swift-nio/pull/2891](https://redirect.github.com/apple/swift-nio/pull/2891)
- Add new `ChannelOption` to get the amount of buffered outbound data in
the Channel by
[@&#8203;johnnzhou](https://redirect.github.com/johnnzhou) in
[https://github.com/apple/swift-nio/pull/2849](https://redirect.github.com/apple/swift-nio/pull/2849)
- Add an `AcceptBackoffHandler` to the async server bootstraps by
[@&#8203;FranzBusch](https://redirect.github.com/FranzBusch) in
[https://github.com/apple/swift-nio/pull/2782](https://redirect.github.com/apple/swift-nio/pull/2782)

##### SemVer Patch

- Adding a nicer description for `WebSocketFrame` by
[@&#8203;supersonicbyte](https://redirect.github.com/supersonicbyte) in
[https://github.com/apple/swift-nio/pull/2862](https://redirect.github.com/apple/swift-nio/pull/2862)
- Improving `description` and adding `debugDescription` to `NIOAny` by
[@&#8203;supersonicbyte](https://redirect.github.com/supersonicbyte) in
[https://github.com/apple/swift-nio/pull/2866](https://redirect.github.com/apple/swift-nio/pull/2866)
- Make FileChunk sendable by
[@&#8203;ali-ahsan-ali](https://redirect.github.com/ali-ahsan-ali) in
[https://github.com/apple/swift-nio/pull/2871](https://redirect.github.com/apple/swift-nio/pull/2871)
- Make `ByteBuffer.debugDescription` suitable for structural display by
[@&#8203;dnadoba](https://redirect.github.com/dnadoba) in
[https://github.com/apple/swift-nio/pull/2495](https://redirect.github.com/apple/swift-nio/pull/2495)
- Add support for WASILibc by
[@&#8203;MaxDesiatov](https://redirect.github.com/MaxDesiatov) in
[https://github.com/apple/swift-nio/pull/2671](https://redirect.github.com/apple/swift-nio/pull/2671)
- `NIOSingleStepByteToMessageDecoder` reentrancy safety by
[@&#8203;rnro](https://redirect.github.com/rnro) in
[https://github.com/apple/swift-nio/pull/2881](https://redirect.github.com/apple/swift-nio/pull/2881)
- Adopt `NIOThrowingAsyncSequenceProducer` by
[@&#8203;rnro](https://redirect.github.com/rnro) in
[https://github.com/apple/swift-nio/pull/2879](https://redirect.github.com/apple/swift-nio/pull/2879)
- Clamp buffer to maximum upon large write operation by
[@&#8203;ali-ahsan-ali](https://redirect.github.com/ali-ahsan-ali) in
[https://github.com/apple/swift-nio/pull/2745](https://redirect.github.com/apple/swift-nio/pull/2745)
- Revert "Adopt `NIOThrowingAsyncSequenceProducer`
([#&#8203;2879](https://redirect.github.com/apple/swift-nio/issues/2879))"
by [@&#8203;rnro](https://redirect.github.com/rnro) in
[https://github.com/apple/swift-nio/pull/2892](https://redirect.github.com/apple/swift-nio/pull/2892)
- Add concrete description for `EmbeddedEventLoop` by
[@&#8203;aryan-25](https://redirect.github.com/aryan-25) in
[https://github.com/apple/swift-nio/pull/2890](https://redirect.github.com/apple/swift-nio/pull/2890)
- Conditionally include linux/udp.h by
[@&#8203;rnro](https://redirect.github.com/rnro) in
[https://github.com/apple/swift-nio/pull/2894](https://redirect.github.com/apple/swift-nio/pull/2894)
- Work around a type checking error when using the Static Linux SDK by
[@&#8203;euanh](https://redirect.github.com/euanh) in
[https://github.com/apple/swift-nio/pull/2898](https://redirect.github.com/apple/swift-nio/pull/2898)

##### Other Changes

- \[CI] Run tests on push to main by
[@&#8203;FranzBusch](https://redirect.github.com/FranzBusch) in
[https://github.com/apple/swift-nio/pull/2868](https://redirect.github.com/apple/swift-nio/pull/2868)
- \[CI] License header support `.in` and `.cmake` files by
[@&#8203;FranzBusch](https://redirect.github.com/FranzBusch) in
[https://github.com/apple/swift-nio/pull/2870](https://redirect.github.com/apple/swift-nio/pull/2870)
- Include nanoseconds in assertion of timestamp for NIOFileSystem tests
by [@&#8203;gjcairo](https://redirect.github.com/gjcairo) in
[https://github.com/apple/swift-nio/pull/2869](https://redirect.github.com/apple/swift-nio/pull/2869)
- Correct the link of sswg-security at SECURITY.md by
[@&#8203;lamtrinhdev](https://redirect.github.com/lamtrinhdev) in
[https://github.com/apple/swift-nio/pull/2872](https://redirect.github.com/apple/swift-nio/pull/2872)
- Speculative fix for flakey AsyncTestingEventLoop test by
[@&#8203;simonjbeaumont](https://redirect.github.com/simonjbeaumont) in
[https://github.com/apple/swift-nio/pull/2873](https://redirect.github.com/apple/swift-nio/pull/2873)
- ci: Install shellcheck if not present in CI runner by
[@&#8203;simonjbeaumont](https://redirect.github.com/simonjbeaumont) in
[https://github.com/apple/swift-nio/pull/2882](https://redirect.github.com/apple/swift-nio/pull/2882)
- ci: Use ${GITHUB_BASE_REF} as treeish for checking API break by
[@&#8203;simonjbeaumont](https://redirect.github.com/simonjbeaumont) in
[https://github.com/apple/swift-nio/pull/2883](https://redirect.github.com/apple/swift-nio/pull/2883)
- ci: Refer to nested reusable workflows using remote variant by
[@&#8203;simonjbeaumont](https://redirect.github.com/simonjbeaumont) in
[https://github.com/apple/swift-nio/pull/2884](https://redirect.github.com/apple/swift-nio/pull/2884)
- \[CI] Fix pull request label workflow by
[@&#8203;FranzBusch](https://redirect.github.com/FranzBusch) in
[https://github.com/apple/swift-nio/pull/2885](https://redirect.github.com/apple/swift-nio/pull/2885)

#### New Contributors

- [@&#8203;ali-ahsan-ali](https://redirect.github.com/ali-ahsan-ali)
made their first contribution in
[https://github.com/apple/swift-nio/pull/2871](https://redirect.github.com/apple/swift-nio/pull/2871)
- [@&#8203;aryan-25](https://redirect.github.com/aryan-25) made their
first contribution in
[https://github.com/apple/swift-nio/pull/2890](https://redirect.github.com/apple/swift-nio/pull/2890)
- [@&#8203;johnnzhou](https://redirect.github.com/johnnzhou) made their
first contribution in
[https://github.com/apple/swift-nio/pull/2849](https://redirect.github.com/apple/swift-nio/pull/2849)
- [@&#8203;euanh](https://redirect.github.com/euanh) made their first
contribution in
[https://github.com/apple/swift-nio/pull/2898](https://redirect.github.com/apple/swift-nio/pull/2898)

**Full Changelog**:
apple/swift-nio@2.72.0...2.73.0

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOC45NC4xIiwidXBkYXRlZEluVmVyIjoiMzguOTQuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: cgrindel-self-hosted-renovate[bot] <139595543+cgrindel-self-hosted-renovate[bot]@users.noreply.github.com>
rnro added a commit that referenced this pull request Oct 15, 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.
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.

4 participants