Add file handle support for overlapped I/O#248
Add file handle support for overlapped I/O#248klu-dev wants to merge 1 commit intosmol-rs:masterfrom
Conversation
d0cf4d3 to
5e24b6b
Compare
5e24b6b to
d60ff79
Compare
notgull
left a comment
There was a problem hiding this comment.
Thank you! I imagine this was a significant effort. I appreciate you writing this. However, I definitely need to read through this and understand this. So this review may take some time.
The immediate thing that jumps out at me is the weird pointer offsets in the FileOverlapped traits. This seems fragile to me. I would prefer them to be direct pointers; I think that would be better when it comes to strict provenance.
Also, have you run this code under Miri on Windows?
|
Hi @notgull Thanks for reviewing. FileCompletionHandle trait would provide conversion from overlapped pointer to packet which will depends on FileOverlapped trait providing pointer offset to packet. Maybe should rename FileOverlapped to FileOverlappedOffset. I do not run the code with miri. Currently I am writing NamedPipe code in async_io crate. I found the code need some change in order for better integration with async_io. I also found a problem that is IOCP port still can receive event after the file handle is removed from the poller and file handle is closed as long as there is I/O operation with overlapped pointer. But the packet has been dropped at this time. This will cause memory access violation I will request review after the issue is fixed. I will also run code in miri and solve your comments. |
59c489c to
4089e77
Compare
|
Hi @notgull I have updated the code to solve IocpFilePacket returned by add_file lifetime issue. The idea is IocpFilePacket will increase reference count of Packet, so the Pakcet is still valid when the Poller is dropped. And for every success overlapped I/O operation ((the operation return TRUE or ERROR_IO_PENDING) will also add reference count, the reference count will decrease after the IOCP event is received. This will make sure the IOCP returned overlapped pointer still valid even the packet is remove from the poller and IocpFilePacket is dropped. I also update the code according your previous comments. I implmeneted NamedPipe in async-io according the updated polling code. The test code is https://github.com/klu-dev/async-io/blob/kail/windows_named_pipe/tests/windows_named_pipe.rs . The main function has completed. I am thinking adding MESSAGE mode and configurable parameter when create named pipe. I try to run miri, but get the following error. Miri do not support code calling native OS API. So the code add an IocpFilePacket::test_ref_count method used in test case to check Packet Arc count. test writable_after_register ... error: unsupported operation: CreateFileW: Unsupported flags_and_attributes: 1073741824 |
550d1eb to
f7d8dad
Compare
|
Noting that I've seen this patch, I'm doing research to ensure everything is sound. |
|
Hi, first of all thanks for the hard work! I don't know if this PR is still on the radar, but I am experimenting with it and the https://github.com/arnodb/teleop/tree/windows-named-pipe My results so far are visible at https://github.com/arnodb/teleop/actions/runs/21923871658 (or more recent runs of CI on the branch). Surprisingly the unit test
I hope this use case can help making progress on overlapped I/O in |
|
@klu-dev, |
|
@arnodb Sorry for reply lately. I am looking at the issue. The crash comes from the poll_flush can be fixed easily. But I found another issue for implementation in async-io about windows named pipe. The problem is named pipe events are edge triggered. The event is only triggered once, so current implmentation does not support poll many times like select! macro in the server.rs in teleop. I am trying to fix this issue. |
|
@arnodb The issue has been fixed in klu-dev/async-io@fd02db0 . async-io repo is updated, but there is no code change for polling repo. I found another issue for your server.rs code, the future async-io NamedPipeListener::bind() add PipeMode and NamedPipeOpenOptions parameters to support set pipe params. Your code need to change to |
f7d8dad to
626caf9
Compare
|
Hi @klu-dev, thanks for this update! I pushed (force) the latest changes to my branch. It looks like it works now 🎉. Regarding the If I understand your comment correctly: all the tasks in the executor are polled whenever there is activity in one of them (e.g. a byte sent through the wire), correct? I didn't know that (still new to (Good news: |
|
@arnodb I found the root cause which is poll_flush bug in async-io for named pipe. It is fixed in commit klu-dev/async-io@10126a1. |
edf7e16 to
85a3100
Compare
|
Sorry for the delay; this is large PR and I haven't found the time to sit down and review the entire thing in one sitting. |
85a3100 to
626caf9
Compare
|
@notgull I update the code based on AI code review. The code improvement could refer to section 2 and 3 in |
Adds Windows file-handle (named pipe / file / device) support to the IOCP
backend via a new completion-based API:
- New `PollerIocpFileExt` trait with `register_file` for binding any
`FILE_FLAG_OVERLAPPED` handle to the poller.
- `RegisteredFile` exposes `submit_read`, `submit_write`, and
`submit_connect_named_pipe`, returning a `Submission`
(Complete / Pending / Failed).
- Pending submissions yield an `OpHandle<B>` that owns the operation
lifecycle: completion state, NTSTATUS, bytes transferred, and buffer
hand-back via `take`. The dispatcher emits
`Event { key, readable, writable }` so reactors (e.g. async-io) keep
ownership of waker registration.
- `set_user_key` lets reactors rebind the event key after registration.
- `StableBuf` / `StableBufMut` traits define the owned-buffer contract;
blanket impls for `Vec<u8>`, `Box<[u8]>`, and `&'static [u8]`.
- IOCP completion key now uses a high-bit tag to discriminate socket vs
file ops; per-op packets are reclaimed via `Arc::from_raw` in the
dispatcher.
- NTSTATUS -> io::Error mapping covers ACCESS_DENIED, INVALID_HANDLE,
INVALID_PARAMETER, BUFFER_TOO_SMALL, BUFFER_OVERFLOW, TIMEOUT, and
IO_TIMEOUT; STATUS_BUFFER_OVERFLOW is treated as a successful short
read.
- `submit_read` / `submit_write` reject buffers larger than u32::MAX up
front.
- Bumps MSRV to 1.77 (required by `std::mem::offset_of!`).
- Adds design doc (`docs/named-pipe.design.md`), examples, and
integration tests covering lifetime, concurrent multi-op,
oversized-buffer rejection, key rebinding, and direction emission.
Closes the work tracked in PR smol-rs#248.
b3f02d5 to
677773e
Compare
Adds Windows file-handle (named pipe / file / device) support to the IOCP
backend via a new completion-based API:
- New `PollerIocpFileExt` trait with `register_file` for binding any
`FILE_FLAG_OVERLAPPED` handle to the poller.
- `RegisteredFile` exposes `submit_read`, `submit_write`, and
`submit_connect_named_pipe`, returning a `Submission`
(Complete / Pending / Failed).
- Pending submissions yield an `OpHandle<B>` that owns the operation
lifecycle: completion state, NTSTATUS, bytes transferred, and buffer
hand-back via `take`. The dispatcher emits
`Event { key, readable, writable }` so reactors (e.g. async-io) keep
ownership of waker registration.
- `set_user_key` lets reactors rebind the event key after registration.
- `StableBuf` / `StableBufMut` traits define the owned-buffer contract;
blanket impls for `Vec<u8>`, `Box<[u8]>`, and `&'static [u8]`.
- IOCP completion key now uses a high-bit tag to discriminate socket vs
file ops; per-op packets are reclaimed via `Arc::from_raw` in the
dispatcher.
- NTSTATUS -> io::Error mapping covers ACCESS_DENIED, INVALID_HANDLE,
INVALID_PARAMETER, BUFFER_TOO_SMALL, BUFFER_OVERFLOW, TIMEOUT, and
IO_TIMEOUT; STATUS_BUFFER_OVERFLOW is treated as a successful short
read.
- `submit_read` / `submit_write` reject buffers larger than u32::MAX up
front.
- Bumps MSRV to 1.77 (required by `std::mem::offset_of!`).
- Adds design doc (`docs/named-pipe.design.md`), examples, and
integration tests covering lifetime, concurrent multi-op,
oversized-buffer rejection, key rebinding, and direction emission.
Closes the work tracked in PR smol-rs#248.
677773e to
15008c2
Compare
Adds Windows file-handle (named pipe / file / device) support to the IOCP
backend via a new completion-based API:
- New `PollerIocpFileExt` trait with `register_file` for binding any
`FILE_FLAG_OVERLAPPED` handle to the poller.
- `RegisteredFile` exposes `submit_read`, `submit_write`, and
`submit_connect_named_pipe`, returning a `Submission`
(Complete / Pending / Failed).
- Pending submissions yield an `OpHandle<B>` that owns the operation
lifecycle: completion state, NTSTATUS, bytes transferred, and buffer
hand-back via `take`. The dispatcher emits
`Event { key, readable, writable }` so reactors (e.g. async-io) keep
ownership of waker registration.
- `set_user_key` lets reactors rebind the event key after registration.
- `StableBuf` / `StableBufMut` traits define the owned-buffer contract;
blanket impls for `Vec<u8>`, `Box<[u8]>`, and `&'static [u8]`.
- IOCP completion key now uses a high-bit tag to discriminate socket vs
file ops; per-op packets are reclaimed via `Arc::from_raw` in the
dispatcher.
- NTSTATUS -> io::Error mapping covers ACCESS_DENIED, INVALID_HANDLE,
INVALID_PARAMETER, BUFFER_TOO_SMALL, BUFFER_OVERFLOW, TIMEOUT, and
IO_TIMEOUT; STATUS_BUFFER_OVERFLOW is treated as a successful short
read.
- `submit_read` / `submit_write` reject buffers larger than u32::MAX up
front.
- Bumps MSRV to 1.77 (required by `std::mem::offset_of!`).
- Adds design doc (`docs/named-pipe.design.md`), examples, and
integration tests covering lifetime, concurrent multi-op,
oversized-buffer rejection, key rebinding, and direction emission.
Closes the work tracked in PR smol-rs#248.
15008c2 to
c7abfb9
Compare
Closes #97
This is an implementation to support Windows file handle overlapped I/O. The basic idea is: