public inbox for cygwin@cygwin.com
 help / color / mirror / Atom feed
* SIGINT lost while program calls PeekMessage
@ 2020-07-02 16:16 Jon Beniston
  2020-07-03 19:22 ` Corinna Vinschen
  0 siblings, 1 reply; 4+ messages in thread
From: Jon Beniston @ 2020-07-02 16:16 UTC (permalink / raw)
  To: cygwin

[-- Attachment #1: Type: text/plain, Size: 813 bytes --]

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. 

Cheers,
Jon


Windows 10 Professional Ver 10.0 Build 18363
    Cygwin DLL version info:
        DLL version: 3.1.5


[-- Attachment #2: sigint_example.c --]
[-- Type: application/octet-stream, Size: 1945 bytes --]

#include <signal.h>
#include <stdio.h>

#include <windows.h>

int cnt;
HWND window;
volatile int x = 0;

void ctrl_c_handler(int x)
{
  signal(SIGINT, ctrl_c_handler);
  printf("sig=%d cnt=%d\n", x, ++cnt);
}

LRESULT CALLBACK window_proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch (message) 
    {
    case WM_DESTROY:
      PostQuitMessage(0);
      x=1;
      break;
      
    default:
      return DefWindowProc(hWnd, message, wParam, lParam);
  }
  return 0;
}

void create_win()
{
  HINSTANCE hInstance;
  WNDCLASSEX wcex; 
             
  hInstance = GetModuleHandle(NULL);
    
  wcex.cbSize = sizeof(wcex);
  wcex.style = CS_HREDRAW | CS_VREDRAW;
  wcex.lpfnWndProc = window_proc;
  wcex.cbClsExtra = 0;
  wcex.cbWndExtra = 0;
  wcex.hInstance = hInstance;
  wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
  wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
  wcex.lpszMenuName = NULL;
  wcex.lpszClassName = "CtrlCWin";
  wcex.hIconSm = NULL;
                     
  if (RegisterClassEx(&wcex))
    {        
      window = CreateWindow("CtrlCWin", "CtrlCWin", WS_OVERLAPPEDWINDOW,
                                 CW_USEDEFAULT, CW_USEDEFAULT, 270, 230, NULL, 
                                 NULL, hInstance, NULL);
      if (window) 
        {
          ShowWindow(window, SW_SHOW);
          UpdateWindow(window);
        }
    }
}

int main()
{
  MSG msg;
  BOOL bRet;

  signal(SIGINT, ctrl_c_handler);
  
  create_win();

  while(!x)
  {
    /* If this call to PeekMessage is removed, signals get through */ 
    while (PeekMessage(&msg, window, 0, 0, PM_NOREMOVE)) 
      {
        bRet = GetMessage(&msg, window, 0, 0);
        if (bRet > 0) 
          {  
            TranslateMessage(&msg);
            DispatchMessage(&msg);
          }  
      }    
  }
  return 0;
}

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: SIGINT lost while program calls PeekMessage
  2020-07-02 16:16 SIGINT lost while program calls PeekMessage Jon Beniston
@ 2020-07-03 19:22 ` Corinna Vinschen
  0 siblings, 0 replies; 4+ messages in thread
From: Corinna Vinschen @ 2020-07-03 19:22 UTC (permalink / raw)
  To: cygwin

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

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: SIGINT lost while program calls PeekMessage
  2020-07-06 13:16 Jon Beniston
@ 2020-07-06 18:21 ` Corinna Vinschen
  0 siblings, 0 replies; 4+ messages in thread
From: Corinna Vinschen @ 2020-07-06 18:21 UTC (permalink / raw)
  To: cygwin

On Jul  6 14:16, Jon Beniston wrote:
> Hi Corinna,
> 
> >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.
> 
> Ok, thanks for the explanation. In the real application, there is some
> actual processing inside the loop, besides the call to PeekMessage and more
> signals get through (Probably 75%), I just cut the code down to make a
> simpler test case. I'll add a Cygwin call as suggested.

Actually, if the application is using lots of Windows calls and (almost)
no Cygwin calls, I'd suggest to drop signals in favor of utilizing the
Windows message queue to signal specific states to the executable.

As for Cygwin's signal handling, it's a bit finicky in border cases,
and the fact that a signal can be queued only once is not very 21st
century either.  I haven't got a grip on these problems yet, but I'll
look into them for sure at one point.


Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

^ permalink raw reply	[flat|nested] 4+ messages in thread

* RE: SIGINT lost while program calls PeekMessage
@ 2020-07-06 13:16 Jon Beniston
  2020-07-06 18:21 ` Corinna Vinschen
  0 siblings, 1 reply; 4+ messages in thread
From: Jon Beniston @ 2020-07-06 13:16 UTC (permalink / raw)
  To: cygwin

Hi Corinna,

>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.

Ok, thanks for the explanation. In the real application, there is some
actual processing inside the loop, besides the call to PeekMessage and more
signals get through (Probably 75%), I just cut the code down to make a
simpler test case. I'll add a Cygwin call as suggested.

Cheers,
Jon


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2020-07-06 18:21 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-02 16:16 SIGINT lost while program calls PeekMessage Jon Beniston
2020-07-03 19:22 ` Corinna Vinschen
2020-07-06 13:16 Jon Beniston
2020-07-06 18:21 ` Corinna Vinschen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).