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

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

I've seen previous questions about how to cleanly exist a script with threads by first making sure the threads exit, but I have a different situation - at least I think I do...

My main script starts a thread that looks for socket connections/disconnections and opens/closes them accordingly and so will never exit. It shares this info with the main program via a shared variable containing the open file numbers. My test program works like a champ and when I exit the main code via a ^C it simply exists.

However, when I take the same code and include it in my real code it works fine there too but when I close that program also with a ^C, I get the infamous "A thread exited while 2 other threads were still running."

This leads to a couple of questions, the most obvious being if I have a thread that never exists, what is the cleanest way to shut down the main script? I suppose I could track down the pid of the thread and kill it from the main script but that feels like it may be masking a more serious problem. I also thought I might be able to set some shared variable in the main line that the thread sees to tell it to shut down but that doesn't feel right either. The thread is also sitting in a can_read() call and so wouldn't see the shared variable anyways.

I'm also puzzled by my simple test script didn't have this problem.

thoughts?

-mark

  • Comment on Exiting a script with an 'infinitely looping' thread

Replies are listed 'Best First'.
Re: Exiting a script with an 'infinitely looping' thread
by BrowserUk (Patriarch) on Nov 24, 2008 at 14:17 UTC
    if I have a thread that never exists, what is the cleanest way to shut down the main script?

    Three ways

    1. detach the never ending thread.

      When the main thread terminates, you won't get the warning.

    2. use a signal handler (to catch ^C) in the main thread to set a shared flag that the never ending thread monitors regularly, and then join the never ending thread from the signal handler before terminating.

      The disadvantage of this is that you need to ensure that and reads or attaches in your never-ending thread timeout occasionally, otherwise you will never check the shared flag until you receive something.

      That means using non-blocking IO for sockets.

    3. Faff around with per-thread signal handlers and use a signal handler in the main thread to send a kill signal to the never ending thread.

      More complicated and no real advantage over using a shared flag, as the thread handler won't respond to the kill signal if it is processing a blocking opcode like read or recv.

    Which is more appropriate to your situation will depend upon how your app is currently coded?


    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.
      Ahhh... I do trap the ^C in my real script but had been lazy in my test script. As soon as I put the ^C in my test script it too complained!

      re detach thread - not sure how to do this but it sounds promising.

      re share flag - yes, I did realize that doing this I'd need to periodically wake up and check the flag, the only real negative here is that it would delay the time it takes for my script to shut down to as much as the 'wake up ' time, something I'd prefer to avoid.

      -mark

        e detach thread - not sure how to do this but it sounds promising.

        See the pod for threads, but essentially where you currently do:

        my $immortal = threads->create( ...);

        doing

        threads->create( ...)->detach;

        is pretty much all there is to it. As the pod says:

        $thr->detach()

        Makes the thread unjoinable, and causes any eventual return value to be discarded. When the program exits, any detached threads that are still running are silently terminated.


        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.

        A quick note: Detaching the thread doesn't cause it to exit cleanly, just silently.

        #!/usr/bin/perl use threads; DESTROY { print "Destroyed\n" } sub test { my $x = bless({}); sleep() while 1; } $SIG{INT} = sub { exit }; my $thread = threads->create('test')->detach(); print("Press Ctrl-C to exit\n"); sleep();

        If the thread exited cleanly, the above prints Destroyed. It doesn't.

Re: Exiting a script with an 'infinitely looping' thread
by ikegami (Patriarch) on Nov 24, 2008 at 14:05 UTC

    Ctrl-C doesn't exit cleanly, so despite the new warning, you're no worse off than you were before.

    $ perl -le'DESTROY { print "Destroyed" } my $x = bless {}; print "slee +ping"; sleep 5;' sleeping Destroyed $ perl -le'DESTROY { print "Destroyed" } my $x = bless {}; print "slee +ping"; sleep 5;' sleeping ^C $

    What you could do is hook into $SIG{TERM}, $SIG{INT}, etc. The signal handler would send some kind of request to the threads asking them to exit, then join with the threads.

    The thread is also sitting in a can_read() call and so wouldn't see the shared variable anyways.

    Not true. You could set a timeout on the can_read so you can poll a shared variable. Another option would be to create a communication pipe or socket and add the it to the select object.