diff --git a/bricks/sync/test.cc b/bricks/sync/test.cc index 889d1bb3..887ac553 100644 --- a/bricks/sync/test.cc +++ b/bricks/sync/test.cc @@ -349,3 +349,42 @@ TEST(WaitableAtomic, WaitFor) { EXPECT_FALSE(b2.WaitFor([](bool b) { return b; }, std::chrono::milliseconds(1))); } } + +TEST(WaitableAtomic, NotifiesOfChangesInWait) { + struct Obj final { + bool done = false; + int from = 0; + int into = 0; + }; + current::WaitableAtomic obj; + int extracted_into = 0; + + std::thread t([&obj, &extracted_into]() { + bool done = false; + while (!done) { + obj.Wait( + [](const Obj& o) { return o.done || o.from != 0; }, + [&done, &extracted_into](Obj& o) { + if (o.done) { + done = true; + } + if (o.from) { + // NOTE(dkorolev): This delay is 0.1ms, which is sufficient. I have tested the code with: + // ./.current/test --gtest_filter=WaitableAtomic.NotifiesOfChangesInWai --gtest_repeat=-1 + std::this_thread::sleep_for(std::chrono::microseconds(100)); + o.into = o.from; + o.from = 0; + extracted_into = o.into; + } + }); + } + }); + + EXPECT_EQ(extracted_into, 0); + obj.MutableScopedAccessor()->from = 1; + obj.Wait([](const Obj& o) { return o.from == 0; }); + EXPECT_EQ(extracted_into, 1); + + obj.MutableScopedAccessor()->done = true; + t.join(); +} diff --git a/bricks/sync/waitable_atomic.h b/bricks/sync/waitable_atomic.h index ad6efa4e..3ec060bc 100644 --- a/bricks/sync/waitable_atomic.h +++ b/bricks/sync/waitable_atomic.h @@ -214,17 +214,30 @@ class WaitableAtomic { // TODO(dkorolev): The `.Wait()` above always returning `true` could use some TLC. template - std::invoke_result_t Wait(std::function wait_predicate, F&& retval_predicate) { + std::invoke_result_t DoWait(std::function wait_predicate, F&& retval_predicate) { std::unique_lock lock(data_mutex_); if (!wait_predicate(data_)) { const data_t& data = data_; - data_condition_variable_.wait(lock, [&wait_predicate, &data] { return wait_predicate(data); }); + data_condition_variable_.wait(lock, [&wait_predicate, &data]() { return wait_predicate(data); }); return retval_predicate(data_); } else { return retval_predicate(data_); } } + template , void>>> + void Wait(std::function wait_predicate, F&& retval_predicate) { + DoWait(wait_predicate, std::forward(retval_predicate)); + Notify(); + } + + template , void>>> + std::invoke_result_t Wait(std::function wait_predicate, F&& retval_predicate) { + std::invoke_result_t retval = DoWait(wait_predicate, std::forward(retval_predicate)); + Notify(); + return retval; + } + #endif // CURRENT_FOR_CPP14 template