matt.schnarr has asked for the wisdom of the Perl Monks concerning the following question:

Hello to all those who know what they're doing!

This is my first attempt at using Threading and I've apparently stumpted myself. :(

I have a main body of code which will open a file handle and then I spawn a new thread which I would like to use to close this file handle that I've just opened outside the new thread.

Unfortunatley it doesn't seem like I can close the file handle in a different thread then which it was opened.

Does anyone have any ideas?

#!/usr/bin/perl use Thread; $filename = "/tmp/input.log"; $outputdir = "/var/log/"; $first_event = 1; $position = 0; $time = time(); # Define Timer sub timer { my $counter = 0; while (1) { sleep(1); if ($counter >= 60) { $counter = 0; # #I can't seem to close FILE from the main thre +ad... why? # close (FILE); $outputfile = $outputdir . "log-". time(); open(FILE, "> $outputfile") || die("Can't open + $outputfile to write to"); } $counter++; } } $thr1 = new Thread \&timer; # Spawn the thread open(TAIL, "tail --follow=name $filename|"); $outputfile = $outputdir . "log-". time(); open(FILE, "> $outputfile") || die("Can't open $outputfile to write to +"); while (<TAIL>) { # do some stuff and print to FILE }

Thanks! Matthew Schnarr

Replies are listed 'Best First'.
Re: Threading and File Handles
by BrowserUk (Patriarch) on Sep 01, 2004 at 13:59 UTC

    Which version of perl are you using?

    Are you aware that there are 2 versions of threading available in Perl. Thread and threads.

    The former (also referred to as 5005threads) has been deprecated since 5.6 and should not be used for any new code.

    The latter, (also referred to as ithreads) should be used for all new work.

    This almost certainly doesn't answer your question or solve your problem, but if you start using the correct libraries and then re-raise your question if you still encounter the problem, it will be easier to tackle.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
      I'm using perl version 5.8.0 and now I've switched to ithreads.

      Unfortunately my problem still is not fixed (like you suggested)... Here is a new snippit of my code - any other help would be greatly appreciated!

      #!/usr/bin/perl use threads; $filename = "/tmp/input.log"; $outputdir = "/var/log/"; $first_event = 1; $position = 0; $time = time(); # Define Timer sub timer { my $counter = 0; while (1) { sleep(1); if ($counter >= 60) { $counter = 0; # #I can't seem to close FILE from the main thre +ad... why? # close (FILE); $outputfile = $outputdir . "log-". time(); open(FILE, "> $outputfile") || die("Can't open + $outputfile to write to"); } $counter++; } } $thr1 = threads->new(\&timer); # Spawn the thread open(TAIL, "tail --follow=name $filename|"); $outputfile = $outputdir . "log-". time(); open(FILE, "> $outputfile") || die("Can't open $outputfile to write to +"); while (<TAIL>) { # do some stuff and print to FILE }

      Thanks! Matthew Schnarr

        The problem is grounded in the duplication of state that occurs with threads. When you spawn a thread, the thread gets a copy of all the state that exists in the thread that spawned it. This includes the state associated with file handles. This necessary to allow each thread to have it own filepointer position, buffers etc. independant of other threads filepointers and buffers.

        At the point in your program where you spawn your thread, FILE has not been opened, and that it the state that gets copied. Hence, when you later try to close that filehandle you get the " Bad file descriptor at ... " error. This can be more clearly demonstrated by the following simplification of your program:

        #! perl -slw use strict; use threads; sub t{ sleep 10; close FILE or die "Error closing FILE : $!" }; threads->new( \&t )->join; open FILE, '>', 'junk.dat' or die "junk.dat: $!"; print FILE 'Some stuff ', $_ for 1 .. 1000; #threads->new( \&t )->join;

        If you run this as is, the thread gets started and immediatly sleeps for 10 seconds, giving the first thread the chance to open the FILE and write some stuff to it. When the 10 seconds is up, the thread attempts to close the FILE handle, and fails with the "Bad file descriptor" error because no file descriptor was associated with FILE when the thread was spawned.

        If you comment out the first thread->new line, uncomment the second, and re-run the program, you'll find that the close is successful because FILE has a descriptor associated with it at the point of the spawn.

        Ultimately, the problem comes down to one of bad design on behalf of your program. It is generally not wise to share resource, not even (or maybe especially) global resources like filehandles between threads. At least not without giving careful thought to the ramifications of doing so.

        For your particular purpose, I would code the application something like this:

        #! perl -slw use strict; use threads; use threads::shared; my $filename = "/tmp/input.log"; $outputdir = "/var/log/"; my $first_event = 1; my $position = 0; my $time = time(); my $timerFlag : shared # Define Timer sub timer { my $counter = 0; while (1) { sleep(1); if ($counter >= 60) { $counter = 0; lock $timerFlag; $timerFlag = 1; } $counter++; } } my $thr1 = threads->new(\&timer); # Spawn the thread open(TAIL, "tail --follow=name $filename|") or die "TAIL : $!"; my $outputfile = $outputdir . "log-". time(); open(FILE, "> $outputfile") or die("Can't open $outputfile to write to $!"); while (<TAIL>) { if( $timerFlag ) { lock $timerFlag; $timerFlag = 0; close FILE or die $!; $outputfile = $outputdir . "log-". time(); open(FILE, "> $outputfile") or die("Can't open $outputfile to write to $!"); } print( FILE $_ ) or die "Writing to FILE : $!"; }

        Note: That is just example code to demonstrate the idea, not fully thought through or tested.


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
        "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
        I'm not familiar enough with the "inner workings" of Perl to play around with "passing around control of filehandles". However, I rarely ever see this done, and to do it with threads adds another layer of complexity. I think you may be forging new territory/. :-)

        My first thought is to forget about passing "filhandles" and to pass the "files saved in scalar variables" through the cond_broadcast facility in threads. I'm not too quick to whip off example code, but what I mean is to figure out how to pass scalars around between threads. Alot of examples have been posted how to do this. Then in each thread, use the "variable as a filehandle technique" like:

        my $foo = ''; open FILEHANDLE, '+>', \$foo or die $!; print FILEHANDLE "Contents of File"; seek(FILEHANDLE,0,0); my @contents = <FILEHANDLE>; close FILEHANDLE or die $!; print 'From $foo: ', $foo, $/; print 'From file read: ', @contents, $/;
        Now, in each thread, you can open and close on that variable, then "broadcast it" to the other threads where they can do the same thing.

        I'm not really a human, but I play one on earth. flash japh
Re: Threading and File Handles
by doowah2004 (Monk) on Sep 01, 2004 at 20:12 UTC
    Im not sure that this will work, but it should be a quick check and worth a try. Instead of using 'FILE' as the filehandle, use $file. By using a variable it works around the file handle being 'local' to the main prog.