@@ -441,3 +441,98 @@ TEST(WhenAll, ConcurrentResolution) {
441441 t4.join ();
442442 t5.join ();
443443}
444+
445+ // Verifies that WaitForResult returns nullopt when the promise is never
446+ // resolved within the timeout.
447+ TEST (Promise, WaitForResultTimeout) {
448+ Promise<int > promise;
449+ Future<int > future = promise.GetFuture ();
450+
451+ auto result = future.WaitForResult (std::chrono::milliseconds (50 ));
452+ EXPECT_FALSE (result.has_value ());
453+ }
454+
455+ // Verifies that multiple threads can concurrently register continuations on
456+ // the same future before it resolves, and all continuations run after
457+ // resolution.
458+ TEST (Promise, ConcurrentThenRegistration) {
459+ Promise<int > promise;
460+ Future<int > future = promise.GetFuture ();
461+
462+ std::atomic<int > count{0 };
463+ std::vector<std::thread> threads;
464+ std::vector<Future<std::monostate>> results;
465+
466+ for (int i = 0 ; i < 10 ; i++) {
467+ results.push_back (future.Then (
468+ [&count](int const &) {
469+ count++;
470+ return std::monostate{};
471+ },
472+ [](Continuation<void ()> f) { f (); }));
473+ }
474+
475+ promise.Resolve (42 );
476+
477+ for (auto & r : results) {
478+ ASSERT_TRUE (r.WaitForResult (std::chrono::seconds (5 )).has_value ());
479+ }
480+ EXPECT_EQ (count, 10 );
481+ }
482+
483+ // Verifies that when multiple threads race to resolve the same promise,
484+ // exactly one succeeds and the rest return false.
485+ TEST (Promise, ConcurrentResolveSingleWinner) {
486+ Promise<int > promise;
487+ Future<int > future = promise.GetFuture ();
488+
489+ std::atomic<int > winners{0 };
490+ std::vector<std::thread> threads;
491+ for (int i = 0 ; i < 10 ; i++) {
492+ threads.emplace_back ([&promise, &winners, i] {
493+ if (promise.Resolve (i)) {
494+ winners++;
495+ }
496+ });
497+ }
498+
499+ for (auto & t : threads) {
500+ t.join ();
501+ }
502+
503+ EXPECT_EQ (winners, 1 );
504+ EXPECT_TRUE (future.GetResult ().has_value ());
505+ }
506+
507+ // Verifies that a chain of Then calls executes correctly when continuations
508+ // are dispatched via a multi-threaded ASIO executor.
509+ TEST (Promise, MultiThreadedASIOExecutor) {
510+ boost::asio::io_context ioc;
511+ auto work = boost::asio::make_work_guard (ioc);
512+
513+ std::vector<std::thread> ioc_threads;
514+ for (int i = 0 ; i < 4 ; i++) {
515+ ioc_threads.emplace_back ([&ioc] { ioc.run (); });
516+ }
517+
518+ auto executor = [&ioc](Continuation<void ()> f) {
519+ boost::asio::post (ioc, std::move (f));
520+ };
521+
522+ Promise<int > promise;
523+ Future<int > result =
524+ promise.GetFuture ()
525+ .Then ([](int const & v) { return v * 2 ; }, executor)
526+ .Then ([](int const & v) { return v + 1 ; }, executor);
527+
528+ promise.Resolve (10 );
529+
530+ auto r = result.WaitForResult (std::chrono::seconds (5 ));
531+ ASSERT_TRUE (r.has_value ());
532+ EXPECT_EQ (*r, 21 );
533+
534+ work.reset ();
535+ for (auto & t : ioc_threads) {
536+ t.join ();
537+ }
538+ }
0 commit comments