$self->{rw} = 1;
####
${$self->{rw}} = 1;
####
#!/usr/bin/perl -w
use strict;
use threads;
sub threadA {
for(my $i = 1; $i <= 100; $i++) {
warn "Thread A: Print $i";
print FILE "A: $i\n";
}
}
sub threadB {
for(my $i = 1; $i <= 100; $i++) {
warn "Thread B: Print $i";
print FILE "B: $i\n";
}
}
tie *FILE, 'FHShare';
open FILE, '>fhshare.out';
autoflush FILE;
my $a = threads->create('threadA');
threadB();
$a->join;
close FILE;
package FHShare;
# A package to share filehandles between threads via tie()
use strict;
use threads;
use threads::shared;
use FileHandle;
# Subroutine to start thread that accesses filehandle
sub start_thread {
my $self = shift;
$self->cmdloop();
}
sub TIEHANDLE {
my $class = shift;
my $sem : shared;
my $cw : shared = 0;
my $rw : shared = 0;
my @cmd : shared;
my @rv : shared;
my $err : shared;
my $self = {
cmd => \@cmd, # Command
rv => \@rv, # Return value
err => \$err, # Error from calling filehandle method
# A semaphore to lock access to send commands to the command
# thread:
sem => \$sem,
# A semaphore for the command thread to notify it that it has
# a command waiting:
cw => \$cw, # 1 for command waiting
# A semaphore for the command thread to notify other threads
# that return values are waiting:
rw => \$rw }; # 1 for values waiting
bless $self, $class;
# Create thread attached to this handle
threads->create('start_thread', $self)->detach();
return $self;
}
sub DESTROY {
my $self = shift;
lock $self->{sem};
${$self->{cmd}} = (); # Empty list to quit
{
lock $self->{cw};
${$self->{cw}} = 1;
cond_signal $self->{cw};
}
{
lock $self->{rw};
while (!${$self->{rw}}) {
cond_wait $self->{rw};
}
}
}
# Autoload section -- convert uppercase method names called to
# lowercase method names for the underlying filehandle.
our $AUTOLOAD;
sub AUTOLOAD {
my $self = shift;
warn "Autoloaded $AUTOLOAD";
my $method = lc($AUTOLOAD); # Convert method to lc.
$method =~ s/^.*:://; # Remove prefix.
FileHandle->can($method) or
die "Error: filehandles have no '$method' method";
my @ret; # Return value
{
lock $self->{sem}; # Lock access
# Send command:
warn "$AUTOLOAD: Sent $method(@_)";
@{$self->{cmd}} = ($method, @_);
# Tell command thread that it is waiting:
{
lock $self->{cw};
${$self->{cw}} = 1;
cond_signal $self->{cw};
}
# Wait for return values
warn "$AUTOLOAD: waiting";
{
lock $self->{rw};
while (!${$self->{rw}}) {
warn "$AUTOLOAD: waiting for data";
cond_wait $self->{rw};
warn "$AUTOLOAD: done waiting";
}
${$self->{rw}} = 0; # Reset
}
# Handle errors
if (my $err = ${$self->{err}}) {
chomp $err;
my($filename, $line) = (caller)[2,3];
die "$err at $filename line $line\n";
}
@ret = @{$self->{rv}}; # Get return values
}
return @ret; # Return w/ return vals
}
# Method for thread that accesses underlying filehandle to loop
# through commands
sub cmdloop {
my $self = shift;
warn "Entered command loop";
my $fh = new FileHandle;
while (1) {
# Wait for and get command
{
warn "Waiting for command ready";
lock $self->{cw};
while (!${$self->{cw}}) {
cond_wait $self->{cw};
}
$self->{cw} = 0; # Reset
}
# Get the method and arguments
my @cmd = @{$self->{cmd}} or last; # Quit on empty list
my $method = shift @cmd;
my @args = @cmd;
# Call the method, enqueue the return value(s)
my $mcall = "\$fh->$method(\@args)";
warn "sending \$fh->$method(@args)";
@{$self->{rv}} = eval $mcall;
#@{$self->{rv}} = $fh->$method(@args);
# Record errors
${$self->{err}} = $@;
} continue {
# Signal that values are waiting
{
warn "Signaling data available";
lock $self->{rw};
warn "locked 'rw' flag";
warn "'rw' flag is $self->{rw}";
${$self->{rw}} = 1;
warn "'rw' flag is $self->{rw}";
cond_signal $self->{rw};
warn "signalled 'rw' flag";
}
}
}
1;