Kamrul Khan
Kamrul Khan

Reputation: 3350

Perl seg fault while joining threads

I have a code similar to the below. I have one main script which is calling another module named initial.pm. initial.pm opens up connection with an AMQP server (In my case RabbitMQ)and using Net::AMQP::RabbitMQ library for establishing the connection. Everything works fine except when I try to join my threads I get segmentation fault.

I think the Net::AMQP::RabbitMQ is not thread safe. But this is only being used by the main thread. Im pretty sure you can reproduce the error if you just copy and past the codes below.

How do I fix it ?

main.pl

#!/usr/bin/perl
use Cwd qw/realpath/;
use File::Basename qw/dirname/;
use lib 'lib';
use threads;
use threads::shared;
use initial;

my @threads = ();
my $run :shared = 1;

my $init = load initial($name);

$SIG{'TERM'} = sub {
    $run = 0;
};

threads->create(\&proc1);
threads->create(\&proc2);


while($run){
    sleep(1);
    print "I am main thread\n";
}

$_->join() for threads->list();

sub proc1 {
    while($run){
        sleep(1);
        print "I am child thread 1 \n"
    }
}

sub proc2 {
    while($run){
       sleep(1);
       print "I am child thread 2 \n";
    }
}

lib/initial.pm

package initial;

use Net::AMQP::RabbitMQ;
use Cwd qw/realpath/;
use File::Basename qw/dirname/;

my $mq;
my $stop = 0;

sub load {
    my $class = shift;
    my $self = {};
    connectq();
    bless $self,$class;
    return $self;
}

sub connectq {
    $mq = Net::AMQP::RabbitMQ->new();
    my ($host,$port,$user,$pass) = ('localhost','5672','guest','guest');
    $mq->connect($host, {
        user => $user,
        password => $pass,
        port => $port,
        timeout => 10,
    });

    $mq->channel_open(1);
    $mq->consume(1, 'logger');
}

1;

Upvotes: 2

Views: 805

Answers (1)

Sobrique
Sobrique

Reputation: 53478

I can't reproduce your problem directly, because I don't have the library installed.

One way of 'faking' thread safety in a not-thread-safe module is to rescope your 'use' to only the bit where you'll be using it.

You see, when you start a thread, it copies the program state - loaded libraries and everything.

If your run (something like):

#!/usr/bin/env perl

use strict;
use warnings;

use XML::Twig;
use Data::Dumper;

sub thread1 {
    print threads->self->tid.": Includes:", Dumper \%INC,"\n";
}


#main;

print "Main includes:", Dumper \%INC,"\n";
threads -> create ( \&thread1 );

You'll see XML::Twig is loaded in both. If the process of 'loading' the module causes some state changes (and it can) then you immediately have a potential thread-safety issue.

However if you instead do:

#!/usr/bin/env perl

use strict;
use warnings;

use threads;
use Data::Dumper;

sub thread1 {
    require XML::Twig;
    XML::Twig -> import;
    print threads->self->tid.": Includes:", Dumper (\%INC),"\n";
}


#main;

print "Main includes:", Dumper (\%INC),"\n";
threads -> create ( \&thread1 );
foreach my $thr ( threads -> list ) { 
   $thr -> join;
}

You effectively cause the module to be dynamically loaded within the thread - the module is only present in one 'code instance' so you are much less likely to be tripped up by 'thread safety' issues.

Alternatively - forking instead of threading ... might be an alternative. This has slightly different 'safety' problems.

But there really is no way to avoid this. Even with shared variables, the core problem is - when you thread, bits of code happen in a different order. There's all sorts of fruity things that can happen as a result. A shared var is one way of ensuring it's the same variable being checked each time - e.g. share $init, but that may well make things worse, because you're then potentially trampling over the same instance/sockets with different threads.

You can, however, reduce the 'thread safety' problem to a limited scope, and use e.g. Thread::Queue to pass messages to/from your 'module user' thread.

Upvotes: 1

Related Questions