Proc::Q
NAME
Proc::Q - Queue up and run a herd of Procs
SYNOPSIS
use Proc::Q;
# Run 26 procs; each receiving stuff on STDIN and putting stuff out
# to STDOUT, as well as sleeping for increasingly long periods of
# time. The timeout of 3 seconds will kill all the procs that sleep
# longer than that.
my @stuff = 'a'..'z';
my $proc-chan = proc-q
@stuff.map({«perl6 -e "print '$_' ~ \$*IN.slurp; sleep $($++/5)"»}),
tags => @stuff.map('Letter ' ~ *),
in => @stuff.map(*.uc),
timeout => 3;
react whenever $proc-chan {
say "Got a result for {.tag}: STDOUT: {.out}"
~ (". Killed due to timeout" if .killed)
}
# OUTPUT:
# Got a result for Letter a: STDOUT: aA
# Got a result for Letter b: STDOUT: bB
# Got a result for Letter c: STDOUT: cC
# Got a result for Letter d: STDOUT: dD
# Got a result for Letter e: STDOUT: eE
# Got a result for Letter f: STDOUT: fF
# Got a result for Letter g: STDOUT: gG
# Got a result for Letter h: STDOUT: hH
# Got a result for Letter i: STDOUT: iI
# Got a result for Letter j: STDOUT: jJ
# Got a result for Letter k: STDOUT: kK
# Got a result for Letter l: STDOUT: lL
# Got a result for Letter m: STDOUT: mM
# Got a result for Letter n: STDOUT: nN
# Got a result for Letter o: STDOUT: oO. Killed due to timeout
# Got a result for Letter p: STDOUT: pP. Killed due to timeout
# Got a result for Letter s: STDOUT: sS. Killed due to timeout
# Got a result for Letter t: STDOUT: tT. Killed due to timeout
# Got a result for Letter v: STDOUT: vV. Killed due to timeout
# Got a result for Letter w: STDOUT: wW. Killed due to timeout
# Got a result for Letter q: STDOUT: qQ. Killed due to timeout
# Got a result for Letter r: STDOUT: rR. Killed due to timeout
# Got a result for Letter u: STDOUT: uU. Killed due to timeout
# Got a result for Letter x: STDOUT: xX. Killed due to timeout
# Got a result for Letter y: STDOUT: yY. Killed due to timeout
# Got a result for Letter z: STDOUT: zZ. Killed due to timeout
DESCRIPTION
Requires Rakudo 2017.06 or newer.
Got a bunch of Procs you want to queue up and run, preferably with some timeout for Procs that get stuck? Well, good news!
EXPORTED SUBROUTINES AND TYPES
proc-q
Defined as:
sub proc-q(
+@commands where .so && .all ~~ List & .so,
:@tags where .elems == @commands = @commands,
:@in where {
.elems == @commands|0
and all .map: {$_ ~~ Cool:D|Blob:D|Nil or $_ === Any}
} = (Nil xx @commands).List,
Numeric :$timeout where .DEFINITE.not || $_ > 0,
UInt:D :$batch where .so = 8,
:$out where Bool:D|'bin' = True,
:$err where Bool:D|'bin' = True,
Bool:D :$merge where .not | .so & (
$out & $err & (
($err eq 'bin' & $out eq 'bin')
| ($err ne 'bin' & $out ne 'bin'))) = False,
--> Channel:D
)
See SYNOPSIS for sample use.
Returns a Channel of Proc::Q::Res
objects. Batches the @commands
in batches of $batch
and runs those via in parallel, optionally feeding STDIN with corresponding data from @in
, as well as capturing STDOUT/STDERR, and killing the process after $timeout
, if specified.
Arguments are as follows:
+@commands
A list of lists, where each of inner lists is a list of arguments to Proc::Async.new. You do not need to specify the :w
argument, and if you do, its value will be ignored.
Must have at least one list of commands inside @commands
.
:@tags
To make it possible to match the input with the output, you can tag
each of the commands in @commands
by specifying the value via @tags
argument at the same index as the command is at. The given tag will be available via .tag
method of the Proc::Q::Res
object responsible.
Any object can be used as a tag. If <:@tags> is provided, it must have the same number of elements as +@commands
argument. If it's not provided, it defaults to @commands
.
:@in
Optionally, you can send stuff to STDIN of your procs, by giving a Blob
or Str
in :@in
arg at the same index as the the index of the command for that proc in @commands
. If specified, the number of elements in @in
must be the same as number of elements in @commands
. Specify undefined value to avoid sending STDIN to a particular proc.
TIP: is your queue hanging for some reason? Ensure the procs you're running arent's sitting and waiting for STDIN. Try passing an empty strings in :@in
.
:$batch
Takes a positive Int
. Defaults to 8
. Specifies how many @commands
to run at the same time.
:$timeout
By default is not specified.
Takes a positive Numeric
specifying the number of seconds after which a proc should be killed, if it did not complete yet. Timer starts ticking once the proc is .ready. The process is killed with SIGTERM
signal and if after 1 second it's still alive, it gets another kill with SIGSEGV
.
:$out
Defaults to True
.
If set to True
or string 'bin'
, the routine will capture STDOUT from the procs, and make it available in .out
method of Proc::Q::Res
object. If set to string <'bin'>, the output will be captured in binary and .out
method will contain a Blob
instead of Str
.
:$err
Same as :$out
except as applied to procs' STDERR.
:$merge
Defaults to False
.
If set to True
, both :$err
and :$out
must be set to True
or both set to string 'bin'
.
If set to True
, the .merged
method will contain the merged output of STDOUT and STDERR (so it'll be a Str
or, if the :$out
/:$err
arei set to 'bin'
, a Blob
).
Note that there's no order guarantee. Output from a proc sent to STDERR after output to STDOUT, might end up before STDOUT's data in .merged
object.
Proc::Q::Res
Each of the item sent to the Channel
from proc-q
routine will be a Proc::Q::Res
object (technically, it might also be an Exception
object if something explodes while trying to launch and wait for a proc, but it's of the "should never happen" variety; the Exception
will be the reason why stuff exploded).
While the @commands
to be executed will be batched in :$batch
items, the order within batches is not guaranteed. Use :@tags
to match the Proc::Q::Res
to the input commands.
The Proc::Q::Res
type contains information about the proc that was ran and provides these methods:
.tag
The same object that was given as a tag via :@tags
argument (by default, the command from @commands
that was executed). The purpose of the .tag
is to match this Proc::Q::Res
object to the proc you ran.
.out
Contains a Stringy
with STDOUT of the proc if :$out
argument to proc-q
is set to a true value.
.err
Contains a Stringy
with STDERR of the proc if :$err
argument to proc-q
is set to a true value.
.merged
Contains a Stringy
with merged STDOUT and STDERR of the proc if :$merge
argument to proc-q
is set to a true value. Note that even when :$merge
is in use, the .out
and .err
methods will contain the separated streams.
.exitcode
Contains the exit code of the executed proc.
.killed
A Bool:D
that is True
if this proc was killed due to the :$timeout
. More precisely, this is an indication that the timeout expired and the kill code started to run. It is possible for a proc to successfully complete in this small window opportunity between the attribute being set and the signal from .kill being received by the process.
AUTHOR
Zoffix Znet
COPYRIGHT AND LICENSE
Copyright 2017 - 2018 Zoffix Znet
Copyright 2019 - 2022 Raku Community
This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.