in reply to Re: Threading and File Handles
in thread Threading and File Handles

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

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

    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
Re: Threading and File Handles - re-opening issue
by zentara (Cardinal) on Sep 02, 2004 at 12:53 UTC
    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