Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Re^2: The implementation of SIGHUP in Win32 Perl

by klaten (Novice)
on Sep 02, 2013 at 02:59 UTC ( [id://1051877]=note: print w/replies, xml ) Need Help??


in reply to Re: The implementation of SIGHUP in Win32 Perl
in thread The implementation of SIGHUP in Win32 Perl

I've had some time to think some more about this issue. Here is additional information.

IMHO, the key problem with the current implementation is that it seems to assume the console control handler code is being called as a callback (which is what I believe happens in Windows XP), when actually the console control handler code (in Windows Vista and later) is being called from another thread being started by Windows (see note 1, below). This control handler code then triggers Perl's signal handling code, from the other thread. The signal handling code in the Perl program will be running at the same time the Perl program is. I'd be very surprised if Perl were designed to do that!

In my mind an equivalent scenario would be if I had a Perl program which ran an XSUB that (in C code) started a thread which then tried to call back into the Perl interpreter. Yes, because all the data structures are in the same process, my new thread could access and change things, sure, but due to synchronization and other thread safety issues I'd bet that my new thread would reek havoc upon the Perl interpreter and return results from my thread might be affected by the program still running in the Perl interpreter.

The other problem is that since Windows Vista, the OS (not Perl) will terminate the process upon the return from the console control handler—regardless of the return value (see note 2, below, and from what I've read elsewhere, this process will eventually be terminated by the OS even if the control handler never returns, see note 3, below). This could abort any type of recovery a Perl signal handler might attempt.

Here's what I propose as a possible solution. There is code in win32.c to convert a WM_USER_KILL message into a C runtime signal. Instead of having the console control handler code call back into Perl, have it post (or maybe in the case of SIGHUP, send -- so the thread could wait for a response) a message to the main thread of the console (possibly get the the console window handle via a GetWindowConsole-type function?). This would allow the existing code in win32c to recreate a SIGHUP (and also SIGINT and SIGBREAK, because they too would be affected by the threading issue) signal in the main thread, the thread running the Perl interpreter. Although slightly less efficient on Windows XP, this technique should operate on any Windows OS. Another advantage of this technique is that it would allow the use of a message spy type utility to determine whether or not the signal-simulation messages were actually reaching the console window.

Regarding the process termination issue, I'm less certain. Originally I suggested a rather simplistic "sleep" type solution prior to returning from the console control handler, but I have to admit, that idea stinks. I read here that the timeout on Windows 7 is 10 seconds, but what about Windows Vista? Windows 8? Windows 9, 10, 11? I don't know. Maybe the answer is: just go ahead and let the OS kill the process at that point. I do believe that somehow, the console control handler should be kept from returning until the Perl $SIG handler has a chance to run. How could that happen? Could the idea about the SendMessage for the SIGHUP work? You know better than I on that.

Anyway, here are my reference notes on this topic:

  1. From msdn: "When the signal is received, the system creates a new thread in the process to execute the function."
  2. From msdn: "The CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, and CTRL_SHUTDOWN_EVENT signals give the process an opportunity to clean up before termination. A HandlerRoutine can perform any necessary cleanup, then take one of the following actions:
    • Call the ExitProcess function to terminate the process
    • Return FALSE. If none of the registered handler functions returns TRUE, the default handler terminates the process.
    • Return TRUE. In this case, no other handler functions are called and the system terminates the process."
  3. From the StackOverflow web site: "It looks like you can no longer ignore close requests on Windows 7. You do get the CTRL_CLOSE_EVENT event though, and from that moment on, you get 10 seconds to do whatever you need to do before it auto-closes."
  4. An interesting side note from msdn regarding the CTRL_LOGOFF_EVENT (a similar note appears in the description of the CTRL_SHUTDOWN_EVENT as well): "Note that this signal is received only by services. Interactive applications are terminated at logoff, so they are not present when the system sends this signal."
  • Comment on Re^2: The implementation of SIGHUP in Win32 Perl

Replies are listed 'Best First'.
Re^3: The implementation of SIGHUP in Win32 Perl
by bulk88 (Priest) on Sep 04, 2013 at 00:31 UTC
    IMHO, the key problem with the current implementation is that it seems to assume the console control handler code is being called as a callback (which is what I believe happens in Windows XP), when actually the console control handler code (in Windows Vista and later) is being called from another thread being started by Windows (see note 1, below). This control handler code then triggers Perl's signal handling code, from the other thread. The signal handling code in the Perl program will be running at the same time the Perl program is. I'd be very surprised if Perl were designed to do that!


    It is always executed in a separate thread in every NT OS, not just Vista. DOS Win I can't speak about, but it might have actually interrupted the main thread since in old MSDN docs for the CRT, on DOS Win ONLT, there a quite a number of more CRT signals that are catchable than on MS CRT on NT. The multi-threading of the console event dispatch often can cause assert fails and crashes from Perl if Perl is in a 100% CPU usage loop on a multi core machine. I've filed tickets about it in Perl RT. Signals block all other execution I think on Unix. The console event thing runs in a separate thread without stopping the first thread. The runloop in the main thread will get a NULL and exit quickly if the console event thread hasn't gotten to the exit() yet in most cases, or the main thread is in blocking IO and its not a problem for the child thread to reenter the same interp and do perl global destruction.

    My idea for the fix would be to do a SetThreadContext (C debugger-ish stuff) see http://msdn.microsoft.com/en-us/library/windows/desktop/ms680632%28v=vs.85%29.aspx, and try to implement it unix style with the event interrupting execution and executing in the context of the parent thread. NT does have its own implementation of Unix signals called asynchronous procedure calls (APCs) see http://msdn.microsoft.com/en-us/library/windows/desktop/ms681951%28v=vs.85%29.aspx, but if you read about them, they only run when you, the process, requests to rundown the APC queue, so they are always predictable unlike Unix signals (an intentional design), but most syscalls typical Win32 processes are never the "alertable" kind, and if you have a single DLL that you didn't write yourself in the process, you are probably screwed cuz that DLL will be making non-alertable syscalls. If someone really wanted to (space elevator fantasy here), they could DLL hook kernel32, or SSDT hook it, and add APC dispatching to syscalls that dont run APCs. The syscalls would have to be retried by the layer/shim when they fail because APCs ran. The kernel32 and ntdll alertable syscalls dont do that for you.
      It is always executed in a separate thread in every NT OS, not just Vista.

      Ah, that's good to know, thanks.

      The multi-threading of the console event dispatch often can cause assert fails and crashes from Perl if Perl is in a 100% CPU usage loop on a multi core machine.

      That makes sense. So basically we were all just lucky that the other two event triggered signals, SIGINT and SIGBREAK worked. Which validated the advice "don't do too much in your signal handler," because the more you did (or the busier your machine), the more likely you would be to run into a thread conflict causing a crash.

      I looked at the thread context and APC stuff, but both those topics are over my head, I'm afraid. All I was thinking about was to simply "re-throw" the event signals (by "posting" a WM_USER_KILL message from the message handling thread appropriate for the signal to "handle") instead of calling back into Perl. What I wonder about, and perhaps you can help with is this side of the win32.c signal code:

      case WM_USER_KILL: { /* We use WM_USER_KILL to fake kill() with other signals */ int sig = (int)wParam; if (do_raise(aTHX_ sig)) sig_terminate(aTHX_ sig); return 1; }

      Do you know of any issues with that side of the code? I was thinking it would trigger the $SIG{ 'HUP' } in the same thread as the perl interpreter. If that's true, then what I wanted to do might have a chance of working. Anyway, thanks for the feedback.

        The problem with just "sending" a message to the main thread is, what if the main thread is in a blocking syscall, directly from Perl lang or through some XS-ed C library? How do you ensure the responsiveness of the fake-signal if the main thread is blocked in the kernel?

        I did once take a C debugger, when the console event hit the 2nd thread, I paused the main thread, let the 2nd thread run to completion and shut down Perl interp struct, and I dont remember (and cant research it ATM, normally I would research it for you but im not at my devel machine this post is from memory), but I think the 2nd thread ended the 2nd thread, I then ran the 1st main thread, now with an almost uninitialized interp struct, and it very quickly called C's exit() and that was it. I also fixed a race condition maybe a year ago between sending a kill 9 from a parent fork thread/fake process to the child fork thread/fake process, where the child thread was still initializing itself in user mode kernel32/ntdll, before calling any process specific code, and when killing the child thread, it would leave the DLL loader lock, locked, by the now gone 2nd thread, when the perl interp trys to exit, the DLL loader from the main thread tried to aquire the DLL loader lock and hung forever.

        I've also dispatched events from multi threaded pure C code, using Perl's win32 fake signals dispatch code by setting a 1 in some array in the perl interp struct which corresponds to one of the 2 dozen signals names on Win32 perl, then setting a global flag that there is a pending signal for the interp to dispatch to the perl lang level, and on PERL_ASYNC_CHECK() the fake signal was dispatched, which eventually called a Perl lang sig handler which then called XS code which pulled events off the pure C's event queue. I think I used signal NUM05.

        You should ask on the p5p mailing list. You will get more C/interp internal knowledgeable eyes than on PerlMonks. The p5p IRC room has less eyes and less Win32 eyes. Since there arent very many p5p win32 guys. You probably want an answer from steve hay, tony cook, or jan dubois. None of them are active at PM. Also use git blame, for example http://perl5.git.perl.org/perl.git/blame/HEAD:/win32/win32.c and look at the evolution of the code you are wondering about.
Re^3: The implementation of SIGHUP in Win32 Perl
by BrowserUk (Patriarch) on Sep 02, 2013 at 03:42 UTC
    The other problem is that since Windows Vista, the OS (not Perl) will terminate the process upon the return from the console control handler—regardless of the return value (see note 2, below, and from what I've read elsewhere, this process will eventually be terminated by the OS even if the control handler never returns, see note 3, below). This could abort any type of recovery a Perl signal handler might attempt.

    I think you've answered your own inquiry. There is nothing the perl process can do about being forcibly terminated.

    And I sincerely doubt there is any merit in trying.

    Windows doesn't do sighup; nor have I felt any need for it. As a Windows user, I know that forcing a program to terminate this way is likely to leave things in an incomplete state; and I use the option as a last resort with that in mind.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1051877]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (5)
As of 2024-04-16 10:20 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found