1010
1111#pragma once
1212
13+ #include " srsran/adt/noop_functor.h"
1314#include " srsran/support/async/execute_on.h"
1415#include " srsran/support/timers.h"
1516
1617namespace srsran {
1718
1819namespace detail {
1920
20- template <typename TaskExecutor, bool IsExecute>
21- auto dispatch_on_blocking (TaskExecutor& exec, timer_manager& timers)
21+ template <typename TaskExecutor, typename OnFailureToDispatch, bool IsExecute>
22+ auto dispatch_on_blocking (TaskExecutor& exec, timer_manager& timers, OnFailureToDispatch&& on_failure )
2223{
2324 struct blocking_dispatch_on_awaiter {
24- blocking_dispatch_on_awaiter (TaskExecutor& exec_, timer_manager& timers_) : exec(exec_), timers(timers_) {}
25+ blocking_dispatch_on_awaiter (TaskExecutor& exec_, timer_manager& timers_, OnFailureToDispatch&& on_failure_) :
26+ exec (exec_), timers(timers_), on_failure(std::forward<OnFailureToDispatch>(on_failure_))
27+ {
28+ }
2529
2630 bool await_ready () noexcept { return false ; }
2731
@@ -41,6 +45,8 @@ auto dispatch_on_blocking(TaskExecutor& exec, timer_manager& timers)
4145 }
4246
4347 // Task execute/defer failed (potentially because task executor queue is full).
48+ on_failure ();
49+
4450 // Leverage timer infrastructure to run task.
4551 // Note: Even if the timer expiry fails to invoke task in executor, it keeps trying on every tick.
4652 retry_timer = timers.create_unique_timer (exec);
@@ -54,56 +60,70 @@ auto dispatch_on_blocking(TaskExecutor& exec, timer_manager& timers)
5460 blocking_dispatch_on_awaiter& get_awaiter () { return *this ; }
5561
5662 private:
57- TaskExecutor& exec;
58- timer_manager& timers;
59- unique_timer retry_timer;
63+ TaskExecutor& exec;
64+ timer_manager& timers;
65+ OnFailureToDispatch on_failure;
66+ unique_timer retry_timer;
6067 };
6168
62- return blocking_dispatch_on_awaiter{exec, timers};
69+ return blocking_dispatch_on_awaiter{exec, timers, std::forward<OnFailureToDispatch>(on_failure) };
6370}
6471
6572} // namespace detail
6673
6774// / \brief Returns an awaitable that resumes the suspended coroutine in a different execution context. If the call
6875// / to execute fails, the awaitable yields and will retry the dispatch at a later point, until it succeeds.
69- template <typename TaskExecutor>
70- auto execute_on_blocking (TaskExecutor& exec, timer_manager& timers)
76+ // / \param[in] exec Executor used to dispatch coroutine to a new execution context.
77+ // / \param[in] timers Timer service used to handle reattempts to dispatch task to new execution context.
78+ // / \param[in] on_failure Callback invoked in case the dispatch to executor fails at first attempt.
79+ template <typename TaskExecutor, typename OnFailureToDispatch = noop_operation>
80+ auto execute_on_blocking (TaskExecutor& exec, timer_manager& timers, OnFailureToDispatch&& on_failure = noop_operation{})
7181{
72- return detail::dispatch_on_blocking<TaskExecutor, true >(exec, timers);
82+ return detail::dispatch_on_blocking<TaskExecutor, OnFailureToDispatch, true >(
83+ exec, timers, std::forward<OnFailureToDispatch>(on_failure));
7384}
7485
7586// / \brief Returns an awaitable that resumes the suspended coroutine in a different execution context. If the call
7687// / to defer fails, the awaitable yields and will retry the dispatch at a later point, until it succeeds.
77- template <typename TaskExecutor>
78- auto defer_on_blocking (TaskExecutor& exec, timer_manager& timers)
88+ // / \param[in] exec Executor used to dispatch coroutine to a new execution context.
89+ // / \param[in] timers Timer service used to handle reattempts to dispatch task to new execution context.
90+ // / \param[in] on_failure Callback invoked in case the dispatch to executor fails at first attempt.
91+ template <typename TaskExecutor, typename OnFailureToDispatch = noop_operation>
92+ auto defer_on_blocking (TaskExecutor& exec, timer_manager& timers, OnFailureToDispatch&& on_failure = noop_operation{})
7993{
80- return detail::dispatch_on_blocking<TaskExecutor, false >(exec, timers);
94+ return detail::dispatch_on_blocking<TaskExecutor, OnFailureToDispatch, false >(
95+ exec, timers, std::forward<OnFailureToDispatch>(on_failure));
8196}
8297
8398// / \brief Returns an async_task<void> that runs a given invocable task in a \c dispatch_exec executor, and once the
8499// / task is complete, it resumes the suspended coroutine in a \c return_exec executor.
85100template <typename DispatchTaskExecutor,
86101 typename CurrentTaskExecutor,
87102 typename Callable,
88- typename ReturnType = detail::function_return_t <decltype (&Callable::operator ())>>
103+ typename OnFailureToDispatch = noop_operation,
104+ typename ReturnType = detail::function_return_t <decltype (&Callable::operator ())>>
89105std::enable_if_t<std::is_same_v<ReturnType, void>, async_task<void>>
90106execute_and_continue_on_blocking(DispatchTaskExecutor& dispatch_exec,
91107 CurrentTaskExecutor& return_exec,
92108 timer_manager& timers,
93- Callable&& callable)
109+ Callable&& callable,
110+ OnFailureToDispatch&& on_failure = noop_operation{})
94111{
95- return launch_async ([&return_exec, &dispatch_exec, task = std::forward<Callable>(callable), &timers](
96- coro_context<async_task<void >>& ctx) mutable {
112+ return launch_async ([&return_exec,
113+ &dispatch_exec,
114+ task = std::forward<Callable>(callable),
115+ on_failure = std::forward<OnFailureToDispatch>(on_failure),
116+ &timers](coro_context<async_task<void >>& ctx) mutable {
97117 CORO_BEGIN (ctx);
98118
99119 // Dispatch execution context switch.
100- CORO_AWAIT (execute_on_blocking (dispatch_exec, timers));
120+ CORO_AWAIT (execute_on_blocking (dispatch_exec, timers, on_failure ));
101121
102122 // Run task.
103123 task ();
104124
105125 // Continuation in the original executor.
106- CORO_AWAIT (execute_on_blocking (return_exec, timers));
126+ CORO_AWAIT (execute_on_blocking (return_exec, timers, on_failure ));
107127
108128 CORO_RETURN ();
109129 });
@@ -114,26 +134,32 @@ execute_and_continue_on_blocking(DispatchTaskExecutor& dispatch_exec,
114134template <typename DispatchTaskExecutor,
115135 typename CurrentTaskExecutor,
116136 typename Callable,
117- typename ReturnType = detail::function_return_t <decltype (&Callable::operator ())>>
137+ typename OnFailureToDispatch = noop_operation,
138+ typename ReturnType = detail::function_return_t <decltype (&Callable::operator ())>>
118139std::enable_if_t<not std::is_same_v<ReturnType, void>, async_task<ReturnType>>
119140execute_and_continue_on_blocking(DispatchTaskExecutor& dispatch_exec,
120141 CurrentTaskExecutor& return_exec,
121142 timer_manager& timers,
122- Callable&& callable)
143+ Callable&& callable,
144+ OnFailureToDispatch&& on_failure = noop_operation{})
123145{
124146 ReturnType ret{};
125- return launch_async ([&return_exec, &dispatch_exec, task = std::forward<Callable>(callable), &timers, ret](
126- coro_context<async_task<ReturnType>>& ctx) mutable {
147+ return launch_async ([&return_exec,
148+ &dispatch_exec,
149+ task = std::forward<Callable>(callable),
150+ on_failure = std::forward<OnFailureToDispatch>(on_failure),
151+ &timers,
152+ ret](coro_context<async_task<ReturnType>>& ctx) mutable {
127153 CORO_BEGIN (ctx);
128154
129155 // Dispatch execution context switch.
130- CORO_AWAIT (execute_on_blocking (dispatch_exec, timers));
156+ CORO_AWAIT (execute_on_blocking (dispatch_exec, timers, on_failure ));
131157
132158 // Run task.
133159 ret = task ();
134160
135161 // Continuation in the original executor.
136- CORO_AWAIT (execute_on_blocking (return_exec, timers));
162+ CORO_AWAIT (execute_on_blocking (return_exec, timers, on_failure ));
137163
138164 CORO_RETURN (ret);
139165 });
0 commit comments