diff --git a/lix/libutil/sync.hh b/lix/libutil/sync.hh index f0e6a9fc0..bdea4782f 100644 --- a/lix/libutil/sync.hh +++ b/lix/libutil/sync.hh @@ -1,10 +1,13 @@ #pragma once ///@file +#include "lix/libutil/async.hh" #include "lix/libutil/types.hh" #include #include #include +#include +#include #include #include #include @@ -232,6 +235,41 @@ public: *this = co_await s->lock(); } + + /** + * Releases the lock, waits for another promise to call `Sync::notify`, + * and reacquires the lock. If `timeout` elapses before another promise + * calls `notify` the lock is acquired again. Returns `true` if another + * promise called `notify` or `false` once the `notify` wait times out. + */ + kj::Promise waitFor(kj::Duration timeout) + { + auto * s = static_cast(this->s); + + { + auto unlock = std::move(*this); + } + + auto pfp = kj::newPromiseAndCrossThreadFulfiller(); + { + std::lock_guard clk(s->conditionMutex); + s->conditionWaiters.push_back(std::move(pfp.fulfiller)); + } + bool result = true; + try { + co_await AIO().provider.getTimer().timeoutAfter(timeout, std::move(pfp.promise)); + } catch (kj::Exception & e) { // NOLINT(lix-foreign-exceptions) + if (e.getType() == kj::Exception::Type::OVERLOADED) { + result = false; + } else { + // NOLINTNEXTLINE(lix-foreign-exceptions): these will be irrecoverable errors + throw; + } + } + + *this = co_await s->lock(); + co_return result; + } }; /**