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

Hi, monks:
    I am trying to write a server based on Http::Daemon, 
and want the main thread to accept the client connections, 
and then pass these client connections to the serveral 
worker threads to handle, but how can I pass these client 
connections to the worker threads that have been created 
before? 
    Originally, I thought I can pass these client 
coneections by a shared array, but when I try to "push 
@clientConnections, share_clone( $client )", the 
share_clone subroutin gives me an "Unsupported type of 
'GLOB'" error message. Yes,it can't share the socket 
descriptor.

... my @clientConnections : shared; # several worker threads threads->create( \&handleClients, ( \@clientConnections ) ); while ( my $client = $httpd->accept) { push @clientConnections, share_clone( $client ); } sub handleClients { ... }

Replies are listed 'Best First'.
Re: How to pass client connections to the worker threads?
by zentara (Cardinal) on Sep 10, 2008 at 12:12 UTC
    but how can I pass these client connections to the worker threads that have been created before?

    Pass the fileno of the socket to the threads. You can share filehandles in threads, but only thru the fileno. See Simple threaded chat server. If the thread has already been created, put the fileno of the socket, into a shared variable, and instruct the thread to read it and open it.


    I'm not really a human, but I play one on earth Remember How Lucky You Are
      I had check the "simple thread chat server", indeed it is a beeter and convenient way to share the fileno of the socket connections thru a shared array variable. But I am confused with the shared variable, should we lock the shared array before we modified it and retrieve data from it?
        To be honest, I usually design with shared deep hashes, so I never have to bother with locking. I make a shared variable for each thread, so each thread's shared variable is for it only.

        This isn't the case in the chat server I showed, and I'm sure the purists out there, will say to use locking, etc, but that would only be needed if you were using a single shared variable being written to by multiple threads. In the chat server case, all they are doing is removing themselves from the shared array. It would be different if they could remove ANY thread from the array. Also, I was given this same comment from a threads guru, who was perplexed why locking wasn't needed, and he guessed that there is some low-level locking occurring because, in threads, the execution pointer can only be at one place at a time. BUT, KNOCK ON WOOD :-) Possibly an error may occur every million runs, but it seems to run fine for me. But add locking if you want, it won't hurt if done correctly.

        I hope you are not the bearer of bad news, forewarning me of the need to lock. :-)


        I'm not really a human, but I play one on earth Remember How Lucky You Are
Re: How to pass client connections to the worker threads?
by sunshine_august (Scribe) on Sep 10, 2008 at 10:45 UTC
    hi, all monks:
        I try to figure out a walk around for this problem, by
    passing the socket descriptor to the worker threads through
    unix domain, and here is my experimental scripts:  
    the server side script:
    #!/usr/bin/perl # server use strict; use warnings; use threads; use IO::Socket::UNIX; use IO::Socket::INET; use Socket::MsgHdr; $| = 1; threads->create( \&handleClient )->detach(); my $listenSocket = IO::Socket::INET->new( 'LocalPort' => '8888', 'Listen' => SOMAXCONN, 'Reuse' => 1, 'Proto' => 'tcp', ); while ( defined ( my $conSocket = $listenSocket->accept ) ) { my $un_ConSocket = IO::Socket::UNIX->new( 'Type' => SOCK_STREAM, 'Peer' => '/tmp/undomain', ); my $outMsgHdr = Socket::MsgHdr->new( 'buf' => 'some bytes' ); $outMsgHdr->cmsghdr( SOL_SOCKET, SCM_RIGHTS, pack ( "i", fileno ($conSocket) ) ); my $sentMsgLen; { no warnings qw( uninitialized ); $sentMsgLen = sendmsg( $un_ConSocket, $outMsgHdr ); } } ## end while ( defined ( my $conSocket... sub handleClient { unlink '/tmp/undomain'; my $listenSocket = IO::Socket::UNIX->new( 'Type' => SOCK_STREAM, 'Local' => '/tmp/undomain', 'Listen' => SOMAXCONN, ); my $unConSocket = $listenSocket->accept; my $inMsgHdr = Socket::MsgHdr->new( buflen => 128, controllen => +256 ); my $receivedMsgLen; { no warnings qw( uninitialized ); $receivedMsgLen = recvmsg( $unConSocket, $inMsgHdr); } print 'received msg:', $inMsgHdr->buf(), "\n"; my ( $level, $type, $data ) = $inMsgHdr->cmsghdr(); my $fileno = unpack ( 'i', $data ); open ( CLIENTSOCKET, '+<&=' , $fileno ); while ( my $line = <CLIENTSOCKET> ) { print CLIENTSOCKET $line or warn $!; print "thread ", threads->tid(), " received line: $line\n"; } } ## end sub handleClient
    The client side script:
    #!/usr/bin/perl # client use strict; use warnings; use IO::Socket; $| = 1; my $socket = IO::Socket::INET->new( 'Proto' => 'tcp', 'PeerAddr' => '127.0.0.1', 'PeerPort' => '8888', ) or die $@; my $msgOut = "hello, server\n"; while (1) { print $socket $msgOut; my $msgIn = <$socket>; print "client received echo:$msgIn\n"; sleep 1; } ## end while (1)
    It didn't work well, the worker thread only can get the first message from the client, and then hold on
     while ( my $line = <CLIENTSOCKET> ) 
    , and the client side also hold on
    my $msgIn = <$socket>; 
    I can't figure out why they both hold on readline operation.
    
    thx 4 helpping ...

      The problem here is that CLIENTSOCKET is not autoflushing. You should add a line like CLIENTSOCKET->autoflush(1); somewhere between the open and the print. Note that for the other sockets this is not necessary because IO::Socket sets its sockets to autoflush automatically.

      By the way, you don't need to store the socket in your filesystem (in /tmp/undomain). Instead, you can call socketpair before you create the thread.

        With CLIENTSOCKEt->autoflush(1), it works well, thx.
Re: How to pass client connections to the worker threads?
by Anonymous Monk on Sep 10, 2008 at 05:09 UTC
    Yes,it can't share the socket descriptor.
    Doesn't that give you your answer? You can't share sockets.
      But I want to find a way to pass the client connections to the worker threads. Is that possible??