I tweaked it a bit.  Attached.

On Thu, May 6, 2010 at 06:02, alfonso caponi <alfonso.cap...@gmail.com> wrote:
> Inspired by the pool.pl example, I wrote a simple multi-thread script for
> dns server testing on my LAN. It seems working fine, but I would like your
> opinion on any improvements.
>
> Note: I've commented "#while (! $TERM)" to avoid multiple requests while !
> $TERM :(
>
> Thank you very much at all!
>
> 2010/4/16 Jerry D. Hedden <jdhed...@cpan.org>
>>
>> Get the 'threads' distribution from CPAN and look at the sample
>> scripts in the 'examples' directory.
>>
>> On Fri, Apr 16, 2010 at 11:48, alfonso caponi <alfonso.cap...@gmail.com>
>> wrote:
>> > Hi list,
>> >
>> > I've some doubts with a multi-threads script. My goal is to create a
>> > script
>> > with the same max number of threads running at all times.
>> >
>> > For example: I've an array with 9 elements and 3 threads to process it;
>> > each
>> > thread can process an element using a different time (a thread could end
>> > before another) and I would like 3 threads always running.
>> >
>> > My idea is to take an element from a queue, start threads to reach the
>> > maximum number of active threads allowed and simultaneously start a
>> > separate
>> > (but unique) thread to wait every thread in execution.
>> >
>> > In attachment my "solution". Have you any tips? I would not use other
>> > modules as well threads, threads::shared, thread::queue.
>> >
>> > Thank you very much!
>> > Al
>
>
#!/usr/bin/perl

$|=1;

use strict;
use warnings;

use Net::DNS;
use threads;
use threads::shared;
use Thread::Queue;
use Uniq;

my $ip_list  = $ARGV[0];

die "$0 <ip list file>\n" unless $ARGV[0];

########################
### Global Variables ###

# FQDN list
my @fqdn = ("www.nba.com","www.google.com","www.kernel.org","www.linux.org");

# Maximum working threads
my $MAX_THREADS = 10;

# Maximum thread working time
my $TIMEOUT = 10;

# Flag to inform all threads that application is terminating
my $TERM :shared = 0;

# Prevents double detach attempts
my $DETACHING :shared;

#######################
### Signal Handling ###

# Gracefully terminate application on ^C or command line 'kill'
$SIG{'INT'} = $SIG{'TERM'} = sub {
    print(">>> Terminating <<<\n");
    $TERM = 1;
};

# This signal handler is called inside threads
# that get cancelled by the timer thread
$SIG{'KILL'} = sub {
    # Tell user we've been terminated
    printf("%3d <- Killed\n", threads->tid());
    # Detach and terminate
    lock($DETACHING);
    threads->detach() if ! threads->is_detached();
    threads->exit();
};

#####

my @hosts;
open(IPLIST, "<$ip_list") || die "Error : cannot open the ip address list file: 
$!\n";
    chomp (@hosts = <IPLIST>);
close (IPLIST);

@hosts = uniq sort @hosts;

### fill the queue
# >> Don't need a shared queue for this
# >> Just use @hosts directly
#my $q = new Thread::Queue;
#foreach my $host (@hosts) {
#    foreach my $fqdn (@fqdn) {
#        $q->enqueue("$host;$fqdn");
#    }
#}

###############################
### Main Processing Section ###

MAIN: {

    # Start timer thread
    print "[+] Start timer thread\n";
    my $queue = Thread::Queue->new();
    threads->create('Timer',$queue)->detach();

    # Manage the thread pool until we run out of data
    # or signalled to terminate
    while (@hosts && ! $TERM) {

        # Keep max threads running
        for (my $needed = $MAX_THREADS - threads->list(); $needed && ! $TERM; 
$needed--) {

            my $element = shift(@hosts);
            last if (! $element);

            # New thread
            threads->create('RequestDNS',$queue,$element,$TIMEOUT);
        }

        # Wait for any threads to finish
        sleep(1);
    }

    # Wait for max timeout for threads to finish
    while ((threads->list() > 0) && $TIMEOUT--) {
        sleep(1);
    }

    # Detach and kill any remaining threads
    foreach my $thr (threads->list()) {
        lock($DETACHING);
        $thr->detach() if ! $thr->is_detached();
        $thr->kill('KILL');
    }
}

exit(0);

sub RequestDNS {

    my ($queue,$element,$timeout) = @_;

    my ($dns_server,$fqdn) = split(/;/,$element);

    # My thread ID
    my $tid = threads->tid();
    printf("$dns_server -> %3d\n", $tid);

    # Register with timer thread
    $queue->enqueue($tid,$timeout);

    # Do some work while monitoring $TERM
    #while (! $TERM) {

        print "try $dns_server;$fqdn\n";

        my $res = Net::DNS::Resolver->new;
        $res->nameservers($dns_server);

        my $query = $res->query($fqdn,"A");

        if ($query) {
            foreach my $rr (grep { $_->type eq 'A' } $query->answer) {
                my $reply = $rr->address;
                print "$dns_server;$fqdn;$reply\n";
            }
        }# else {
        #    warn "query failed: $dns_server ($fqdn)", $res->errorstring, "\n";
        #}
    #}

    # Remove signal handler
    $SIG{'KILL'} = sub {};

    # Unregister with timer thread
    $queue->enqueue($tid, undef);

    # Tell user we're done
    printf("%3d <- Finished\n", $tid);

    # Detach and terminate
    lock($DETACHING);
    threads->detach() if ! threads->is_detached();
    threads->exit();
}

# The timer thread that monitors other threads for timeout
sub Timer {

    my $queue = shift;   # The registration queue
    my %timers;          # Contains threads and timeouts

    # Loop until told to quit
    while (! $TERM) {

        # Check queue
        while (my $tid = $queue->dequeue_nb()) {
            if (! ($timers{$tid}{'timeout'} = $queue->dequeue()) || ! 
($timers{$tid}{'thread'}  = threads->object($tid))) {
                # No timeout - unregister thread
                delete($timers{$tid});
            }
        }

        # Cancel timed out threads
        foreach my $tid (keys(%timers)) {
            if (--$timers{$tid}{'timeout'} < 0) {
                $timers{$tid}{'thread'}->kill('KILL');
                delete($timers{$tid});
            }
        }

        # Tick tock
        sleep(1);
    }
}

Reply via email to