Skip to content

Commit

Permalink
Avoid getting stuck forever on monitor.Signal (#1976)
Browse files Browse the repository at this point in the history
  • Loading branch information
kasperl authored Nov 29, 2023
1 parent b042b53 commit 23675ce
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 8 deletions.
18 changes: 11 additions & 7 deletions lib/monitor.toit
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ A signal synchronization primitive.
This class must not be extended.
*/
monitor Signal:
waiters_ /int := 0
current_ /int := 0
awaited_ /int := 0

Expand Down Expand Up @@ -169,17 +170,20 @@ monitor Signal:

// Helper method for condition waiting.
wait_ [condition] -> none:
while true:
awaited := awaited_
if current_ == awaited:
waiters_++
try:
while true:
awaited := awaited_
awaited_ = ++awaited
await: current_ >= awaited
if condition.call: return
finally:
if waiters_-- == 1:
// No other task is waiting for this signal to be raised,
// so it is safe to reset the counters. This helps avoid
// the ever increasing counter issue that may lead to poor
// performance in (very) extreme cases.
current_ = awaited = 0
awaited_ = ++awaited
await: current_ >= awaited
if condition.call: return
current_ = awaited_ = 0

/**
A synchronization gate.
Expand Down
5 changes: 4 additions & 1 deletion src/primitive_flash_registry.cc
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,10 @@ PRIMITIVE(grant_access) {
if (!grant) FAIL(MALLOC_FAILED);
Locker locker(OS::global_mutex());
for (auto it : grants) {
if (it->offset() == offset && it->size() == size) FAIL(ALREADY_IN_USE);
if (it->offset() == offset && it->size() == size) {
delete grant;
FAIL(ALREADY_IN_USE);
}
}
grants.prepend(grant);
return process->null_object();
Expand Down
35 changes: 35 additions & 0 deletions tests/monitor-test.toit
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ run:
test-process-messages-on-leave
test-gate
test-latch
test-signal

monitor A:
foo-ready := false
Expand Down Expand Up @@ -487,6 +488,40 @@ test-latch:
l3.set --exception exception
expect-throw 99: l3.get

test-signal:
done := Semaphore
order := []
signal := Signal
t0 := false
t1 := false
t2 := false
task::
signal.wait: t0
order.add 0
done.up
task::
signal.wait: t1
order.add 1
done.up
// Make sure we can raise the signal here without making
// progress and without getting any of the tasks already
// waiting stuck.
signal.raise
task::
signal.wait: t2
order.add 2
done.up
t0 = true
signal.raise
done.down
t1 = true
signal.raise
done.down
t2 = true
signal.raise
done.down
expect-list-equals [0, 1, 2] order

test-semaphore:
semaphore := Semaphore
expect-equals 0 semaphore.count
Expand Down

0 comments on commit 23675ce

Please sign in to comment.