How to share an object which contains a filehandle? How to share an object which contains a filehandle? multithreading multithreading

How to share an object which contains a filehandle?


I don't have access to threaded Perl at the moment, so can't guarantee that this will work.

But a somewhat simplistic approach would be to use a level of abstraction and store a key/index into a global filehandle hash/array into the object, something similar to the following:

my @filehandles = (); # Stores all the filehandles         ### CHANGEDmy $stdout; # Store the index into @filehandles, NOT filehandle.            # Should really be renamed "$stdout_id" instead.sub stdout {    my $self = shift;    return $stdout if defined $stdout;    $stdout = scalar(@filehandles);                         ### CHANGED    my $stdout_fh = $self->dup_filehandle(\*STDOUT);        ### CHANGED    push @filehandles, $stdout_fh;                          ### CHANGED    $self->autoflush($stdout_fh);                           ### CHANGED    $self->autoflush(\*STDOUT);    return $stdout;}sub safe_print {    my $self = shift;    my $fh_id = shift;                                       ### CHANGED    my $fh = $filehandles[$fh_id];                           ### CHANGED    local( $\, $, ) = ( undef, '' );    print $fh @_; }

I have a strong feeling that you would need to somehow also thread-safe the list of IDs, so perhaps an shared index counter would be needed instead of $stdout = scalar(@filehandles);


As an alternative to my other answer with global array, here's another approach from Perlmonks:

http://perlmonks.org/?node_id=395513

It works by actually storing fileno (file descriptor) of the filehandle. Here's his sample code based on what BrowserUk posted:

my $stdout; # Store the fileno, NOT filehandle.            # Should really be renamed "$stdout_fileno" instead.sub stdout {    my $self = shift;    return $stdout if defined $stdout;    my $stdout_fh = $self->dup_filehandle(\*STDOUT);        ### CHANGED    $stdout = fileno $stdout_fh;                            ### CHANGED    $self->autoflush($stdout_fh);                           ### CHANGED    $self->autoflush(\*STDOUT);    return $stdout;}sub safe_print {    my $self = shift;    my $fh_id = shift;                                       ### CHANGED    open(my $fh, ">>&=$fh_id")                                ### CHANGED        || die "Error opening filehandle: $fh_id: $!\n";     ### CHANGED    local( $\, $, ) = ( undef, '' );    print $fh @_; }

CAVEAT - as of 2004, this had a bug where you couldn't read from the shared filehandle from >1 thread. I am guessing that writing is OK. More specifics on how to do synchronised writes on a shared filehandle (from the same Monk): http://www.perlmonks.org/?node_id=807540


It just occurred to me there's two possible solutions:

  1. Put the filehandle outside the Streamer object.
  2. Put the Streamer object outside the Formatter.

@DVK's suggestions are all about doing 1.

But 2 is in some ways simpler than 1. Instead of holding the Streamer object itself, the Formatter can hold an identifier to the Streamer object. If the Streamer is implemented inside-out, that happens naturally!

Unfortunately, reference addresses change between threads, even shared ones. This can be solved with Hash::Util::FieldHash, but that's a 5.10 thing and I have to support 5.8. It's possible something could be put together using CLONE.