-
Notifications
You must be signed in to change notification settings - Fork 32
add interruptible stdin hint to docs #377
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -51,4 +51,45 @@ blocking methods within the forks. | |
|
|
||
| Virtual threads are normally not visible when using tools such as `jstack` or IntelliJ's debugger. To inspect their | ||
| stack traces, you'll need to create a thread dump to a file using `jcmd [pid] Thread.dump_to_file [file]`, or use | ||
| Intellij's thread dump utility, when paused in the debugger. | ||
| Intellij's thread dump utility, when paused in the debugger. | ||
|
|
||
| ## Dealing with uninterruptible stdin | ||
|
|
||
| Some I/O operations, like reading from stdin, block the thread on the `read` syscall and only unblock when data becomes | ||
| available or the stream is closed. The problem with stdin specifically is that it can't be easily closed, making it | ||
| impossible to interrupt such operations directly. This pattern extends to other similar blocking operations that behave | ||
| like stdin. | ||
|
|
||
| The solution is to delegate the blocking operation to a separate thread and use a [channel](../streaming/channels.md) | ||
| for communication. Since receiving from a channel is interruptible, this makes the overall operation interruptible as well: | ||
|
|
||
| ```scala | ||
| import ox.*, channels.* | ||
| import scala.io.StdIn | ||
|
|
||
| object stdinSupport: | ||
| private lazy val chan: Channel[String] = | ||
| val rvChan = Channel.rendezvous[String] | ||
|
|
||
| Thread | ||
| .ofVirtual() | ||
| .start(() => forever(rvChan.sendOrClosed(StdIn.readLine()).discard)) | ||
|
|
||
| rvChan | ||
|
|
||
| def readLineInterruptibly: String = | ||
| try chan.receive() | ||
| catch | ||
| case iex: InterruptedException => | ||
| // Handle interruption appropriately | ||
| throw iex | ||
| ``` | ||
|
|
||
| This pattern allows you to use stdin (or similar blocking operations) with ox's timeout and interruption mechanisms, | ||
| such as `timeoutOption` or scope cancellation. | ||
|
|
||
| Note that for better stdin performance, you can use `Channel.buffered` instead of a rendezvous channel, or even use | ||
| `java.lang.System.in` directly and proxy raw data through the channel. Keep in mind that this solution leaks a thread | ||
| that will remain blocked on stdin for the lifetime of the application. It's possible to avoid this trade-off by using | ||
| libraries that employ JNI/JNA to access stdin, such as JLine 3, which can use raw mode with non-blocking or | ||
|
||
| timeout-based reads, allowing the thread to be properly interrupted and cleaned up. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome that you remembered - thanks! :) Maybe it would also be good to mention why the thread needs to be manually created, not Ox-managed (that is, not created through
fork)?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok