@@ -51,4 +51,49 @@ blocking methods within the forks.
5151
5252Virtual threads are normally not visible when using tools such as ` jstack ` or IntelliJ's debugger. To inspect their
5353stack traces, you'll need to create a thread dump to a file using ` jcmd [pid] Thread.dump_to_file [file] ` , or use
54- Intellij's thread dump utility, when paused in the debugger.
54+ Intellij's thread dump utility, when paused in the debugger.
55+
56+ ## Dealing with uninterruptible stdin
57+
58+ Some I/O operations, like reading from stdin, block the thread on the ` read ` syscall and only unblock when data becomes
59+ available or the stream is closed. The problem with stdin specifically is that it can't be easily closed, making it
60+ impossible to interrupt such operations directly. This pattern extends to other similar blocking operations that behave
61+ like stdin.
62+
63+ The solution is to delegate the blocking operation to a separate thread and use a [ channel] ( ../streaming/channels.md )
64+ for communication. This thread cannot be managed by Ox as Ox always attempts to run clean up when work is done and that
65+ means interrupting all forks living in the scope that's getting shut down. Blocking I/O can't, however, be interrupted
66+ on the JVM and the advised way of dealing with that is to just close the resource which in turn makes read/write methods
67+ throw an ` IOException ` . In the case of stdin closing it is usually not what you want to do. To work around that you can
68+ sacrifice a thread and since receiving from a channel is interruptible, this makes the overall operation interruptible as well:
69+
70+ ``` scala
71+ import ox .* , channels .*
72+ import scala .io .StdIn
73+
74+ object stdinSupport :
75+ private lazy val chan : Channel [String ] =
76+ val rvChan = Channel .rendezvous[String ]
77+
78+ Thread
79+ .ofVirtual()
80+ .start(() => forever(rvChan.sendOrClosed(StdIn .readLine()).discard))
81+
82+ rvChan
83+
84+ def readLineInterruptibly : String =
85+ try chan.receive()
86+ catch
87+ case iex : InterruptedException =>
88+ // Handle interruption appropriately
89+ throw iex
90+ ```
91+
92+ This pattern allows you to use stdin (or similar blocking operations) with ox's timeout and interruption mechanisms,
93+ such as ` timeoutOption ` or scope cancellation.
94+
95+ Note that for better stdin performance, you can use ` Channel.buffered ` instead of a rendezvous channel, or even use
96+ ` java.lang.System.in ` directly and proxy raw data through the channel. Keep in mind that this solution leaks a thread
97+ that will remain blocked on stdin for the lifetime of the application. It's possible to avoid this trade-off by using
98+ libraries that employ JNI/JNA to access stdin, such as [ JLine 3] ( https://jline.org/docs/intro ) , which can use raw mode
99+ with non-blocking or timeout-based reads, allowing the thread to be properly interrupted and cleaned up.
0 commit comments