Search code examples
perlforkipc

Store multiple items in IPC::ShareLite object


I have a Perl program that forks subprocesses and processes N items in them, after that I need to return these processed items to main process.

From multiple available IPC options to return processed items to main process, IPC::ShareLite seems to be a simplest one, but I'm not sure if it supports storing multiple items in one shared object.

Here is a snippet from the program, but it doesn't work:

use IPC::ShareLite;

# create shared object in main process
my $share = new IPC::ShareLite(
    -key     => 1234,
    -create  => 'yes',
    -destroy => 'yes'
) or die $!;

# fork subprocesses, process and store N items in shared object
$share->store($member);

# After subprocesses finish, fetch items in main process
my $members_size = scalar @$members_ref;
@$members_ref = ();
while ($members_size > 0) {
    my $member = $share->fetch();
    push @$members_ref, $member;
    $members_size--;
}

Above code fails with quite obscure error on my $member = $share->fetch();:

IPC::ShareLite fetch() error: Invalid argument at ...

Is it possible to use IPC::ShareLite object as I intend or it can hold only one item?


Solution

  • You are creating a child using fork, and this child gets a copy of the $share object. When the child exits, the child's copy of the $share object is destroyed, which causes the underlying system resources to be destroyed because you used -destroy => 'yes'.

    In general, you want to create objects with destructors after performing the forks.

    But when creating a IPC::ShareLite object, you normally want to leave key to let the system choose the key for you, but you need to do that before creating the forks.

    Workaround

    Perform the following in the child:

    $share->destroy(0);
    

    A better solution

    Since the a normal usage mode for the module involves creating an object inherited by child processes, the module should handle that situation.

    To that end, you should have the module's maintainer change the module so that the destruction only occurs if the current PID is the same as the PID in which $share was created.

    sub _initialize {
      ...
      $self->{pid} = $$;   # ADD
      ...
    }
    
    sub DESTROY {
      my $self = shift;
    
      #destroy_share( $self->{share}, $self->{destroy} )                           # REMOVE
      destroy_share( $self->{share}, $self->{pid} == $$ ? $self->{destroy} : 0 )   # ADD
       if $self->{share};
    }