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

I have a basic daemon that I am trying to use threads and Thread::Queue with, and have been searching and going through a myriad of problems to get it to work, but to no avail..

I went through this entire thread (which is the most useful I could find) but it only confused me and complicated things more :(
http://www.perlmonks.org/?node_id=549414

I will post my working basic non-threading daemon script first to show what I was originally trying to accomplish
Any hints/suggestions would be greatly appreciated
Thanks..

use strict; use warnings; use Socket; use POSIX qw(setsid); use Proc::PID::File; use Log::Dispatch; use Log::Dispatch::File; use Date::Format; use File::Spec; use FindBin qw($Bin); sub dienice ($); my $port = 9999; my $log_connections = 1; # Want to log if script is executed when it's already running? my $log_is_running = 1; my $content = '<?xml version="1.0"?><cross-domain-policy><allow-access +-from domain="*" to-ports="' . $port . '" /></cross-domain-policy>'; my $NULLBYTE = pack( 'c', 0 ); ### ### Detect our base directory where we create the log and pid files ### our $BASE_DIR = $Bin; # Get script name, chop off any preceding path it was called with and +chop off its extension (ex: 'some/dir/script.pl' becomes 'script') our $ME = $0; $ME =~ s|.*/||; $ME =~ s|\..*||; our $LOG_FILE = "$ME.log"; ### ### Setup a logging agent ### my $log = new Log::Dispatch( callbacks => sub { my %h=@_; return Date::Format::time2str('%B %e +%T', time)." $ME\[$$]: ".$h{message}."\n"; } ); $log->add( Log::Dispatch::File->new( name => 'file1', min_level => 'warning', mode => 'append', filename => File::Spec->catfil +e($BASE_DIR, $LOG_FILE), ) ); $log->warning("Logging Started"); ### ### Fork and background daemon process ### startDaemon(); ### ### Setup signal handlers to give us time to cleanup (and log) before +shutting down ### my $running = 1; $SIG{HUP} = sub { $log->warning("Caught SIGHUP: exiting gracefully") +; $running = 0; }; $SIG{INT} = sub { $log->warning("Caught SIGINT: exiting gracefully") +; $running = 0; }; $SIG{QUIT} = sub { $log->warning("Caught SIGQUIT: exiting gracefully" +); $running = 0; }; $SIG{TERM} = sub { $log->warning("Caught SIGTERM: exiting gracefully" +); $running = 0; }; ### ### As long as the daemon is running, listen for and handle received c +onnections ### while ($running) { ### ### BEGIN LISTENING ### socket( LISTENSOCK, PF_INET, SOCK_STREAM, getprotobyname( 'tcp' ) +) or dienice("socket() error: $!"); setsockopt( LISTENSOCK, SOL_SOCKET, SO_REUSEADDR, pack( 'l', 1 ) ) + or dienice("setsockopt() error: $!"); bind( LISTENSOCK, sockaddr_in( $port, INADDR_ANY ) ) or dienice("b +ind() error: $!"); listen( LISTENSOCK, SOMAXCONN ) or dienice("listen() error: $!"); $log->warning("Listening on port $port"); ### ### HANDLE CONNECTIONS ### while ( my $clientAddr = accept( CONNSOCK, LISTENSOCK ) ) { my ( $clientPort, $clientIp ) = sockaddr_in( $clientAddr ); my $clientIpStr = inet_ntoa( $clientIp ); local $/ = $NULLBYTE; my $request = <CONNSOCK>; chomp $request; if ( $request eq '<policy-file-request/>' ) { $log->warning("XML Policy file request from: $clientIpStr" +) if ($log_connections); print CONNSOCK $content.$NULLBYTE; close CONNSOCK; } else { $log->warning("Connection from: $clientIpStr") if ($log_co +nnections); print CONNSOCK "<data><ip>$clientIpStr</ip></data>".$NULLB +YTE; close CONNSOCK; next; } } } ### ### Mark a clean exit in the log ### $log->warning("Logging Stopped"); ### ### startDaemon ### sub startDaemon { defined(my $pid = fork) or dienice("Can't fork: $!"); exit if $pid; POSIX::setsid() or dienice("Can't start a new session: $!"); # Get a PID file - or exit without error in case we're running per +iodically from cron if ( Proc::PID::File->running(dir => "$BASE_DIR", name => "$ME", v +erify => "1") ) { $log->warning("Daemon Already Running!") if ($log_is_running); exit(0); } } ### ### dienice ### sub dienice ($) { my ($package, $filename, $line) = caller; # write die messages to the log before die'ing $log->critical("$_[0] at line $line in $filename"); die $_[0]; }

Replies are listed 'Best First'.
Re: Trying to thread a daemon with threads and Thread::Queue
by BrowserUk (Patriarch) on Aug 27, 2008 at 09:00 UTC

    What do you hope to achieve by threading this?

    Given that you are responding to each connection with a single short line of output, there is no point in threading it.

    It would take far longer to start a new thread than to just send the line and close the connection. Even if you used a pool of pre-existing threads, it would require (at minimum) a context switch between receiving the connection and responding, and that would again be far slower than just responding.

    On the basis of what you've posted, there is nothing to be gained from threading this application.


    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.
      That's exactly what I was trying to do: use a pool of pre-existing threads.
      What I was hoping to achieve is to accept more than one connection at a time, as this script currently only handles one connection at a time.
      I am planning on releasing this as part of a open source project, and as some people who might run this script (as part of the implementation) might have high traffic, this may not be very desirable, I think.

      For example, simulating 100 connections from the bash shell locally on the server:
      time for i in `seq 1 100`; do
      perl -e 'printf "<policy-file-request/>%c",0' | nc myhost.com 9999
      perl -e 'printf ">%c",0' | nc myhost.com 9999; done
      takes 1.990 seconds

      And I believe if I was to have say a pool of 3 threads (on a dual-processor server), I could easily handle 100 client connections per second

      I extended the script quite a bit from a sample script offered by someone at Adobe, who also made another standalone script in python that supports more then one user connection (both scripts in the standalone folder of the sample scripts zip file at the head of the page):
      http://www.adobe.com/devnet/flashplayer/articles/socket_policy_files.html

      And I was hoping to extend it a bit more to thread just like the sample python script does, as to offer different variations (threaded or non-threaded perl scripts, python, java)

      So, briefly, this is what I was trying to do (iirc):
      $listeners = 3; my $Q = Thread::Queue->new; while ($running) { # Spawn listener threads for (1..$listeners) { threads->create(\&handleData)->detach; } # BEGIN LISTENING socket( LISTENSOCK, PF_INET, SOCK_STREAM, getprotobyname( 'tcp' ) +) or dienice("socket() error: $!"); setsockopt( LISTENSOCK, SOL_SOCKET, SO_REUSEADDR, pack( 'l', 1 ) ) + or dienice("setsockopt() error: $!"); bind( LISTENSOCK, sockaddr_in( $port, INADDR_ANY ) ) or dienice("b +ind() error: $!"); listen( LISTENSOCK, SOMAXCONN ) or dienice("listen() error: $!"); $log->warning("Listening on port $port"); while ( my $clientAddr = accept( CONNSOCK, LISTENSOCK ) ) { # put the connection on the queue $Q->enqueue(fileno(CONNSOCK), $clientAddr ); } } sub handleData { my $tid = threads->tid(); while (1) { my ($fno, $clientAddr) = $Q->dequeue(2); next if $fno eq 'STOP'; my ( $clientPort, $clientIp ) = sockaddr_in( $clientAddr ); my $ipStr = inet_ntoa( $clientIp ); open my $socket, '+<&=' . $fno; while (my $request = <$socket>) { chomp $request; if ($request eq '<policy-file-request/>') { $log->warning("($tid) : XML Policy file request from: +$ipStr"); print $socket $content.$NULLBYTE; close $socket; } else { $log->warning("($tid) : Connection from: $ipStr"); print $socket "<data><ip>$ipStr</ip></data>".$NULLBYTE +; close $socket; } }; #close $socket; } return 1; }


      Then I run it, and simulate 2 connections (the handshake) from command-line shell like so:

      perl -e 'printf "<policy-file-request/>%c",0' | nc myhost.com 9999 ; perl -e 'printf "%c",0' | nc myhost.com 9999

      And it just hangs, instead of returning back to the shell. So I hit CTRL+C, then on the server log I see this show right after I CTRL+C'ed on the client connect simulation command:

      August 27 10:33:44 xmlsockd[1722]: (1) : Connection from: x.x.x.x


      And the server dies with no error/log messages :(

      What am I doing wrong?
        What am I doing wrong?

        Your using the "new" version of Thread::Queue which used to be a simple, reliable and fast module, but now someone has taken it upon theselves to "enhance" it, and in the process they have fu**ed up the single most useful, most reliable threading module.

        Is there any process for banning people from messing with things that work?


        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.

        Try replacing your handledata() sub with this:

        sub handleData { my $tid = threads->tid(); my $fno = $Q->dequeue(); my $clientAddr = $Q->dequeue(); next if $fno eq 'STOP'; my ( $clientPort, $clientIp ) = sockaddr_in( $clientAddr ); my $ipStr = inet_ntoa( $clientIp ); open my $socket, '+<&=' . $fno or die $!; my $request = <$socket>; chomp $request; if ($request eq '<policy-file-request/>') { warn("($tid) : XML Policy file request from: $ipStr"); print $socket $content.$NULLBYTE; } else { warn("($tid) : Connection from: $ipStr"); print $socket "<data><ip>$ipStr</ip></data>".$NULLBYTE; } shutdown $socket, 2; close $socket; return 1; }

        I don't anticipate that it will perform better than your non-threaded solution for teh reasons I stated above. Create a new thread to reply with a single line of code is never going to be quicker than replying inline, but it might just work well enough to allow you to see that for yourself.

        For the application as you've posted, I am quite certain that you will not improve your performance using threads this way.

        You'd almost certainly be better off using IO::Socket::INET and setting the Listener option to allow the tcpip stack to queue inbound requests.


        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.
Re: Trying to thread a daemon with threads and Thread::Queue
by moritz (Cardinal) on Aug 27, 2008 at 08:38 UTC
    and going through a myriad of problems to get it to work, but to no avail..

    Maybe you could elaborate on what exactly you are having problems with? What did you try to make that program threaded?