Skip to content

Commit e3e8fc0

Browse files
committed
forward receiver queries in debug_async_scope
1 parent 4ed2411 commit e3e8fc0

File tree

2 files changed

+45
-0
lines changed

2 files changed

+45
-0
lines changed

include/unifex/detail/debug_async_scope.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,14 @@ struct _receiver<Receiver>::type final {
144144
}
145145

146146
void set_done() noexcept { op_->complete(unifex::set_done); }
147+
148+
template(typename CPO) //
149+
(requires is_receiver_query_cpo_v<CPO>) //
150+
friend auto tag_invoke(CPO cpo, const type& r) noexcept(
151+
is_nothrow_callable_v<CPO, const Receiver&>)
152+
-> callable_result_t<CPO, const Receiver&> {
153+
return std::move(cpo)(std::as_const(r.op_->receiver_));
154+
}
147155
};
148156

149157
template <typename Sender, typename Receiver>

test/debug_async_scope_test.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
#include <unifex/async_manual_reset_event.hpp>
1617
#include <unifex/inplace_stop_token.hpp>
1718
#include <unifex/just_from.hpp>
1819
#include <unifex/nest.hpp>
1920
#include <unifex/scheduler_concepts.hpp>
21+
#include <unifex/single_thread_context.hpp>
2022
#include <unifex/spawn_detached.hpp>
2123
#include <unifex/sync_wait.hpp>
2224
#include <unifex/v1/async_scope.hpp>
@@ -53,6 +55,41 @@ TEST(Debug, DISABLED_SyncWaitDeadlockV1Meth) {
5355
}));
5456
}
5557

58+
TEST(Debug, DISABLED_SyncWaitDeadlockV1TooLate) {
59+
single_thread_context ctx;
60+
v1::debug_async_scope scope;
61+
async_manual_reset_event evt;
62+
std::atomic_bool scheduled{false};
63+
64+
// wait for evt to be set on a background thread; note that the `async_wait()`
65+
// _Sender_ is unstoppable
66+
auto fut = scope.spawn_on(
67+
ctx.get_scheduler(),
68+
sequence(
69+
just_from([&scheduled]() noexcept { scheduled = true; }),
70+
evt.async_wait()));
71+
72+
// wait for the scheduled op to be started
73+
while (!scheduled) {
74+
continue;
75+
}
76+
// send a stop request to all the Senders spawned within the scope; this will
77+
// trigger the future to cancel itself, but not the unstoppable `async_wait()`
78+
scope.request_stop();
79+
80+
// with the scope joined, pending futures should all immediately
81+
// complete with done. result has no value.
82+
auto result = sync_wait(std::move(fut));
83+
ASSERT_FALSE(result.has_value());
84+
85+
// but the scope itself won't complete until the spawned work is actually
86+
// done so we will be stuck waiting for the event to be signaled
87+
sync_wait(scope.cleanup());
88+
89+
// it's too late. this should be called before `scope.cleanup()`
90+
evt.set();
91+
}
92+
5693
TEST(Debug, DISABLED_SyncWaitDeadlockV2) {
5794
v2::debug_async_scope scope;
5895
spawn_detached(

0 commit comments

Comments
 (0)