I'm developing an application that is going to read data from a serial / UART on Linux. I'm trying to wake up/unblock a pthread started to read the data when the UART has data available from an external source asynchronously. I was looking at using a signal (SIGIO) "soft interrupt" instead of select() or pselect(), but I'm not sure I can get the behavior I want in a multi-threaded application from signals.
When I configure the device file descriptor I want to set F_SETOWN(int) to get the kernel to send a signal to the pthread I've set to block on SIGIO.
// Configure the signal to send
fcntl(m_fileId, F_SETSIG, m_sigNum);
// Set to non-blocking read mode
fcntl(m_fileId, F_SETFL, FNDELAY);
// Bind the PID of the task which is getting the signal
fcntl(m_fileId, F_SETOWN, m_pid);
// Enable sending a signal when data ready
fcntl(m_fileId, F_SETFL, O_ASYNC);
What I realized is that when I wrote the code m_pid was set to the process ID, not the pthread ID and when I checked the man page...
According to the fnctl(2) man page:
F_SETOWN (int)
Set the process ID or process group ID that will receive SIGIO and SIGURG signals for events on file descriptor fd to the ID given in arg. A process ID is specified as a positive value; a process group ID is specified as a negative value. Most commonly, the calling process specifies itself as the owner (that is, arg is specified as getpid(2)).
Instead of using getpid() for the process ID can you pass in a pthread ID from a call to pthread_self() so only a specific thread gets the signal?
On Linux, you can use F_SETOWN_EX to target a particular thread.
From the manpage:
The following was true in 2.6.x kernels up to and including kernel 2.6.11:
If a nonzero value is given to F_SETSIG in a multithreaded process running with a threading library that sup‐ ports thread groups (e.g., NPTL), then a positive value given to F_SETOWN has a different meaning: instead of being a process ID identifying a whole process, it is a thread ID identifying a specific thread within a process. Consequently, it may be necessary to pass F_SETOWN the result of gettid(2) instead of getpid(2) to get sensible results when F_SETSIG is used. (In current Linux threading implementations, a main thread's thread ID is the same as its process ID. This means that a single-threaded program can equally use gettid(2) or getpid(2) in this scenario.) Note, however, that the statements in this paragraph do not apply to the SIGURG signal generated for out-of-band data on a socket: this signal is always sent to either a process or a process group, depending on the value given to F_SETOWN.
The above behavior was accidentally dropped in Linux 2.6.12, and won't be restored. From Linux 2.6.32 onward, use F_SETOWN_EX to target SIGIO and SIGURG signals at a particular thread.
Linux will expect a pid/tid (thread id from the point of view of the kernel; Linux often calls them just pids even though they identify threads not processes), though, not a pthread_t.
Pthread implementations will generally map pthread_t's to pids internally in an implementation specific way and you could hack against your particular implementation to get it.
Alternatively you could wrap pthread_create to insert a pthread_t-to-pid mapping hash table entry upon thread creation so that you can later map pthread_ts to pids without dependencies on your pthread implementation.
(you can obtain a pid of the current thread with syscall(SYS_gettid)).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With