From 0bb0f43aae2d370a5f905ffc7803db7bf8b29195 Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Wed, 5 Mar 2025 16:42:11 +0100 Subject: [PATCH] libutil: add AsyncMutex lock waits this is like a condition variable associated with the mutex. it's all we'll need for the async transition, so we don't add a separate type. Change-Id: Id9aefadd3b50e5f14f7b4d6bad41721f5249ce59 --- lix/libutil/sync.hh | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) 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();