SIGINT lost while program calls PeekMessage

Corinna Vinschen corinna-cygwin@cygwin.com
Fri Jul 3 19:22:25 GMT 2020


On Jul  2 17:16, Jon Beniston wrote:
> Hi,
> 
> I have a Cygwin program that:
> 
> - registers a SIGINT handler, via signal(SIGINT,h)
> - creates a window, using Win32 CreateWindow
> - and is then calling PeekMessage for that window in a loop
> 
> It appears that while PeekMessage is being called, any SIGINTs sent to the
> program are lost. Is this to be expected as it's not really supported or a
> bug?
> 
> To reproduce:
> 
> - Compile and run the attached example program, and repeatedly press CTRL-C
> in the console. 
> - Only maybe 1 of every 10 or 20 times CTRL-C is pressed, is the signal
> handler called.
> - Similarly, running 'kill -SIGINT PID' results in most of the signals being
> ignored.
> - Remove the call to PeekMessage, and the signals are received. 

The cause of the lost signals is the fact that PeekMessage is called in
a very tight busy loop(*).

As soon as you ease the pain by adding a usleep(1) practically all
signals will arrive(**).

The problem is this: Every process has a so-called "wait_sig" thread.
This thread waits for signals and when it gets one, it tries to process
it.  To be able to call a user-space signal handler, the thread supposed
to handle this signal (here: the main thread) has to be interruptible.

Threads are not interruptible while they are running in a Windows system
DLL (like, e.g., kernel32.dll).

Since your example code is running very tightly most of its time inside
some Windows system DLL function, there's next to no chance to interrupt
the thread to inject the signal handler call.  Cygwin tries this 100
times per signal.  If that fails, the signal gets lost.

One way to solve this problem is adding a Cygwin call into the loop,
like the aforementioned usleep.  When a Cygwin call is performed, the
signal handler will be called as soon as the Cygwin call returns to
user-space.

The bottom line is, Cygwin user space signal handlers and lots of
Windows-only calls in a tight loop don't work nicely together.


Corinna

(*)  Which is a no-no.  Better use a thread calling GetMessage or
     MsgWaitForMultipleObjectsEx in a loop.

(**) "Practically", because every signal is only queued once yet.
     That is, if a SIGINT is queued and another SIGINT arrives before
     the first SIGINT got dequeued, the second signal is ignored.

-- 
Corinna Vinschen
Cygwin Maintainer


More information about the Cygwin mailing list