http://qs321.pair.com?node_id=553319

JFarr has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,
I have a irriating thread(s) problem.

We have the following code:
Everything looks fine but......

use threads; use threads : shrared; foreach $host (@list_of_nodes) #we are connecting other PCs { threads->new(\&somefunction, $host); } (threads->list)[0]->join while threads->list;
The "somefunction" has nothing special in it, just some print statements and calls to show host name, ip address, etc.....

I have a small handle on Perl threads, I'm a C++ developer, so Perl causes me no end of trouble :)

The problem that we keep encountering is that randomly the threads hang or never return.

I've looked through my Perl books, but there is not much documentation and the web is not much more help.

What can be done to kill the stray thead and warn the user that a thread has gone on to la-la-land. Is there a way to terminate this thread?

Thanks

2006-06-02 Retitled by jeffa, as per Monastery guidelines
Original title: 'thread help please'

Replies are listed 'Best First'.
Re: How to terminate threads that hang or never return?
by zentara (Archbishop) on Jun 02, 2006 at 16:21 UTC
    The problem that we keep encountering is that randomly the threads hang or never return.

    For a thread to be joinable, it must "return" from it's code block with a return statement, OR reach the end of the code block.

    Since you didn't show how you wrote &somefunction, I can only generalize.

    sub somefunction{ # somehow incorporate an alarm or a loop # to run a "return" or "goto END" ....... if ( $shared_flag ==1 ){ return } #or if( $shared_flag == 1 ){ goto END } ...... ...... END: }
    or maybe alarm?
    sub somefunction{ local $SIG{ALRM} = sub { goto END }; alarm 10; # wait 10 seconds before interrupting while (1){sleep} END: }
    You can also try to detach the thread, and have it set a shared variable when it successfully completes. Then loop through the shared flags at the end of your main thread, and kill hanging threads.

    I'm not really a human, but I play one on earth. flash japh
Re: How to terminate threads that hang or never return?
by jdhedden (Deacon) on Jun 02, 2006 at 18:16 UTC
    What can be done to kill the stray thead and warn the user that a thread has gone on to la-la-land? Is there a way to terminate this thread?
    Threads cannot be cancelled directly. However, if you install threads v1.27 or higher (obtain the latest version from CPAN), you can send a signal to a thread so that it can then terminate itself (say by calling die). Be sure to read the caveats in the Thread Signalling section of the POD.

    Here is a working example that should give you an idea of what you need to do:

    #!/usr/bin/perl use strict; use warnings; no warnings 'threads'; use threads 1.27; use threads::shared; use Thread::Queue; ### Global Variables ### # Flag to inform all threads that application is terminating my $TERM :shared = 0; # Used for timing out threads my $TIMEOUT = 10; my $TIMER = Thread::Queue->new(); # Used by threads to signal that they are done my $DONE = Thread::Queue->new(); ### Signal Handling ### # Gracefully terminate application on ^C or 'kill' $SIG{'INT'} = $SIG{'TERM'} = sub { print(">>> Terminating <<<\n"); $TERM = 1; }; # This signal handler is called inside threads # that get cancelled by the timer thread $SIG{'KILL'} = sub { my $tid = threads->tid(); # Tell user we've been terminated printf(" %3d <- Killed\n", $tid); # Tell main thread that we're done $DONE->enqueue($tid); # Commit hari-kari die("Thread $tid killed\n"); }; ### Main Processing Section ### MAIN: { # Number of threads my $max_threads = (@ARGV) ? shift : 10; # Start timer thread threads->create('timer')->detach(); # Do lots of work while (! $TERM) { # Start max threads for (my $needed = $max_threads - threads->list(); $needed && ! $TERM; $needed--) { # New thread my $tid = threads->new(\&worker)->tid(); # Add to timer queue $TIMER->enqueue($tid, $TIMEOUT); } # Join with threads as they finish while (! monitor_threads()) { sleep(1); } } # Wait for threads to finish while ((threads->list() > 0) && $TIMEOUT--) { sleep(1); monitor_threads(); } # Kill and detach remaining threads foreach my $thr (threads->list()) { $thr->kill('KILL')->detach(); } sleep(1); # A moment of silence } print("Done\n"); exit(0); ### Thread Entry Point Subroutines ### # A worker thread sub worker { # My thread ID my $tid = threads->tid(); printf("Working -> %3d\n", $tid); # Do some work while monitoring $TERM my $sleep = 5 + int(rand(10)); while (($sleep > 0) && ! $TERM) { $sleep -= sleep($sleep); } # Tell user we're done printf(" %3d <- Finished\n", $tid); # Tell main thread that we're done $DONE->enqueue($tid); return; } # The timer thread that monitors other threads for timeout sub timer { my %timers; # Contains thread IDs and their remaining time # Loop until told to quit while (! $TERM) { # Check for new threads while (my $tid = $TIMER->dequeue_nb()) { # Decrement its timeout $timers{$tid} = $TIMER->dequeue() - 1; } sleep(1); # Check for timed out threads while (my ($tid, $timeout) = each(%timers)) { if ($timeout) { $timers{$tid}--; } else { # Cancel timed out threads, if still running if (my $thr = threads->object($tid)) { $thr->kill('KILL'); } delete($timers{$tid}); } } } } ### Helper Subroutines ### # Join to threads that have finished processing # Returns the number of threads joined sub monitor_threads { my $count = 0; while (my $tid = $DONE->dequeue_nb()) { threads->object($tid)->join(); $count++; } return ($count); }

    Remember: There's always one more bug.