Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion doc/other/best-practices.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Member

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)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok

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
Copy link
Member

Choose a reason for hiding this comment

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

maybe a link on how to do that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

meaning how to use jline3 or how it uses raw mode with O_NONBLOCKING or VTIME=1?

Copy link
Member

Choose a reason for hiding this comment

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

just a link to JLine 3 - maybe an appropriate docs page, or anything that would help people use it in a project

timeout-based reads, allowing the thread to be properly interrupted and cleaned up.