use strict; use warnings; use threads; use threads::shared ; use Thread::Queue; #---------------------------------------------------------------------------------------- # Options my $YIELD = 0 ; # yield after async my $CLOSE_STDIN = 0 ; # close STDIN after dispatching Terminal Watcher my $INPUT_FILE = 0 ; # read from "file" not from STDIN foreach my $arg (@ARGV) { if ($arg =~ m/^y/i) { $YIELD = 1 ; } elsif ($arg =~ m/^c/i) { $CLOSE_STDIN = 1 ; } elsif ($arg =~ m/^f/i) { $INPUT_FILE = 1 ; } else { die "don't recognise $arg" ; } ; } ; #---------------------------------------------------------------------------------------- # Instrumentation use IO::Handle ; use Time::HiRes qw(time) ; my $START = time() ; my @trace : shared = () ; sub trace { my ($s) = @_ ; my $when = time() - $START ; $s =~ s/\n/\\n/g ; { lock(@trace) ; push @trace, sprintf("%12.6f: (%2d) '%s'\n", $when, threads->tid(), $s) ; } ; } ; sub stdin_state { my ($tag) = @_ ; my $state = "STDIN is: " .(defined(fileno(STDIN)) ? 'open' : 'closed'). " $tag" ; print "$state\n" ; trace("$state") ; } ; my $IN = "fred\n" ."bill\n" ."john\n" ."mary\n" ."quit\n" ; if ($INPUT_FILE) { close STDIN ; open STDIN, "<", \$IN or die "failed to reopen STDIN" ; } ; #---------------------------------------------------------------------------------------- # The instrumented code # Create terminal watcher print "Create terminal watcher...\n"; my $Q_stdin = Thread::Queue->new; async { trace("Terminal Watcher started") ; while (1) { trace("Waiting for STDIN") ; last if !defined($_ = ) ; trace("Input: '$_'") ; $Q_stdin->enqueue($_) ; } ; trace("Terminal Watcher terminating") ; }->detach; threads->yield() if $YIELD ; stdin_state("after Terminal Watcher dispatched") ; if ($CLOSE_STDIN) { close STDIN ; stdin_state("after close") ; } ; my $Q_found = Thread::Queue->new; my $cmd; print "Awaiting commands...\n"; trace("Entering MAIN_LOOP") ; MAIN_LOOP: while (not defined $cmd or $cmd !~ /^q/i) { trace("Top of MAIN_LOOP") ; sleep(1); # Reduce load # Process commands $cmd = $Q_stdin->dequeue_nb; if (defined $cmd) { trace("Dequeued '$cmd'") ; chomp $cmd; if ($cmd =~ /^q/i) { trace("'QUIT' command") ; print "Resolving open threads\n"; } else { trace("About to start child thread") ; async { trace("Child started") ; $Q_found->enqueue( $cmd ) ; trace("Child enqueued '$cmd' & terminating") ; }->detach; threads->yield() if $YIELD ; trace("Dispatched child thread") ; } } else { trace("Nothing to dequeue") ; } ; # Print announcements while (defined(my $output = $Q_found->dequeue_nb)) { trace("Output: '$output'") ; print ">$output\n"; } } trace("Left MAIN_LOOP") ; #------------------------------------------------------------------------------ print STDERR "-- with Yield after async\n" if $YIELD ; print STDERR "-- with Close STDIN\n" if $CLOSE_STDIN ; print STDERR "-- with Input from File" if $INPUT_FILE ; print STDERR sort @trace ;