xtronics has asked for the wisdom of the Perl Monks concerning the following question:

I just ran the test listed in this deb bug report code using perl 5.8.4 and got a seg fault. I'm confused - I read in a few places that this was no longer a problem? I wanted to run Interchange with stock debian perl code if I can, but I don't understand if this is still an issue?

This bug is getting close to 2 years old -seems like perl should default to non threaded or this bug should get fixed?

I ran the test code listed here:

http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=203579

and got a seg fault with the testing version of perl.

But this:

http://www.perl.com/lpt/a/2003/01/16/whatsnew.html

--seems to imply that it is fixed????? (see under the "Perl Threads" section.)

Has anyone in perl upstream ever looked at this?:

Is this the same bug that causes problems in the debian interchange package?

I would like to recreate the perl thread bug in interchange?

Replies are listed 'Best First'.
Re: Threaded perl (re: debian bug #203579)
by Corion (Patriarch) on Feb 26, 2005 at 08:56 UTC

    After some digging through the ugly (and at least partially) bad code, I think I know where the error happens, and what you might do to hide this error. There is a programming error in the code where one of the references to a shared variable gets overwritten by a number. In sub cmdloop, replace the line

    $self->{rw} = 1;
    by
    ${$self->{rw}} = 1;

    This makes the code "work" for me in the sense that the segfault disappears with both Perl versions. It then seems to hang in the PRINT command, but that might well be an error in the programming logic. I've pasted my modified code below, maybe somebody sees the deadlock.

    #!/usr/bin/perl -w use strict; use threads; sub threadA { for(my $i = 1; $i <= 100; $i++) { warn "Thread A: Print $i"; print FILE "A: $i\n"; } } sub threadB { for(my $i = 1; $i <= 100; $i++) { warn "Thread B: Print $i"; print FILE "B: $i\n"; } } tie *FILE, 'FHShare'; open FILE, '>fhshare.out'; autoflush FILE; my $a = threads->create('threadA'); threadB(); $a->join; close FILE; package FHShare; # A package to share filehandles between threads via tie() use strict; use threads; use threads::shared; use FileHandle; # Subroutine to start thread that accesses filehandle sub start_thread { my $self = shift; $self->cmdloop(); } sub TIEHANDLE { my $class = shift; my $sem : shared; my $cw : shared = 0; my $rw : shared = 0; my @cmd : shared; my @rv : shared; my $err : shared; my $self = { cmd => \@cmd, # Command rv => \@rv, # Return value err => \$err, # Error from calling filehandle method # A semaphore to lock access to send commands to the command # thread: sem => \$sem, # A semaphore for the command thread to notify it that it has # a command waiting: cw => \$cw, # 1 for command waiting # A semaphore for the command thread to notify other threads # that return values are waiting: rw => \$rw }; # 1 for values waiting bless $self, $class; # Create thread attached to this handle threads->create('start_thread', $self)->detach(); return $self; } sub DESTROY { my $self = shift; lock $self->{sem}; ${$self->{cmd}} = (); # Empty list to quit { lock $self->{cw}; ${$self->{cw}} = 1; cond_signal $self->{cw}; } { lock $self->{rw}; while (!${$self->{rw}}) { cond_wait $self->{rw}; } } } # Autoload section -- convert uppercase method names called to # lowercase method names for the underlying filehandle. our $AUTOLOAD; sub AUTOLOAD { my $self = shift; warn "Autoloaded $AUTOLOAD"; my $method = lc($AUTOLOAD); # Convert method to lc. $method =~ s/^.*:://; # Remove prefix. FileHandle->can($method) or die "Error: filehandles have no '$method' method"; my @ret; # Return value { lock $self->{sem}; # Lock access # Send command: warn "$AUTOLOAD: Sent $method(@_)"; @{$self->{cmd}} = ($method, @_); # Tell command thread that it is waiting: { lock $self->{cw}; ${$self->{cw}} = 1; cond_signal $self->{cw}; } # Wait for return values warn "$AUTOLOAD: waiting"; { lock $self->{rw}; while (!${$self->{rw}}) { warn "$AUTOLOAD: waiting for data"; cond_wait $self->{rw}; warn "$AUTOLOAD: done waiting"; } ${$self->{rw}} = 0; # Reset } # Handle errors if (my $err = ${$self->{err}}) { chomp $err; my($filename, $line) = (caller)[2,3]; die "$err at $filename line $line\n"; } @ret = @{$self->{rv}}; # Get return values } return @ret; # Return w/ return vals } # Method for thread that accesses underlying filehandle to loop # through commands sub cmdloop { my $self = shift; warn "Entered command loop"; my $fh = new FileHandle; while (1) { # Wait for and get command { warn "Waiting for command ready"; lock $self->{cw}; while (!${$self->{cw}}) { cond_wait $self->{cw}; } $self->{cw} = 0; # Reset } # Get the method and arguments my @cmd = @{$self->{cmd}} or last; # Quit on empty list my $method = shift @cmd; my @args = @cmd; # Call the method, enqueue the return value(s) my $mcall = "\$fh->$method(\@args)"; warn "sending \$fh->$method(@args)"; @{$self->{rv}} = eval $mcall; #@{$self->{rv}} = $fh->$method(@args); # Record errors ${$self->{err}} = $@; } continue { # Signal that values are waiting { warn "Signaling data available"; lock $self->{rw}; warn "locked 'rw' flag"; warn "'rw' flag is $self->{rw}"; ${$self->{rw}} = 1; warn "'rw' flag is $self->{rw}"; cond_signal $self->{rw}; warn "signalled 'rw' flag"; } } } 1;

      There is another, similar error at line 123 of the module: $self->{cw} = 0;    # Reset

      Which should be ${ $self->{cw} } = 0;    # Reset

      With that also fixed, the code does appear to run, and the shared file does receive output from both threads.

      It still exits with 2 threads still running, and there does not appear to be any mechanism code for dealing with that.

      It is intriguing code. Whomever wrote it knows a lot more about the cond_* calls than I do--but then I have made it my habit to avoid them.


      Examine what is said, not who speaks.
      Silence betokens consent.
      Love the truth but pardon error.
Re: Threaded perl (re: debian bug #203579)
by dave_the_m (Monsignor) on Feb 26, 2005 at 12:14 UTC
    As others have pointed out, the core of the problem is turning the lock object into a simple scalar. However, that still shouldn't cause perl to coredump. Turns out it was a bug in cond_signal(), reproducable as follows:
    use threads; use threads::shared; my $rw = 0; cond_signal $rw;
    I've now fixed this in bleedperl.

    As regards to the OP's code, I'd recommend avoiding low-level stuff like cond_wait()/cond_signal() where possible; they are very hard to get right and avoid deadlock. For example I suspect from a brief look at the code that there are are multiple race conditions. Instead, use something like Thread::Queue which takes away most of the pain about passing streams of commands or data between threads.

    Dave.

Re: Threaded perl (re: debian bug #203579)
by Corion (Patriarch) on Feb 26, 2005 at 08:37 UTC

    At least on my Perl 5.8.5 and Perl 5.9.2 (some recent sync), the segfault occurs under Win32 as well. The code you referenced is fairly long, so I don't directly see what might be causing this. The perl.com link you referred to does not seem to imply in any case that there was any bugfix - the section "Perl Threads" only describes the move from the really broken threading model to a model with different bugs.

    If Interchange relies on threads, I would not use it, but you might have other reasons that outweigh the pain of Perl threads.

    In any case, I think it would pay off to reduce this program to a small program of about 50 lines or something, and then report that bug via the perlbug utility, so the right people see it.

    Update: From the first looks, it seems like your threads are playing tag-team on the FILE - I don't think that this will really work, even if the crash is removed ...

Re: Threaded perl (re: debian bug #203579)
by BrowserUk (Patriarch) on Feb 26, 2005 at 08:38 UTC

    I don't understand how you conclude that this "seems to imply that it is fixed?????" ? (????)

    See Re^2: Threaded perl (re: debian bug #203579) for more info.


    Examine what is said, not who speaks.
    Silence betokens consent.
    Love the truth but pardon error.