Pollable signalling between threads Pollable signalling between threads unix unix

Pollable signalling between threads


Jan Hudec's answer is correct, although I wouldn't recommend using signals for a few reasons:

  • Older versions of glibc emulated pselect and ppoll in a non-atomic fashion, making them basically worthless. Even when you used the mask correctly, signals could get "lost" between the pthread_sigprocmask and select calls, meaning they don't cause EINTR.
  • I'm not sure signalfd is any more efficient than the pipe. (Haven't tested it, but I don't have any particular reason to believe it is.)
  • signals are generally a pain to get right. I've spent a lot of effort on them (see my sigsafe library) and I'd recommend avoiding them if you can.

Since you're trying to have asynchronous handling portable to several systems, I'd recommend looking at libevent. It will abstract epoll or kqueue for you, and it will even wake up workers on your behalf when you add a new event. See event.c

2058 static inline int2059 event_add_internal(struct event *ev, const struct timeval *tv,2060     int tv_is_absolute)2061 {...2189         /* if we are not in the right thread, we need to wake up the loop */2190         if (res != -1 && notify && EVBASE_NEED_NOTIFY(base))2191                 evthread_notify_base(base);...2196 }

Also,

The worker thread deals with both socket I/O and asynchronous disk I/O, which means that it is optimally always waiting for the event queuing mechanism (epoll/kqueue).

You're likely to be disappointed here. These event queueing mechanisms don't really support asynchronous disk I/O. See this recent thread for more details.


As far as performance goes, the cost of system call is comparably huge to other operations, so it's the number of system calls that matters. There are two options:

  1. Use the pipes as you wrote. If you have any useful payload for the message, you get one system call to send, one system call to wait and one system call to receive. Try to pass any relevant data down the pipe instead of reading them from a shared structure to avoid additional overhead from locking.
  2. The select and poll have variants, that also waits for signals (pselect, ppoll). Linux epoll can do the same using signalfd, so it remains a question whether kqueue can wait for signals, which I don't know. If it can, than you could use them (you are using different mechanism on Linux and *BSD anyway). It would save you the syscall for reading if you don't have good use for the passed data.

I would expect passing the data over socket to be more efficient if it allows you do do away with any other locking.