Subject: | Problems using fork together with Tk::Stderr |
Due to the missing thread-safety of Tk it is forbidden to call Tk
functions from more than one execution thread, be it a perl thread or a
forked process. If a forked process writes something to STDERR in a
Tk::Stderr-enabled script, then unfortunately exactly this forbidden
thing happens. See the attached script.
A possible solution would be to check the pid before PRINTing or
PRINTFing in the Tk::Stderr::Handle package. I assume that
TIEHANDLE is called very early in the original (parent) process.
In this method the pid will be recorded in the Tk::Stderr::Handle
object and compared when PRINT is called. The changed methods
could look like this:
sub Tk::Stderr::Handle::TIEHANDLE {
my ($class, $window) = @_;
bless { w => $window, pid => $$ }, $class;
}
sub Tk::Stderr::Handle::PRINT {
my $self = shift;
if ($self->{pid} != $$) {
# child window, use fallback
print STDOUT "@_";
} else {
my $window = $self->{w};
my $text = $window->Subwidget('text');
$text->insert('end', $_) foreach (@_);
$text->see('end');
$window->deiconify;
$window->raise;
$window->focus;
}
}
In case of a different pid the message will not be written to the Text
widget. A better fallback would be to write to STDERR, but this is
unfortunately already tied to Tk::Stderr::Handle, hence the usage of
STDOUT. Or maybe just untieing STDERR would work?
A similar solution would probably be needed for threads, but I don't
know much about perl threads. Maybe there's also some kind of
thread id which could be compared.
Regards,
Slaven
Subject: | tk-stderr-fork.pl |
#!/usr/bin/perl -w
use Tk;
use Tk::Stderr;
$mw = tkinit;
$mw->InitStderr;
if (fork == 0) {
warn "something in child process...";
CORE::exit(0);
}
warn "something in main process...";
MainLoop;
__END__