Skip to content

Commit

Permalink
Switch Promise to use cond var, not JVM semaphore.
Browse files Browse the repository at this point in the history
A condition variable is a much better construct to be using here,
especially when a semaphore granting many permits is not portably
available.
  • Loading branch information
jnthn committed Mar 8, 2014
1 parent 381bf1b commit 67935fd
Showing 1 changed file with 21 additions and 15 deletions.
36 changes: 21 additions & 15 deletions src/vm/jvm/core/Promise.pm
Expand Up @@ -17,15 +17,14 @@ my class Promise {
has $.status;
has $!result;
has int $!vow_taken;
has Mu $!ready_semaphore;
has $!lock;
has $!cond;
has @!thens;

submethod BUILD(:$!scheduler = $*SCHEDULER) {
my $interop := nqp::jvmbootinterop();
my \Semaphore := $interop.typeForName('java.util.concurrent.Semaphore');
$!ready_semaphore := Semaphore.'constructor/new/(I)V'(-1);
$!lock := nqp::create(Lock);
$!cond := $!lock.condition();
$!status = Planned;
}

Expand Down Expand Up @@ -61,10 +60,12 @@ my class Promise {
}

method !keep(\result) {
$!result := result;
$!status = Kept;
$!ready_semaphore.'method/release/(I)V'(32768);
self!schedule_thens();
$!lock.protect({
$!result := result;
$!status = Kept;
self!schedule_thens();
$!cond.signal_all;
});
$!result
}

Expand All @@ -73,26 +74,31 @@ my class Promise {
}

method !break($result) {
$!result = $result ~~ Exception ?? $result !! X::AdHoc.new(payload => $result);
$!status = Broken;
$!ready_semaphore.'method/release/(I)V'(32768);
self!schedule_thens();
$!lock.protect({
$!result = nqp::istype($result, Exception)
?? $result
!! X::AdHoc.new(payload => $result);
$!status = Broken;
self!schedule_thens();
$!cond.signal_all;
});
}

method !schedule_thens() {
nqp::lock($!lock);
while @!thens {
$!scheduler.cue(@!thens.shift, :catch(@!thens.shift))
}
nqp::unlock($!lock);
}

method result(Promise:D:) {
# One important missing optimization here is that if the promise is
# not yet started, then the work can be done immediately by the
# thing that is blocking on it.
if $!status == Planned {
$!ready_semaphore.'method/acquire/()V'();
$!lock.protect({
# Re-check planned to avoid data race.
$!cond.wait() if $!status == Planned;
});
}
if $!status == Kept {
$!result
Expand All @@ -116,7 +122,7 @@ my class Promise {

method then(Promise:D: &code) {
nqp::lock($!lock);
if $!status == any(Broken, Kept) {
if $!status == Broken | Kept {
# Already have the result, start immediately.
nqp::unlock($!lock);
Promise.start( { code(self) }, :$!scheduler);
Expand Down

0 comments on commit 67935fd

Please sign in to comment.