Skip to content

Commit

Permalink
Merge pull request #2842 from Kaiepi/lock-async
Browse files Browse the repository at this point in the history
Document missing Lock::Async methods
Thanks!
  • Loading branch information
JJ committed Jun 9, 2019
2 parents b394e9d + c557b5f commit d17da25
Showing 1 changed file with 110 additions and 32 deletions.
142 changes: 110 additions & 32 deletions doc/Type/Lock/Async.pod6
Expand Up @@ -15,7 +15,7 @@ exclusion mechanism, C<Lock::Async> works with the high-level concurrency
features of Perl 6. The C<lock> method returns a C<Promise>, which will
be kept when the lock is available. This C<Promise> can be used with
non-blocking C<await>. This means that a thread from the thread pool need
not be consumed while waiting for the C<Async::Lock> to be available,
not be consumed while waiting for the C<Lock::Async> to be available,
and the code trying to obtain the lock will be resumed once it is available.
The result is that it's quite possible to have many thousands of outstanding
Expand All @@ -37,6 +37,51 @@ and L<Supply|/type/Supply>.
=head1 Methods
=head2 method lock
Defined as:
method lock(Lock::Async:D: --> Promise:D)
Returns a L<Promise|/type/Promise> that will be kept when the lock is
available. In the case that the lock is already available, an already
kept C<Promise> will be returned. Use C<await> to wait for the lock to
be available in a non-blocking manner.
my $l = Lock::Async.new;
await $l.lock;
Prefer to use L<protect|/type/Lock/Async#method_protect> instead of
explicit calls to C<lock> and C<unlock>.
=head2 method unlock
Defined as:
method unlock(Lock::Async:D: --> Nil)
Releases the lock. If there are any outstanding C<lock> C<Promise>s,
the one at the head of the queue will then be kept, and potentially
code scheduled on the thread pool (so the cost of calling C<unlock>
is limited to the work needed to schedule another piece of code that
wants to obtain the lock, but not to execute that code).
my $l = Lock::Async.new;
await $l.lock;
$l.unlock;
Prefer to use L<protect|/type/Lock/Async#method_protect> instead of
explicit calls to C<lock> and C<unlock>. However, if wishing to use
the methods separately, it is wise to use a C<LEAVE> block to ensure
that C<unlock> is reliably called. Failing to C<unlock> will mean that
nobody can ever C<lock> this particular C<Lock::Async> instance again.
my $l = Lock::Async.new;
{
await $l.lock;
LEAVE $l.unlock;
}
=head2 method protect
Defined as:
Expand Down Expand Up @@ -79,50 +124,83 @@ thus they don't actually protect the code we want to protect.
}
}
=head2 method lock
=head2 method protect-or-queue-on-recursion
Defined as:
method lock(Lock::Async:D: --> Promise:D)
method protect-or-queue-on-recursion(Lock::Async:D: &code)
When calling L<protect|/type/Lock/Async#method_protect> on a C<Lock::Async>
instance that is already locked, the method is forced to block until the lock
gets unlocked. C<protect-or-queue-on-recursion> avoids this issue by either
behaving the same as L<protect|/type/Lock/Async#method_protect> if the lock is
unlocked or the lock was locked by something outside the caller chain,
returning C<Nil>, or queueing the call to C<&code> and returning a C<Promise>
if the lock had already been locked at another point in the caller chain.
my Lock::Async $lock .= new;
my Int $count = 0;
# The lock is unlocked, so the code runs instantly.
$lock.protect-or-queue-on-recursion({
$count++
});
# Here, we have caller recursion. The outer call only returns a Promise
# because the inner one does. If we try to await the inner call's Promise
# from the outer call, the two calls will block forever since the inner
# caller's Promise return value is just the outer's with a then block.
$lock.protect-or-queue-on-recursion({
$lock.protect-or-queue-on-recursion({
$count++
}).then({
$count++
})
});
# Here, the lock is locked, but not by anything else on the caller chain.
# This behaves just like calling protect would in this scenario.
for 0..^2 {
$lock.protect-or-queue-on-recursion({
$count++;
});
}
Returns a L<Promise|/type/Promise> that will be kept when the lock is
available. In the case that the lock is already available, an already
kept C<Promise> will be returned. Use C<await> to wait for the lock to
be available in a non-blocking manner.
say $count; # OUTPUT: 5
my $l = Lock::Async.new;
await $l.lock;
=head2 method with-lock-hidden-from-recursion-check
Prefer to use L<protect|/type/Lock/Async#method_protect> instead of
explicit calls to C<lock> and C<unlock>.
Defined as:
=head2 method unlock
method with-lock-hidden-from-recursion-check(&code)
Defined as:
Temporarily resets the Lock::Async recursion list so that it no longer includes
the lock this method is called on and runs the given C<&code> immediately if
the call to the method occurred in a caller chain where
L<protect-or-queue-on-recursion|/type/Lock/Async/#method_protect-or-queue-on-recursion>
has already been called and the lock has been placed on the recursion list.
method unlock(Lock::Async:D: --> Nil)
my Lock::Async $lock .= new;
my Int $count = 0;
Releases the lock. If there are any outstanding C<lock> C<Promise>s,
the one at the head of the queue will then be kept, and potentially
code scheduled on the thread pool (so the cost of calling C<unlock>
is limited to the work needed to schedule another piece of code that
wants to obtain the lock, but not to execute that code).
$lock.protect-or-queue-on-recursion({
my Int $count = 0;
my $l = Lock::Async.new;
await $l.lock;
$l.unlock;
# Runs instantly.
$lock.with-lock-hidden-from-recursion-check({
$count++;
});
Prefer to use L<protect|/type/Lock/Async#method_protect> instead of
explicit calls to C<lock> and C<unlock>. However, if wishing to use
the methods separately, it is wise to use a C<LEAVE> block to ensure
that C<unlock> is reliably called. Failing to C<unlock> will mean that
nobody can ever C<lock> this particular C<Lock::Async> instance again.
# Runs after the outer caller's protect-or-queue-on-recursion call has
# finished running.
$lock.protect-or-queue-on-recursion({
$count++;
}).then({
say $count; # OUTPUT: 2
});
my $l = Lock::Async.new;
{
await $l.lock;
LEAVE $l.unlock;
}
say $count; # OUTPUT: 1
});
=end pod

Expand Down

0 comments on commit d17da25

Please sign in to comment.