Skip to content

Open special files in another thread #15768

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

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

ysbaddaden
Copy link
Contributor

We can't detect without significant overhead whether the file at path might block for a while before calling open.

While O_NONBLOCK has no effect on regular disk files, special file types are a different story. Open with O_NONBLOCK will fail with ENXIO for O_WRONLY (no connected reader) but it will always succeed for O_RDONLY (regardless of a connected writer or not), then any attempt to read will return EOF, leaving no means to wait until a writer connects.

I thus rely on the blocking arg: when nil the file might be a special file type, so I check it's type, when false I consider it's a special file. If the file is a fifo (named pipe) or a character device, I open it in another thread to not risk blocking the current thread (and thus other fibers) until a reader or writer is also connected.

Opening regular files is unaffected: we still open directly, while opening a special file will now avoid blocking the current thread.

NOTE: we need the preview_mt flag to safely re-enqueue the current fiber from the bare thread.

Depends on #15754 and #15767.

A bare thread doesn't have an execution context, yet may be capable to
call Fiber#enqueue to enqueue a fiber back into its original context.
We can't reliably detect without significant overhead whether the file at
*path* might block for a while before calling open; while O_NONBLOCK has
no effect on regular disk files, special file types are a different story.

Open with O_NONBLOCK will fail with ENXIO for O_WRONLY (no connected
reader) but it will always succeed for O_RDONLY (regardless of a connected
writer or not), then any attempt to read will return EOF, leaving no means
to wait until a writer connects.

We thus rely on the *blocking* arg: when false the file might be a special
file type, so we check it; if it's a fifo (named pipe) or a character
device, we open in another thread so we don't risk blocking the current
thread (and thus other fibers) until a reader or writer is also connected.

We need preview_mt to safely re-enqueue the current fiber from the thread.
@@ -14,12 +14,37 @@ module Crystal::System::File
end
end

{% if flag?(:preview_mt) && !flag?(:interpreted) %}
protected def self.async_open(filename, flags, permissions)
Copy link
Member

Choose a reason for hiding this comment

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

polish: "async" seems a big ambiguous in the context of the event loop. The method name could express better that this is about parallel open in a different thread. Maybe .open_threaded?

@ysbaddaden
Copy link
Contributor Author

ysbaddaden commented May 12, 2025

After talking with @straight-shoota this PR might be a bit rushed/shallow.

We should take execution contexts into account.

  1. The isolated context doesn't need to start a thread: we can block the current fiber/thread;

  2. MT context could indeed detach the scheduler as Go does (can be moved to another waiting thread, or reattached upon return), and I guess we could have a pool of available threads (shared between contexts); an issue is how many maximum threads do we want 😅

  3. ST context might be more challenging: we could also detach the thread if we're just wrapping a syscall, it might be acceptable... as long as we don't expect thread locals 🤔

That would allow a more general and useful solution. For example we could wrap addrinfo in addition to open.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants