http://www.perlmonks.org?node_id=1052197


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

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.
  • Comment on Re^3: The implementation of SIGHUP in Win32 Perl

Replies are listed 'Best First'.
Re^4: The implementation of SIGHUP in Win32 Perl
by klaten (Novice) on Sep 04, 2013 at 09:22 UTC
    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.

        Wow, that's *seriously* deep stuff, you've attempted! I don't think I want to go that deeply into the forest. The quote, "Do not meddle in the affairs of dragons, for you are crunchy and taste good with ketchup" would seem to apply to me regarding the things you tried. Your paragraph beginning with "I did once take a C debugger, . . .," made me smile because it sounded like the beginning of one of the stories an Arthurian knight might have spun. Powerful stuff, nonetheless.

        The next paragraph about the dispatch of events from perked my interest because I can see how that could work to solve the issues you raised in the first paragraph. I'm going to return to that first paragraph because I think it's the most interesting of all.

        Thank you for the advice about the p5p mailing list. I've come to the conclusion that I need to craft my own console control handler in XS and install it on top of Perl's, ensure that it works to my satisfaction, and then I'll be able to make a more acceptable contribution. It sounds like they may be a great source of wisdom in that endeavor. And tracking the evolution of win32.c actually sounds like fun! So I know I'll do that. Thank you, again.

        Now back to that first paragraph (written with a smile)! You wrote:

        How do you ensure the responsiveness of the fake-signal if the main thread is blocked in the kernel?

        One possible answer might be, "you don't." Your other question holds the key to that idea.

        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?

        Isn't Perl5 evolving in that direction, anyway? Aren't those restrictions similar to the limitations of "safe signals"? Are there many things that can *only* be done via blocking syscalls in the main process? It reminds me of the classic "doctor-patient joke:"

        Doctor: Please tell me about your problem.
        Patient: It hurts when I do this.
        Doctor: So don't do that!

        In Perl5 we have many tools to allow the use of multiple processes (in Windows, Linux, and the other platforms as well) to handle the types of things most people would want to use blocking syscalls for anyway. With simple IPC techniques like piped opens and "safe signals" why bother with complex, flawed, and potentially crash prone things like full POSIX signals anyway? I can see that you have the skills and moxie to attack that problem, but I can't help but wonder whether or not the better way is simply to accept Perl5 as a wonderful single-threaded C program (perhaps the greatest, ever) and maybe leave those more complex issues to the Perl6 people.