diff --git a/lix/libutil/sync.hh b/lix/libutil/sync.hh index a6d9f4c0b..7b051801f 100644 --- a/lix/libutil/sync.hh +++ b/lix/libutil/sync.hh @@ -168,6 +168,9 @@ private: std::mutex waitMutex; std::list>> waiters; + std::mutex conditionMutex; + std::list>> conditionWaiters; + public: Sync() = default; Sync(T && data) : base_type(std::move(data)) {} @@ -198,8 +201,45 @@ public: } using base_type::Lock::operator->, base_type::Lock::operator*; + + /** + * Releases the lock, waits for another promise to call `Sync::notify`, + * and reacquires the lock. There is no `condition_variable`-equivalent + * object to allow multiple wait queues on the same lock since we don't + * need that yet. There's no reason not to add such a type when needed. + */ + kj::Promise wait() + { + 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)); + } + co_await pfp.promise; + + *this = co_await s->lock(); + } }; + /** + * Notify all promises awaiting `Lock::wait`. There is no `notify_one` like + * `std::condition_variable` provides owing to implementation complexities. + */ + void notify() + { + std::lock_guard clk(conditionMutex); + for (auto & f : conditionWaiters) { + f->fulfill(); + } + conditionWaiters.clear(); + } + auto lockSync(NeverAsync = {}) { return base_type::lock();