in reply to Perl5.8.4 , threads - Killing the "async" kid

How can I ensure the main thread shuts down other threads before shutting itself down with exit.

If your only goal is to suppress the error messages, the simplest way is to use POSIX::_exit() from your main thread once it has done whatever clean-up and finalisations you require.

I don't advocate that as a general solution, but if there is no clean-up to be done in the threads, and you cannot move to a more modern version of Perl then it is the simplest.

If you are prepared to do a little more work, then use a threads::shared variable to tell your threads to finish. The complication comes because the simplicity of:

my $thread300 = async { while( sleep 300 ) { ... } };

Has to become something like:

my $timeToExit : shared = 0; sub shutdown{ print logTime." INT|ABRT|QUIT|TERM received....shutting down servic +e.\n"; $timeToExit = 1; ## Tell the threads to quit sleep 1; ## give them time to react close(LOOK_FOR_LEASE) if fileno(LOOK_FOR_LEASE); close(OUTPUT) if fileno(OUTPUT); close(ERROR) if fileno(ERROR); threads->exit(); } ... my $thread300 = async { my $sleepTime = 300; while( !$timeToExit ) { sleep 1 and next while --$sleepTime; $sleepTime = 300; ... } };

Now you can set $timeToExit = 1; in your main thread signal handler and know that the thread will terminate within one second. Not so complicated.

With later versions of threads there are the "signal handling" options, but in reality, they are no better than a simple shared variable. You still have to poll because the 'threaded-signals' (KILL etc.) don't interrupt anything.

However--and this is my recommendation--based on the pseudo-code you've posted which suggests the threads in question have no clean up to perform, nor any values to return, I would suggest upgrading to (the binary compatible) 5.8.9 and detaching the threads.

The warnings output in earlier versions about "still running detached thread" were recognised to be a design error, and were removed:

c:\test> \perl32\bin\perl -Mthreads -e"print $]; async{ sleep 1000 }-> +detach; sleep 5" 5.008009 c:\test>

What that snippet shows, is that with 5.8.9, when the main thread's 5 second sleep times out, the detached thread is still running (but sleeping) and the program ends without any warning message. Detached threads are, by definition, fire-and-forget, so the earlier warnings were just wrong.

If you really can't upgrade, using POSIX::_exit()--once you've cleaned up everything that needs cleaning up--is a hackish way of achieving your stated goal.

A final thought is: these are only informational messages. You could simple document them as being "known and correct" and otherwise just ignore them!


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.
RIP an inspiration; A true Folk's Guy

Replies are listed 'Best First'.
Re^2: Perl5.8.4 , threads - Killing the "async" kid
by MonkeyMonk (Sexton) on Oct 12, 2010 at 11:20 UTC
    Firstly, thanks everyone and BrowserUK for the responses. Below are my responses to select parts of the follow-ups.

    1.BrowserUK wrote:
    However--and this is my recommendation--based on the pseudo-code you've posted which suggests the threads in question have no clean up to perform, nor any values to return, I would suggest upgrading to (the binary compatible) 5.8.9 and detaching the threads.

    Well, I am to blame here. The pseudo-code, as expected, did not reveal much. This code below is declared before any thread is defined at the very top of the program. In effect, what I have done is to declare redirects for STDOUT and STDERR to a log file. And this is shared by all threads.

    # Opening log file. # Redirecting OUTPUT and ERR to log file for all threads open OUTPUT, '>>', "/usr/local/xxx/log/lease.log" or die $!; open ERROR, '>>', "/usr/local/xxx/log/lease.log" or die $!; STDOUT->fdopen( \*OUTPUT, 'w' ) or die $!; STDERR->fdopen( \*ERROR, 'w' ) or die $!;

    This simplifies my logging process but it, in turn creates problems when I shut the daemon down. Please keep in mind the the signal handler itself is shared by all threads which means each process has it's signal handlers defined. So what I needed to write in the signal handler was a clean closing of threads and file-handles, independent of the active thread. I thought about something similar to $timetoExit before posting here but did not know how to get beyond that point. The sleep 1 and next while --$sleepTime; was what I was looking for. Saw it. Loved it. Tested it with a mild tweak. And no more error messages.

    2.BrowserUK wrote:
    Detached threads are, by definition, fire-and-forget, so the earlier warnings were just wrong.

    I did try detaching the thread. But the error message kept coming. And I was also worried about the open file handles in the async thread. I kept imagining the main thread receiving a SIGTERM while the sync thread was trying to write to the file-handle. I believe this is a possibility.

      Please keep in mind the the signal handler itself is shared by all threads which means each process has it's signal handlers defined.

      Hm. Does that mean that you are mixing threads and processes? Or are you just conflating the two terms? I'm going to assume the latter.

      In a threaded process, if you install a signal handler using the 'normal mechanism' of %SIG, then that signal handler will only be called on the main thread. I assume--but haven't tested--this is the same for sigtrap.

      I did try detaching the thread. But the error message kept coming.

      It will unless you upgrade to a later version of perl. The warning about detached threads is no longer produced for 5.8.9. It may have been removed earlier, I don't recall, but if you were to upgrade, you might as well at least go as far as 5.8.9 and benefit from other fixes that came from that release also.

      And I was also worried about the open file handles in the async thread. I kept imagining the main thread receiving a SIGTERM while the sync thread was trying to write to the file-handle. I believe this is a possibility.

      If you are sharing these file handles between threads, then you should be synchronising access to them anyway. Ie. declare a shared variable for use as a mutex, take a lock() on them before printing to them and release it afterward.

      Something like:

      my $semSTDOUT :shared; sub tprint{ lock $semSTDOUT; print @_; }

      That's a very simplistic version, but demonstrates the point.

      For the 'fire & forget' method, the signal handler would acquire a lock on that semaphore before exiting. That would ensure that none of your other threads could be in the process of writing to the handles when they are forcibly killed.


      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.
        Hi again,

        You wrote:
        In a threaded process, if you install a signal handler using the 'normal mechanism' of %SIG, then that signal handler will only be called on the main thread. I assume--but haven't tested--this is the same for sigtrap.
        Sorry about the unclear sentence. What I meant was this. In the code, the signal handler (sigtrap) , is defined at the very top before the thread definitions. This means, like all other functions and variables it duplicated for the threads created subsequently and each thread gets its own signal handler.
        In the last 24 hours , I have had at least 3 instances where this appears as-is in the log file:

        INT|ABRT|QUIT|TERM received....shutting down service. INT|ABRT|QUIT|TERM received....shutting down service.

        2 of those 3 instances was when the main thread was sent a

        kill -15
        and 1 was a reboot.

        From this I assumed that each thread gets its own signal handler.
        Or is that the signals are chained somehow and when the main thread is sent a TERM, the child gets them too?

        Thanks for the tip on synchronising access to file handles. That is something I have not done at all. Maybe that is the reason why on some other boxes, the threads die a premature death after 3 minutes or so as opposed to the planned 24hr reboot cycle.

        If you are sharing these file handles between threads, then you should be synchronising access to them anyway. Ie. declare a shared variable for use as a mutex, take a lock() on them before printing to them and release it afterward.

        Something like this where the logging file is declared before the threads are started?

        #!/usr/bin/perl use threads; use threads::shared; my $loglock :shared ; open(LOG, ">/usr/local/lease.log"); sub ilog{ my $text = shift; lock $loglock; print LOG $text; } ## Start defining threads from here on

        From your post, this is what I gathered. This should work when multiple threads write to the log file and when one has a lock on the variable, the others cannot write until the lock goes out of scope.