Skip to content

Commit

Permalink
Fix a data race in Proc::Async.
Browse files Browse the repository at this point in the history
It is possible that the ready callback of the process will fire before
the code that binds the async process handle into `$!process_handle`
gets to run. This can happen if the thread that initiates the async
process call is suspended very soon after it has done so, and then
another thread gets the ready message. The result was that the permit
issuing code would run without `$!process_handle` being bound, and so
it would never issue the emit permit, and there would therefore be a
deadlock because the stdout and/or stderr handles were never read.
  • Loading branch information
jnthn committed Jun 28, 2017
1 parent 864fa72 commit 2a8d1e7
Showing 1 changed file with 3 additions and 1 deletion.
4 changes: 3 additions & 1 deletion src/core/Proc/Async.pm
Expand Up @@ -92,6 +92,7 @@ my class Proc::Async {

has $!ready_promise = Promise.new;
has $!ready_vow = $!ready_promise.vow;
has $!handle_available_promise = Promise.new;
has $!stdout_descriptor_vow;
has $!stderr_descriptor_vow;
has $!stdout_descriptor_used = Promise.new;
Expand Down Expand Up @@ -129,7 +130,7 @@ my class Proc::Async {
}

method !pipe-cbs(\channel) {
-> { $!ready_promise.then({ nqp::permit($!process_handle, channel, -1) }) },
-> { $!handle_available_promise.then({ nqp::permit($!process_handle, channel, -1) }) },
-> { (channel == 1 ?? $!stdout_descriptor_used !! $!stderr_descriptor_used).keep(True) }
}

Expand Down Expand Up @@ -341,6 +342,7 @@ my class Proc::Async {
CLONE-HASH-DECONTAINERIZED(%ENV),
$callbacks,
);
$!handle_available_promise.keep(True);
nqp::permit($!process_handle, 0, -1) if $!merge_supply;
Promise.allof( $!exit_promise, @!promises ).then({
.close for @!close-after-exit;
Expand Down

0 comments on commit 2a8d1e7

Please sign in to comment.