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


in reply to Re: Threads and signals in Windows
in thread Threads and signals in Windows

BrowserUk, thank you! I appreciate your straightforward answers.

Sorry n'all, but that is garbage! Whilst you may have seen some symptoms like these and have attributed them to the use of signals; I'll bet that you cannot post code to demonstrate it.
This is a case of, as I believe, an inappropriate usage of signals, and the related symptoms of system degeneration. It is from https://rt.cpan.org/Public/Bug/Display.html?id=66437
I am using:
  • Strawberry-perl-5.12.2.0
  • Perl 5, version 12, subversion 2 (v5.12.2) built for MSWin32-x86-multi-thread
  • Windows 7 Home Premium with Service Pack 1

I have used version 1.11 and 1.12 of Test::TCP.

Problem: The test in Test::TCP is blocking. Get the system in state, that it must be restarted. It is sometimes not possible to kill the blocked processes. I even get errors like “Can't spawn "cmd.exe"” when using the system call. It indicates a degeneration of the perl interpreter.

One example of stochastic behavior (from https://rt.cpan.org/Public/Bug/Display.html?id=66016) is running this batch file:
@echo off set count=0 :loop set /a count=%count%+1 echo Count %count% @echo on perl -e "if ($pid=fork){Win32::Sleep(0); kill(9, $pid); Win32::Sleep(0 +)}else {sleep 1000}” if errorlevel 1 goto exit @echo off goto loop :exit ECHO.%ERRORLEVEL%

Examples of results I got was:

This was done using Strawberry-perl-5.12.2.0. The patch http://perl5.git.perl.org/perl.git/commitdiff/82e24582 removed most of the problem but not all. It was also found that version of the Windows operating system had different behavior.

As for the rest: Unix Signals are (one of several) "Unfixable designs". And most all the caveats and warnings you quoted from the MS docs for TerminateThread() are equally applicable to causing any piece of code to be interrupted without resumption using a longjump. Ie. Signals.

As an IPC mechanism, that only conveys 1 bit of information and is edge triggered -- ie. missable unless you happen to be ready for them -- signals are a piss poor substitute for proper IPC mechanisms.

And the need to use them to abandon in-progress processing is a sign of badly designed code. Describe the use case for using them and I'll outline a better way of tackling the problem. (Though it may or may not be currently available from Perl.)

I interpret your standpoint as:

Could you accept (or even recommend) the addition of something like this:

It is strongly advice against using signals for Inter Process Communication. This is particular important in Perl code intended to be portable.

to the Perl documentation? The addition could be done to the function kill in the Perl Language reference.

Replies are listed 'Best First'.
Re^3: Threads and signals in Windows
by BrowserUk (Patriarch) on Mar 09, 2013 at 16:37 UTC
    One example of stochastic behavior...

    You reinvented a fork bomb. Except on windows, fork actually spawns a thread; so it might better be termed a thread-bomb in this case.

    Try feeding this:  :(){ :|:& };: to bash on a *nix system and see how deterministic the DoS is.

    You are also asking the child 'process' to kill its parent 'process'; but on windows they are actually just threads within the same process. Ie. You are attempting to kill your own process. In addition, you are also thread-bombing the system from within that same process. Is there any wonder that the process may not respond to the request in a predictable manner?

    And you are using a random delay -- sleep 0 equates to "relinquish the rest of the current timeslice" which is an unknowable quantum of time, that could range from 0 microseconds -- if the timeslice was about to end anyway; or if there are no other processes in the system immediately eligible to run -- to N * q where N is the number of other processes and threads in the system that are eligible to run; and q is the scheduler quantum which can vary from ~5 to ~180 microseconds depending upon: a) the version of Windows; b) how that version is configured (workstation or server; foreground or background priority); c) a whole bunch of other factors.

    In other words; you have programmed a random delay into your program as surely as if you had written Win32::Sleep( rand()*100 ).

    Of course the behaviour is "stochastic"; That's how you programmed it to be!

    Could you accept (or even recommend) the addition of something like this:

    Neither!

    I use kill (on windows) to good effect all the time. The difference is that I have spent enough time to understand the limitations of the emulation on my platform. I understand that they give me a fairly crude interface to the native Console control handler functionality from Perl; and that -- used carefully -- can provide functionality that I might otherwise have to dip into Inline::C or XS to gain access to.

    What I would say; and have said many times; is do not attempt to use fork & the windows signals emulation to try to port *nix idioms to windows; because you will be sadly let down.

    The basic problem here is the entire attempt to make Windows look like or work like some variant of *nix. It is simply far easier to write two scripts -- one for each platform -- than to try and construct and maintain one that will operate correctly on both platforms.


    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.

      BrowserUk, thank you!

      What I would say; and have said many times; is do not attempt to use fork & the windows signals emulation to try to port *nix idioms to windows; because you will be sadly let down.

      Based on this, I will propose an addition to the documentation of function fork (and perhaps also kill) in the Perl Language reference.

      You reinvented ... a thread-bomb.

      And you are using a random delay -- sleep 0 equates to "relinquish the rest of the current timeslice" which is an unknowable quantum of time, that could range from 0 microseconds

      My “thread-bomb” resulted in a patch. The patch http://perl5.git.perl.org/perl.git/commitdiff/82e24582 adds to t/op/fork.t:

      +system $^X, "-e", "if (\$pid=fork){sleep 1;kill(9, \$pid)} else {sle +ep 5}";

      Is this also a thread-bomb, or is it OK with non zero sleep?

      The patch also changes win32/win32.c:

      --- a/win32/win32.c +++ b/win32/win32.c @@ -1282,6 +1282,13 @@ win32_kill(int pid, int sig) case 9: /* kill -9 style un-graceful exit */ if (TerminateThread(hProcess, sig)) { + /* Allow the scheduler to finish cleaning up the +other thread. + * Otherwise, if we ExitProcess() before another +context switch + * happens we will end up with a process exit cod +e of "sig" instead + * of our own exit status. + * See also: https://rt.cpan.org/Ticket/Display.h +tml?id=66016#txn-908976 + */ + Sleep(0); remove_dead_pseudo_process(child); return 0; }

      Is it OK with “Sleep(0)”, or must it be a non zero parameter value? Can the non zero time value cause that we still sometimes get 9 as an erroneous exit value? What is the smallest non-zero value that can be used?

        Is it OK with “Sleep(0)”, or must it be a non zero parameter value?

        The problem here is the assumption that yielding the processor will ensure that the terminated thread has had time to fully clean up before this thread will get another timeslice and exit. If the system is busy, many other threads in the system are eligible to run -- especially if they are higher priority -- then the callee of TerminateThread() might easily get several timeslices before the thread actually ends.

        The correct (or maybe just better) thing to do, would be to pass a known exit code value on the TerminateThread() call and then loop checking the thread exit code until it returns that value rather than STILL_ACTIVE. Eg. Something like this:

        TerminateThread( hProcess, 123456789 ); Sleep( 0 ); GetExitCodeThread( hProcess, &exitCode ); while( exitCode != 123456789 ) { Sleep(0); GetExitCodeThread( hProcess, &exitCode ); }

        Once GetExitCodeThread() returns the known value, you should be pretty certain that the thread has actually terminated.


        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.