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.
In reply to Re^3: Threading and File Handles
by BrowserUk
in thread Threading and File Handles
by matt.schnarr
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |