4

I have a Xlib-based program with an event loop that uses XNextEvent to receive and process relevant events.

I would like to be able to gracefully close this program from another process (actually from a shell script). I need to do some cleanup when closing, so I considered to setup a signal handler (for example for SIGUSR1) and when this signal is received, do the appropriate cleanup.

My question is, how can I interrupt the (blocking) XNextEvent call from the signal handler?

Any other suggestions?

Grodriguez
  • 21,501
  • 10
  • 63
  • 107
  • I'm not sure I understand your question. Signals already interrupt the normal flow of the program. The system call inside `XNextEvent()` will be interrupted in order for your handler to run. You have nothing to do to achieve this. – Frédéric Hamidi Jan 30 '14 at 13:45
  • I assume `XNextEvent()` will be interrupted for my handler to run, but once the handler is run, execution will resume and `XNextEvent()` will still be blocked. The question is how to (safely) unblock `XNextEvent()` from the signal handler. – Grodriguez Jan 30 '14 at 15:17
  • 1
    I see. I imagine you do not want to `exit()` from the signal handler. I don't think `XCloseDisplay()` is safe to call from a signal handler either. Maybe setting a global boolean and injecting an event so `XNextEvent()` returns right away would be an option. I'll look around. – Frédéric Hamidi Jan 30 '14 at 15:43
  • 1
    Unfortunately, after searching a bit, I don't think this can be done. No Xlib function is safe to call within a signal handler, and having `XNextEvent()` return after a signal is not possible unless you patch Xlib. Interesting material about this situation can be found [here](http://www-h.eng.cam.ac.uk/help/tpl/graphics/X/signals.html). – Frédéric Hamidi Jan 30 '14 at 16:28
  • 1
    Very interesting read. – Grodriguez Jan 30 '14 at 19:39
  • @FrédéricHamidi Thank you for your comment and for the link, I think I found a way to solve this (see my answer). – Grodriguez Jan 31 '14 at 10:20

2 Answers2

6

I found a way to do this based on this SO question and this one.

Basically you can use the ConnectionNumber() macro to get the fd that XNextEvent() is reading from. This lets me call select() myself to wait for data on the Xlib fd and some other fd. Now it is select() that is blocking, and not XNextEvent(). I can easily unblock select() from my signal handler by writing to the second fd.

Pseudo-code for the event loop:

/* Get X11 fd */
x11_fd = ConnectionNumber(display);

while(1) {
    /* Create a File Description Set containing x11_fd and other_fd */
    FD_ZERO(&in_fds);
    FD_SET(x11_fd, &in_fds);
    FD_SET(other_fd, &in_fds);

    /* Wait for X Event or exit signal */
    ret = select(nfds, &in_fds, ...);
    if (FD_ISSET(other_fd, &in_fds) {
        /* Do any cleanup and exit */
    } else {
        while (XEventsQueued(display, QueuedAlready) > 0) {
            /* Process X events */
        }
    }
}
Community
  • 1
  • 1
Grodriguez
  • 21,501
  • 10
  • 63
  • 107
  • Can you please clarify this, the code you've provided is incomplete... Shouldn't nfds be replaced by x11_fd + 1? And why not use exceptfds? – Centril Oct 16 '15 at 07:27
  • As explicitly stated in the answer, this is **pseudo-code** showing how to solve the specific problem stated in the question; it is not meant to be a standalone example. Re. your questions: 1) nfds should be "the highest-numbered file descriptor in any of the fd sets passed to select, plus 1". If x11_fd is the highest-numbered file descriptor, then you can replace nfds with x11_fd+1, otherwise not. It depends on your use case. 2) "Why not use exceptfds". Nobody says not to use it, it again depends on your use case. – Grodriguez Oct 16 '15 at 09:49
  • I see, how do I create `other_fd`, and how do I unblock select by writing to `other_fd`? – Centril Oct 16 '15 at 09:59
  • Depends on your specific application and use case. Your other_fd could be stdin, it could be a pipe, it could be a network socket.. Better post a separate SO question explaining your actual problem. – Grodriguez Oct 16 '15 at 10:35
  • The actual problem is exactly this post, it's 0% different... Is there some way of doing this where you can unblock select with other_fd for all *nix platforms? As far as I understand, `eventfd`s are Linux only... – Centril Oct 16 '15 at 10:42
  • If your question is "how do I create other_fd and how do I unblock select" then that is definitely a different question. Also: `select` (and its arguments) is part of POSIX, not Linux specific. – Grodriguez Oct 16 '15 at 17:46
0

Assuming you have the process id, you can use the kill function:

int kill(pid_t pid, int sig);

You can send any signal but SIGKILL (SIGKILL cannot be handled)

ichramm
  • 6,437
  • 19
  • 30
  • 1
    How can I (safely) unblock `XNextEvent()` from the signal handler? – Grodriguez Jan 30 '14 at 15:17
  • 1
    Using [`XSendEvent()`](http://h50146.www5.hp.com/products/software/oe/tru64unix/manual/v51a_ref/HTML/MAN/MAN3/3363_X11.HTM) with a fake event? – ichramm Jan 30 '14 at 15:41