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

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

Hello

Building from this node, I did some more experimentation with threads (please refer to that node for details). I am now trying to find another way to terminate nicely a thread.

The idea was: If I can handle $httpd out of the thread where it is accepting, maybe I could simply $httpd->close instead of timeout-ing the accept call. But... can I pass an $httpd object outside of its thread using a queue?

Now, in the Camel book, it is said about Thread::Queue:

The standard Thread::Queue module provides a way to pass objects between threads... you get to pass around full scalars, including references and blessed objects!

but it is, maybe, a bit outdated. In the documentation of the module makes no mention about passing objects in queue.

Uhmmm... time to experiment! I modified my code to create an "httpd queue" and the httpd sub to enqueue the $httpd object in it. No luck:

bronto@brabham:~/tmp$ ./420587.pl thread failed to start: Invalid value for shared scalar at /usr/share/ +perl/5.8/Thread/Queue.pm line 90.

I am going to have no luck with this approach, am I?

Ciao!
--bronto


In theory, there is no difference between theory and practice. In practice, there is.

Replies are listed 'Best First'.
Re: Thread::Queue and objects
by BrowserUk (Patriarch) on Jan 14, 2005 at 12:57 UTC
    I am going to have no luck with this approach, am I?

    The best answer I can give is that I haven't succeeded in doing this--and I have tried quite hard.

    I think that the documentation for Thread::Queue (and much of the other threads-related documentation) still has some vestigies of the original 5.6.x pthreads information lingering.

    I did read somewhere (not on this site I think), that it is possible to re-bless an object into a different thread. Apart from that threads::shared will not allow you to share blessed references, theoretically you ought to be able to have a copy of the class loaded in two threads and by re-blessing an object reference created in one, into the other, have both threads access copies of the same object.

    I see all manner of problems with synchronisation and locking inherent in this. I did make a half-hearted attemt to verify this was possible, but I got nowhere. Again, it could be that what I read was only applicable to pthreads not iThreads.

    What is the problem you are encountering with the current method?

    If you do find another way, please post it here. Thanks.


    Examine what is said, not who speaks.
    Silence betokens consent.
    Love the truth but pardon error.
      What is the problem you are encountering with the current method?

      Suppose you run it on a very busy machine: even if short, the amount of time that passes from a timeouted accept and the new accept call could be long enough that a client tries to connect in between and finds nothing accepting on the socket. Not good...

      --bronto


      In theory, there is no difference between theory and practice. In practice, there is.

        I see that there is a notional window there, but I think it only exists conceptually, if you only consider a single running thread at a time.

        On a single cpu machine, when any given thread is in that window between the accept timing out and the new accept being issued, every other thread, whilst dormant, still has an open accept on the same port (or is awaiting a timeslice to finish processing before re-issuing it's accept).

        If you have multiple threads running, the window in which all threads would be either processing data, or in between the timeout and the re-issue of the accept is vanishingly small.

        On a multi-cpu machine, even if you only have one more thread than cpus, the possibility of none of your threads having an accept in progress is pretty small.

        If you have double the number of threads as cpus, it becomes statistically very improbably.

        You have to realise, that even if a given thread has an accept in progress when the connect arrives, the odds are very high that it will not currently be in a timeslice. So most times, there will be a small delay between the connect occuring and the thread that accepts it getting a timeslice to complete the accept. This is true even of a non-threaded socket program running on a multi-tasking OS. So long as there is at least one thread that has an open accept, the tcp/ip stack / driver will accept the connect and 'hold' it until an appropriate thread or process gets a timeslice.

        The more threads you have issuing the accept on the appropriate port, the less likely that they will all be out of the accept wait state at the same time. It is not zero, but with even 5 threads, it is really very small.

        I'm not overly familiar with the detailed nitty gritty of tcp, but even in a traditional accept'n'fork model, there is a similar window between the accept occuring and it being re-issued. Even with a "dedicated" accept process, that process is still subject to being swapped at the schedulers convenience. It is only notionally "always running".

        I would say that that window is larger in the forking model, as even on COW optimised OSs, the process still has to be cloned, and then the new process will get the next timeslice. So the parent will have to wait until the new process finishes it's timeslice, plus any other processes that get schedule between times, before it will get a timeslice in which to go back and re-issue the accept.

        This doesn't seem to be an issue there, and I don't think the much rarer possibility, will be an issue in a threaded model.


        Examine what is said, not who speaks.
        Silence betokens consent.
        Love the truth but pardon error.
Re: Thread::Queue and objects
by kazin (Novice) on Jan 14, 2005 at 16:04 UTC

      Thanks for the suggestion, which is a precious one since http://search.cpan.org seems to have bad bad problems currently and I couldn't dig it to find it myself.

      Unfortunately, that doesn't work...: I modified the program to use a T::Q::A to share the object and...

      bronto@brabham:~/tmp$ ./420587.pl --peerport 1081 thread failed to start: Can't store GLOB items at ../../lib/Storable.p +m (autosplit into ../../lib/auto/Storable/_freeze.al) line 287, at /u +sr/local/share/perl/5.8.4/Thread/Queue/Any.pm line 30

      :-(

      Ciao!
      --bronto


      In theory, there is no difference between theory and practice. In practice, there is.

      Form my experience, Therad::Queue::Any never really did work with threads. It was designed for use with Forks.

      Besides which, all those modules that Storable Freeze/Thaw are slow and somewhat dubious when it comes to sharing either process global entities like filehandles, sockets,pieps etc., or blessed objects. If it were that easy to do, threads::shared would probably allow it.


      Examine what is said, not who speaks.
      Silence betokens consent.
      Love the truth but pardon error.
Re: Thread::Queue and objects
by zentara (Archbishop) on Jan 14, 2005 at 14:53 UTC
    I am now trying to find another way to terminate nicely a thread.

    Your code is kind of complicated for me to get my mind around( without work :-) ). All I can think of is to remind you, that to terminate a thread, the execution pointer in the thread must be sent to the end of it's code block. I resort to sending a shared variable to the thread, which tells it to "goto END", where the END: label is placed at the end of the thread code block.


    I'm not really a human, but I play one on earth. flash japh

      Ok, but... how would you make goto END a thread that is stuck in blocking call (a socket accept in this case)?

      Ciao!
      --bronto


      In theory, there is no difference between theory and practice. In practice, there is.
        Caveat: I'm not sure exactly what code you are referring to, and I havn't tested this. But I'm guessing you mean the following worker thread code block, which I would try to modify like the following. If you are blocking somewhere on a socket, maybe setup a timeout alarm on the connection, or use sysread or one of the other tricks to prevent blocking. All you really need to do, is somehow keep the thread looping, and periodically checking for $die.
        #in your main share $die; $die = 0; .... #set $die =1 when you want to exit or kill the thread ...... #worker code block sub httpd { LISTEN: { my $client = $httpd->accept ; goto END if $die; redo LISTEN unless defined $client ; my $request = $client->get_request ; unless ($request->method eq 'POST' and $request->url->path eq '/message') { $client->send_error(RC_FORBIDDEN) ; $client->close ; goto END if $die; redo LISTEN ; } my $q = CGI->new($request->content) ; my ($nick,$message) = map $q->param($_),qw(nick message) ; print STDERR "Resetting text box state\n" if DEBUG ; $tbox->configure(-state => 'normal') ; $tbox->insert('end',qq($nick says: $message\n)) ; print STDERR "Disabling text box\n" if DEBUG ; $tbox->configure(-state => 'disabled') ; $client->send_status_line ; $client->close ; goto END if $die; redo LISTEN ; } END: }

        I'm not really a human, but I play one on earth. flash japh