diff --git a/lib/monitor.toit b/lib/monitor.toit index 0559edf75..f7784e5ba 100644 --- a/lib/monitor.toit +++ b/lib/monitor.toit @@ -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 @@ -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. diff --git a/src/primitive_flash_registry.cc b/src/primitive_flash_registry.cc index 79aa000e1..6fa428924 100644 --- a/src/primitive_flash_registry.cc +++ b/src/primitive_flash_registry.cc @@ -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(); diff --git a/tests/monitor-test.toit b/tests/monitor-test.toit index fac27f4af..dca4ac2a5 100644 --- a/tests/monitor-test.toit +++ b/tests/monitor-test.toit @@ -31,6 +31,7 @@ run: test-process-messages-on-leave test-gate test-latch + test-signal monitor A: foo-ready := false @@ -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