15

In a multi-threaded Linux program used for serial communication, is it possible (and what would be the best approach) to terminate a blocking read() call from another thread?

I would like to keep everything as reactive as possible and avoid any use of timeouts with repeated polling.

The background of this question is that I'm trying to create a Scala serial communication library for Linux using JNI. I'm trying to keep the native side as simple as possible providing, amongst others, a read() and close() function. On the Scala side, one thread would call read() and block until data from the serial port is available. However, the serial port can be closed by other means, resulting in a call to close(). Now, to free up the blocked thread, I would somehow need to cancel the system read call.

Jakob Odersky
  • 1,371
  • 11
  • 23

3 Answers3

20

One fairly popular trick: instead of blocking in read(), block in select() on both your serial-socket and a pipe. Then when another thread wants to wake up your thread, it can do so by writing a byte to the other end of that pipe. That byte will cause select() to return and your thread can now cleanup and exit or whatever it needs to do. (Note that to make this work 100% reliably you'll probably want to set your serial-socket to be non-blocking, to ensure that your thread only blocks in select() and never in read())

Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
  • 1
    seconded. I do almost all my multithreaded programming that way. It's a pattern right out of Communicating Sequential Processes (CSP). Given that Scala does Communicating Scala Objects (a practical implementation of CSP) I think you suggestion is highly appropriate. BTW the biggest drawback of programming for Windows Win32 or .NET is that you can't select() on pipes, a massive omission. MS would have you believe that async IO is better, but it leads to really messy code. The Cygwin project had problems writing select(). Their solution involves a lot of threads and polling. – bazza May 20 '13 at 05:35
  • FWIW you can do the same trick (with only 10 times the effort ;)) under Windows by creating two TCP sockets and connecting() one of them to the other one, as shown here: http://code.google.com/p/imagesoft/source/browse/trunk/rpcrt4/socketpair.c?r=5 – Jeremy Friesner May 20 '13 at 05:47
  • 1
    Thanks for the answer! Select didn't work for me (maybe I did something wrong), however whilst reading select() man pages I stumbled upon poll(). That worked perfectly :) – Jakob Odersky May 20 '13 at 12:46
  • Using a pipe also proved useful for sending data to the thread. To signal that it was time to close the thread, I simply `close`'d the pipe from the main thread. When the blocking thread gets `POLLHUP` on the pipe, it can shut down. – John Leuenhagen Aug 28 '20 at 03:34
  • How would this be done in the analogous case of `poll`-`write` where the "waiting thread" is stuck on a `poll(POLLOUT)`? – PiCTo Apr 21 '21 at 09:02
  • 1
    @PiCTo I think you would need to modify the waiting thread to add a `POLLIN` to its `poll()` call so that it would also return when a byte arrives on its notification-pipe. – Jeremy Friesner Apr 21 '21 at 14:18
5

AFAIK signals are the only way to break any thread out of a blocking system call.

Use a pthread_kill() aimed at the thread with a USR1 signal.

kja
  • 101
  • 1
  • 5
  • Thanks for the answer, but a pthread_kill would not be possible in my use-case (since the functions are called from different threads of a JVM). Are there any workarounds I could use? I heard some stuff about epoll but my tests didn't work either. – Jakob Odersky May 19 '13 at 17:30
  • I don't know enough about JNI to make a recommendation. You might use `epoll` from one thread and `close()` from another but I believe that's undefined behavior. – kja May 19 '13 at 18:04
1

You could probably do fake data input:

tty_ioctl(fd,TIOCSTI,"please unblock!");

Before calling it you should set some global flag, in order be able to check after 'read(...)' returns, if received data are just wake up goo or rather something more important.

Source: https://www.systutorials.com/docs/linux/man/4-tty_ioctl/

Anonymous
  • 2,122
  • 19
  • 26