Parallel::Runner(3pm)
π Parallel::Runner π
okay so bestie, gather round β this is the page for Parallel::Runner, and yes it lives in the User Contributed Perl Documentation section of your man pages. Perl module realness. Let's get into it π«Ά
β¨ NAME β¨
Parallel::Runner β an object to manage running things in parallel processes. That's the whole vibe. That's the assignment. π
πΈ DESCRIPTION πΈ
okay so first thing, no cap: there are several other modules that do this, and you probably want one of them. Like, genuinely. This one exists as a super specialised parallel task manager. It's giving niche. It's giving purpose-built.
So here's how it goes: you make the object with a process limit, and you hand it callbacks β one for what to do while it's waiting for a free process slot, and one for what a process should do right before it exits.
π¨ CRITICAL, bestie, listen to me: you must explicitly call $runner->finish() when you're done. If the runner gets destroyed before its children are finished, a warning will be generated and your child processes will be killed β by force if necessary. Not a drill. π
And here's a fun little quirk β¨: if you specify a maximum of 1, then no forking will occur, and run() will block until the coderef returns. But β plot twist β you can force a fork by giving a boolean true value as the second argument to run(). That makes the runner fork before running the coderef, however run() will still block until the child exits. So like. It forks. But it also waits. Iconic behaviour, honestly. π
π¦ SYNOPSYS π¦
(yes, it's spelled like that in the original, we don't fix it, we respect it π)
#!/usr/bin/perl
use strict;
use warnings;
use Parallel::Runner;
my $runner = Parallel::Runner->new(4);
$runner->run( sub { ... } );
$runner->run( sub { ... } );
$runner->run( sub { ... } );
$runner->run( sub { ... } );
# This will block until one of the previous 4 finishes
$runner->run( sub { ... } );
# Do not forget this.
$runner->finish;
That # Do not forget this. comment? That's not decoration. That's a threat. /lh π
π CONSTRUCTOR π
$runner = $class->new( $max, $accessor => $value, ... );
Creates a new instance of Parallel::Runner.
$accessorcan be anything listed under the ACCESSORS section. Go wild.$maxshould be the maximum number of processes allowed β and it defaults to1.
π ACCESSORS π
These are simple accessors, bestie. Pass an argument β sets the accessor to that argument. Pass nothing β it just returns the current value. Simple as. π«Ά
$val = $runner->data_callback( \&callback )β if this is specified then IPC will be automatically enabled, and the final return from each process will be passed into this handler in the main process. Now, because of the way IPC works, only strings/numerical data is passed. If you need to pass a ref you'll have to serialize it yourself before returning it, and then deserialize it in your callback. No shortcuts, sorry babe. π
Example:
# Place to put the accumulated data
my @accum_data;
# Create the runner with a callback that pushes the data onto our array.
$runner = $CLASS->new( 2,
data_callback => sub {
my ($data) = @_;
push @accum_data => $data;
},
);
# 4 processes that return data
$runner->run( sub { return "foo" });
$runner->run( sub { return "bar" });
$runner->run( sub { return "baz" });
$runner->run( sub { return "bat" });
$runner->finish;
# Verify the data (order is not predictable)
is_deeply(
[ sort @accum_data ],
[ sort qw/foo bar baz bat/ ],
"Got all data returned by subprocesses"
);
note the "order is not predictable" β do not come for me about ordering, it's literally in the original π
$val = $runner->exit_callback( \&callback )β codref to call just before a child exits (called within child). Yes it says "Codref". Yes we're keeping it. β¨$val = $runner->iteration_delay( $float )β how long to wait per iterate if nothing has changed. Chill lil pause. π$val = $runner->iteration_callback( $newval )β coderef to call multiple times in a loop whilerun()is blocking, waiting for a process slot. It's the "are we there yet" callback fr fr.$val = $runner->reap_callback( $newval )β codref to call whenever a pid is reaped usingwaitpid. The callback sub will be passed 3 values β okay so, technically it lists four, we're just relaying the lore faithfully:- The exit status of the child process.
- The pid of the child process.
- This one used to be the return of
waitpid, but that's deprecated asChildis now used and throws an exception whenwaitpidis not what it should be. So the third is simply the pid of the child process again. - The final argument is the child process object itself.
$runner->reap_callback( sub {
my ( $status, $pid, $pid_again, $proc ) = @_;
# Status as returned from system, so 0 is good, 1+ is bad.
die "Child $pid did not exit 0"
if $status;
});
status 0 = slay, status 1+ = not slay. π
@children = $runner->children( @append )β returns a list ofChild::Link::Procobjects. Look at your little babies. π₯Ή$val = $runner->pid()β pid of the parent process.$val = $runner->max( $newval )β maximum number of children.
π₯ OBJECT METHODS π₯
run( $code )
run( $code, $force_fork )
Run the specified code in a child process. Blocks if no free slots are available. Force fork can be used to force a fork when max is 1 β however, it will still block until the child exits. (Same energy as before. It forks. It waits. Both things are true. π¦)
finish()
finish( $timeout )
finish( $timeout, $timeoutcallback )
Wait for all children to finish, then clean up after them. If a timeout is specified it will return after the timeout regardless of wether or not children have all exited. If there's a timeout callback then that code will be run upon timeout, just before the method returns.
π¨π¨ NOTE: DO NOT LET YOUR RUNNER BE DESTROYED BEFORE FINISH COMPLETES WITHOUT A TIMEOUT. π¨π¨ the runner will kill all children, possibly with force, if your runner is destroyed with children still running, or not waited on. This is the part where you do not play games. π
killall( $sig )
Send all children the specified kill signal. π
DESTROY()
Automagically called when the object is destroyed. If it's called while children are running, it will forcefully clean up after you as follows:
- Sends an ugly warning. π
- Will first give all your children
1second to complete.
Windows) Strawberry fails with processes, so on windows DESTROY will wait as long as needed, possibly forever. π«
- Sends kill signal
15to all children then waits up to4seconds. - Sends kill signal
9to any remaining children then waits up to10seconds. - Gives up and returns.
step 5 is genuinely just "gives up and returns" and honestly? relatable. π
π§ FENNEC PROJECT π§
This module is part of the Fennec project. See Fennec for more details. Fennec is a project to develop an extendable and powerful testing framework. Together the tools that make up the Fennec framework provide a potent testing environment. β¨
The tools provided by Fennec are also useful on their own. Sometimes a tool created for Fennec is useful outside the greater framework. Such tools are turned into their own projects. This is one such project. π«Ά
- Fennec - The core framework
- The primary Fennec project that ties them all together.
π AUTHORS π
Chad Granum exodist7@gmail.com
πΈ COPYRIGHT πΈ
Copyright (C) 2010 Chad Granum
Parallel-Runner is free software; Standard perl licence.
Parallel-Runner is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
and that's the whole page, bestie, you ate that π
this was Parallel::Runner(3pm) from perl v5.38.2, dated 2024-05-08 β mwah, go call finish() on your runners, ily, bye πβ¨π¦π
π¦ where this page comes from
- debian trixie: libparallel-runner-perl 0.014-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libparallel-runner-perl 0.014-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libparallel-runner-perl 0.014-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms