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

Help I'm stuck!

I need to talk to a piece of kit that uses UDP to accept binary commands and send back replies. One UDP packet sent to the unit will result in one reply packet sent back to the IP/port that it came from.

I'm trying to create a set of packages/objects to communicate with the unit. The main object I've created is the session object. When I create an instance of session I want it to send a login command and receive the reply and then have a thread that will receive and process each reply as it comes until the session is destroyed.

My problem is sharing the socket connection between the session object (to send commands) and the thread it creates (to recv replies).

I've tried two approaches which both fail:

I've run out of ideas of how to get this to work. Anybody know of another way to do this? I'm using ActiveState 5.8 in WinXP

Thanks!

Replies are listed 'Best First'.
Re: Threaded UDP Communication
by acid06 (Friar) on Feb 17, 2006 at 00:58 UTC
    If you create the socket on the "main" thread and then when creating the "child" threads you pass the socket object as parameter it works as expected as sockets survive thread cloning.
    You don't even need shared variables for this to work:
    use warnings; use strict; use threads; use IO::Socket; my $socket = IO::Socket::INET->new(); # pass your parameters threads->create(\&do_stuff, $socket); sleep(0xffffffff); # ;-) sub do_stuff { my ($socket) = @_; # play happily ever after with $socket in your new thread }
    Note that this is code is untested, but I'm sure it works because I've got production code like this at work. Actually, it's slightly different, the thread creation doesn't use the "standard parameter passing procedure", so although I can't guarantee the above will work, I'm completely sure that this works:
    threads->create(sub { do_stuff($socket) });
    But I guess it should work both ways.


    acid06
    perl -e "print pack('h*', 16369646), scalar reverse $="

      Thank you, this seems to work perfectly. I guess I was trying to be too clever!

      However, I've now come across another problem. I need the call to socket->recv to be non-blocking. I see that there is a flags parameter and I'm wondering whether I need to set a flag to make the recv call return immediately but I can't find any documentation for the flags, all I can find it a very old post 129590 which doesn't really help a lot.

      I've added my code so you can see what I'm trying to do but it's in no way finish yet hence all the print's to help me find out whats going on.

        I've just found a solution to my own problem... I'm quite new to perl and I've just discovered IO:Select after looking at 133389, which solves all my problems!

        Thanks!

      If you create the socket on the "main" thread and then when creating the "child" threads you pass the socket object as parameter it works as expected as sockets survive thread cloning. You don't even need shared variables for this to work

      As you point out, you can only get away with this if the filehandle/socket exists prior to creating the thread that will subsequently use it. Which means that everything created in the spawning thread gets cloned into the new thread. Then the next time you spawn a thread, everything that has been created since spawning the previous thread, plus all the stuff created prior to spawning it, get cloned into the new thread. And so on ad nausuem.

      So each thread you spawn, the time spent cloning gets longer and the memory taken by the thread gets bigger. And as is easily demonstrated, a large proportion of what gets cloned is never recovered when the thread dies. So, each thread created costs more and more in memory (most of which it has no interest in and can never use), and when it dies, it leaves ever bigger chunks of ram behind unusable and unrecoverable.

      This style of coding, and the reliance upon everything in the parent thread being cloned into the child just so that it may gain access to the one thing it actually needs chews memory like it was an all-you-can-eat buffet.

      And only being able to pass handles to threads if they were created prior to thread spawning prevents the whole class of solutions that use thread pools.

      Almost all the problems that dog iThreads exist "by design". The only thing preventing iThreads being fixed and finally made properly useful is the inane, dogged adherance to the 'fork model' of operation.

      And, if my opinion on this is so wrong, where are all the 'experts' showing us all how it should be done?


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Threaded UDP Communication
by renodino (Curate) on Feb 16, 2006 at 22:14 UTC
    This is case where apartment threading might be useful...

    I'd suggest wrapping your socket I/O logic in its own class - kinda like a protocol stack, but I'll call it an "I/O discipline". You then run the I/O discipline object in its own thread, and the session object in its own thread, and communicate between your session object and your I/O object via a Thread::Queue (or similar facility). (Take a peek at DBIx::Threaded for a similar solution to a more complex I/O discipline)

    Note that, while it is possible to pass the socket around in a fileno form, and then fdopen() it in the receiver, I think using the apt. threaded I/O discipline may be preferable, in the event you ever need to add another apartment threaded object to your app.

Re: Threaded UDP Communication
by jdhedden (Deacon) on Feb 16, 2006 at 19:41 UTC
    When I create an instance of session I want it to send a login command and receive the reply and then have a thread that will receive and process each reply as it comes until the session is destroyed.
    I presume you create the thread after the login to take care of the case where the login fails.

    My suggestion would be to rework your code so that you:
    1. Create the session object.
    2. Create a thread.
    3. Login from the thread. If this fails, communicate this to the main thread.
    4. Process commands.


    Remember: There's always one more bug.