19#include "TasksConfig.h"
22#if SQUID_ENABLE_TASK_DEBUG
25#define TASK_NAME(...) co_await NAMESPACE_SQUID::SetDebugName(__VA_ARGS__);
26#define DEBUG_STR , std::string in_debugStr
27#define PASS_DEBUG_STR , in_debugStr
28#define MANUAL_DEBUG_STR(debugStr) , debugStr
29#define WaitUntilImpl(...) _WaitUntil(__VA_ARGS__, #__VA_ARGS__)
30#define WaitWhileImpl(...) _WaitWhile(__VA_ARGS__, #__VA_ARGS__)
32#define WaitUntil(...) WaitUntilImpl(__VA_ARGS__)
33#define WaitWhile(...) WaitWhileImpl(__VA_ARGS__)
39#define MANUAL_DEBUG_STR(...)
41#define WaitUntil(...) _WaitUntil(__VA_ARGS__)
42#define WaitWhile(...) _WaitWhile(__VA_ARGS__)
76template <
typename tRet, eTaskRef RefType, eTaskResumable Resumable>
78template <
typename tRet =
void>
90struct Suspend :
public std::suspend_always
98 bool IsStopRequested()
const
100 return *m_isStoppedPtr;
104 friend class TaskInternalBase;
106 : m_isStoppedPtr(in_isStoppedPtr)
111 const bool* m_isStoppedPtr =
nullptr;
123#include "Private/TaskPrivate.h"
202template <
typename tRet =
void, eTaskRef RefType = eTaskRef::Strong, eTaskResumable Resumable = eTaskResumable::Yes>
207 using tTaskInternal = TaskInternal<tRet>;
208 using promise_type = TaskPromise<tRet>;
211#define NONVOID_ONLY template <typename U = tRet, typename std::enable_if_t<!std::is_void<U>::value>* = nullptr>
214 static_assert(RefType ==
eTaskRef::Strong || std::is_void<tRet>::value,
"Illegal task type (cannot combine weak reference type with non-void return type");
222 Task(std::shared_ptr<tTaskInternal> in_taskInternal)
223 : m_taskInternal(in_taskInternal)
227 Task(std::coroutine_handle<promise_type> in_coroHandle)
228 : m_taskInternal(std::make_shared<tTaskInternal>(in_coroHandle))
233 : m_taskInternal(in_otherTask.GetInternalTask())
235 static_assert(IsCopyable(),
"Cannot copy-construct Task/WeakTask (only TaskHandle/WeakTaskHandle)");
239 : m_taskInternal(std::move(in_otherTask.m_taskInternal))
246 m_taskInternal =
nullptr;
251 static_assert(IsCopyable(),
"Cannot copy-assign Task/WeakTask (only TaskHandle/WeakTaskHandle)");
253 m_taskInternal = in_otherTask.m_taskInternal;
263 m_taskInternal = std::move(in_otherTask.m_taskInternal);
275 return m_taskInternal.get();
277 operator bool() const
283 return IsValid() ? m_taskInternal->IsDone() :
true;
287 return IsValid() ? m_taskInternal->IsStopRequested() :
true;
293 m_taskInternal->RequestStop();
301 m_taskInternal->Kill();
306 SQUID_RUNTIME_CHECK(
IsValid(),
"Tried to retrieve return value from an invalid handle");
307 return GetInternalTask()->TakeReturnValue();
311 static_assert(IsResumable(),
"Cannot call Resume() on a TaskHandle/WeakTaskHandle");
315#if SQUID_ENABLE_TASK_DEBUG
316 std::string
GetDebugName(std::optional<TaskDebugStackFormatter> in_formatter = {})
const
318 const char* defaultRetVal = Resumable ==
eTaskResumable::Yes ?
"[empty task]" :
"[empty task handle]";
319 auto debugName =
IsValid() ? m_taskInternal->GetDebugName() : defaultRetVal;
320 return in_formatter ? in_formatter.value().Format(debugName) : debugName;
322 std::string
GetDebugStack(std::optional<TaskDebugStackFormatter> in_formatter = {})
const
326 return in_formatter ? in_formatter.value().Format(m_taskInternal->GetDebugStack()) : m_taskInternal->GetDebugStack();
331 std::string
GetDebugName(std::optional<TaskDebugStackFormatter> in_formatter = {})
const
335 std::string
GetDebugStack(std::optional<TaskDebugStackFormatter> in_formatter = {})
const
342#if SQUID_USE_EXCEPTIONS
343 std::exception_ptr GetUnhandledException() const
345 SQUID_RUNTIME_CHECK(
IsValid(),
"Tried to retrieve unhandled exception from an invalid handle");
346 return m_taskInternal->GetUnhandledException();
348 void RethrowUnhandledException() const
350 if(
auto e = m_taskInternal->GetUnhandledException())
352 std::rethrow_exception(e);
356 void RethrowUnhandledException() const
362 template <
typename tOtherRet>
365 constexpr bool isLegalReturnTypeConversion = std::is_void<tOtherRet>::value || std::is_same<tRet, tOtherRet>::value;
366 constexpr bool isLegalTypeConversion = IsStrong() && IsResumable();
367 static_assert(isLegalTypeConversion,
"Cannot promote WeakTask/TaskHandle/WeakTaskHandle to Task");
368 static_assert(!isLegalTypeConversion || isLegalReturnTypeConversion,
"Mismatched return type (invalid return type conversion)");
369 static_assert(!isLegalTypeConversion || !isLegalReturnTypeConversion,
"Cannot copy Task -> Task because it is non-copyable (try std::move(task))");
372 template <
typename tOtherRet>
375 constexpr bool isLegalReturnTypeConversion = std::is_void<tOtherRet>::value || std::is_same<tRet, tOtherRet>::value;
376 constexpr bool isLegalTypeConversion = IsStrong() && IsResumable();
377 static_assert(isLegalTypeConversion,
"Cannot promote WeakTask/TaskHandle/WeakTaskHandle to Task");
378 static_assert(!isLegalTypeConversion || isLegalReturnTypeConversion,
"Cannot convert tasks to non-void return type (invalid return type conversion)");
381 return MoveToTask<tOtherRet, RefType, Resumable>();
385 static_assert(IsResumable(),
"Cannot convert TaskHandle -> WeakTask (invalid resumability conversion");
386 static_assert(!IsResumable(),
"Cannot copy Task -> WeakTask because it is non-copyable (try std::move(task))");
391 static_assert(IsResumable(),
"Cannot convert TaskHandle -> WeakTask (invalid resumability conversion)");
392 return MoveToTask<void, eTaskRef::Weak, eTaskResumable::Yes>();
396 static_assert(IsStrong(),
"Cannot convert WeakTask/WeakTaskHandle -> TaskHandle (invalid reference-strength conversion)");
397 return CopyToTask<tRet, eTaskRef::Strong, eTaskResumable::No>();
399 template <
typename tOtherRet>
402 constexpr bool isLegalReturnTypeConversion = std::is_void<tOtherRet>::value || std::is_same<tRet, tOtherRet>::value;
403 static_assert(IsStrong(),
"Cannot convert WeakTask/WeakTaskHandle -> TaskHandle (invalid reference-strength conversion)");
404 static_assert(!IsStrong() || isLegalReturnTypeConversion,
"Mismatched return type (invalid return type conversion)");
405 return CopyToTask<tOtherRet, eTaskRef::Strong, eTaskResumable::No>();
410 return CopyToTask<void, eTaskRef::Weak, eTaskResumable::No>();
418 return CancelTaskIf(std::move(*
this), in_cancelFn);
424 return std::move(*this).CancelIf([
this] { return IsStopRequested(); });
428 static_assert(static_false<tRet>::value,
"Cannot call CancelIf() on an lvalue (try std::move(task).CancelIf())");
429 return CancelTaskIf(std::move(*
this), in_cancelFn);
433 static_assert(static_false<tRet>::value,
"Cannot call CancelIfStopRequested() on an lvalue (try std::move(task).CancelIfStopRequested())");
434 return std::move(*this).CancelIf([
this] { return IsStopRequested(); });
442 return StopTaskIf(std::move(*
this), in_cancelFn);
446 static_assert(static_false<tRet>::value,
"Cannot call StopIf() on an lvalue (try std::move(task).StopIf())");
447 return StopTaskIf(std::move(*
this), in_cancelFn);
449#if SQUID_ENABLE_GLOBAL_TIME
455 return StopTaskIf(std::move(*
this), in_cancelFn, in_timeout);
459 static_assert(static_false<tRet>::value,
"Cannot call StopIf() on an lvalue (try std::move(task).StopIf())");
460 return StopTaskIf(std::move(*
this), in_cancelFn, in_timeout);
465 static_assert(static_false<tRet>::value,
"Global task time not enabled (see SQUID_ENABLE_GLOBAL_TIME in TasksConfig.h)");
466 return StopTaskIf(std::move(*
this), in_cancelFn);
470 static_assert(static_false<tRet>::value,
"Global task time not enabled (see TasksConfig.h)");
471 return StopTaskIf(std::move(*
this), in_cancelFn);
476 template <typename tTimeFn>
479 return StopTaskIf(std::move(*
this), in_cancelFn, in_timeout, in_timeFn);
481 template <
typename tTimeFn>
484 static_assert(static_false<tRet>::value,
"Cannot call StopIf() on an lvalue (try std::move(task).StopIf())");
485 return StopTaskIf(std::move(*
this), in_cancelFn, in_timeout, in_timeFn);
490 template <
typename, eTaskRef, eTaskResumable,
typename>
friend struct TaskAwaiterBase;
491 template <
typename, eTaskRef, eTaskResumable>
friend class Task;
492 friend class TaskInternalBase;
496 std::shared_ptr<TaskInternalBase> m_taskInternal;
499 std::shared_ptr<tTaskInternal> GetInternalTask()
const
502 return std::static_pointer_cast<tTaskInternal>(m_taskInternal);
506 template <
typename tNewRet, eTaskRef NewRefType, eTaskResumable NewResumable>
510 ret.m_taskInternal = m_taskInternal;
515 template <
typename tNewRet, eTaskRef NewRefType, eTaskResumable NewResumable>
519 ret.m_taskInternal = m_taskInternal;
522 m_taskInternal =
nullptr;
533 m_taskInternal->AddLogicalRef();
543 m_taskInternal->RemoveLogicalRef();
547 void KillIfResumable()
549 if (IsResumable() &&
IsValid())
556 static constexpr bool IsResumable()
560 static constexpr bool IsStrong()
564 static constexpr bool IsCopyable()
566 return !IsResumable();
612template <
typename tTimeFn>
615 return in_timeFn() - in_t;
618#if SQUID_ENABLE_GLOBAL_TIME
636template <
typename T =
void>
639 static_assert(static_false<T>::value,
"Global task time not enabled (see SQUID_ENABLE_GLOBAL_TIME in TasksConfig.h)");
654 TaskWrapper(
Task<> in_task) : task(std::move(in_task)) {}
658 template <
typename tRet>
659 static std::shared_ptr<TaskWrapper> Wrap(
Task<tRet> in_task)
661 return std::make_shared<TaskWrapper>(std::move(in_task));
663 template <
typename tReadyFn>
664 static std::shared_ptr<TaskWrapper> Wrap(tReadyFn in_readyFn)
666 auto task = [](tReadyFn in_readyFn) ->
Task<> {
co_await in_readyFn; }(in_readyFn);
667 return std::make_shared<TaskWrapper>(std::move(task));
672struct TaskSingleEntry
674 template <
typename tRet>
676 : taskWrapper(TaskWrapper::Wrap(std::move(in_task)))
679 template <
typename tReadyFn>
680 TaskSingleEntry(tReadyFn in_readyFn)
681 : taskWrapper(TaskWrapper::Wrap(in_readyFn))
686 return taskWrapper->task.Resume();
688 std::shared_ptr<TaskWrapper> taskWrapper;
692template <
typename tValue>
693struct TaskSelectEntry
695 template <
typename tRet>
696 TaskSelectEntry(tValue in_value,
Task<tRet> in_task)
698 , taskWrapper(TaskWrapper::Wrap(std::move(in_task)))
701 template <
typename tReadyFn>
702 TaskSelectEntry(tValue in_value, tReadyFn in_readyFn)
704 , taskWrapper(TaskWrapper::Wrap(in_readyFn))
709 return taskWrapper->task.Resume();
716 std::shared_ptr<TaskWrapper> taskWrapper;
720#define TASK_NAME_ENTRIES(name, entries) \
721 TASK_NAME(name, [entries]() { \
722 std::string debugStr; \
723 for(auto entry : entries) \
725 debugStr += debugStr.size() ? "\n" : "\n`"; \
726 debugStr += entry.taskWrapper->task.GetDebugStack(); \
732#define TASK_NAME_ENTRIES_ALL(name, entries) \
733 TASK_NAME(name, [entries]() { \
734 std::string debugStr; \
735 for(auto entry : entries) \
737 debugStr += debugStr.size() ? "\n" : "\n`"; \
738 debugStr += entry.taskWrapper->task.GetDebugStack() + (entry.taskWrapper->task.IsDone() ? " [DONE]" : " [RUNNING]"); \
748 TASK_NAME_ENTRIES(__FUNCTION__, in_entries);
750 for(
auto& entry : in_entries)
752 co_await AddStopTask(entry.taskWrapper->task);
757 for(
auto& entry : in_entries)
771 TASK_NAME_ENTRIES_ALL(__FUNCTION__, in_entries);
773 for(
auto& entry : in_entries)
775 co_await AddStopTask(entry.taskWrapper->task);
781 for(
auto& entry : in_entries)
797template<
class tValue>
800 TASK_NAME_ENTRIES(__FUNCTION__, in_entries);
802 for(
auto& entry : in_entries)
804 co_await AddStopTask(entry.taskWrapper->task);
809 for(
size_t i = 0; i < in_entries.size(); ++i)
813 co_return in_entries[i].GetValue();
829inline Task<> _WaitUntil(tTaskReadyFn in_readyFn DEBUG_STR)
831 TASK_NAME(
"WaitUntil", [debugStr = FormatDebugString(in_debugStr)]{
return debugStr; });
835inline Task<> _WaitWhile(tTaskReadyFn in_readyFn DEBUG_STR)
837 TASK_NAME(
"WaitWhile", [debugStr = FormatDebugString(in_debugStr)]{
return debugStr; });
839 co_await[&in_readyFn]{
return !in_readyFn(); };
845 return _WaitUntil([]() {
return false; } MANUAL_DEBUG_STR(
"WaitForever"));
849template <
typename tTimeFn>
852 auto startTime = in_timeFn();
853 TASK_NAME(__FUNCTION__, [in_timeFn, startTime, in_seconds] {
return std::to_string(
GetTimeSince(startTime, in_timeFn)) +
"/" + std::to_string(in_seconds); });
855 auto IsTimerUp = [in_timeFn, startTime, in_seconds] {
856 return GetTimeSince(startTime, in_timeFn) >= in_seconds;
859 co_return in_timeFn() - startTime - in_seconds;
863template <
typename tRet,
typename tTimeFn>
866 auto IsTimerUp = [in_timeFn, startTime = in_timeFn(), in_seconds]{
867 return GetTimeSince(startTime, in_timeFn) >= in_seconds;
869 return CancelTaskIf(std::move(in_task), IsTimerUp);
873template <
typename tFn,
typename tTimeFn>
883#if SQUID_ENABLE_GLOBAL_TIME
891template <
typename tRet>
898template <
typename tFn>
904template <
typename T =
void>
907 static_assert(static_false<T>::value,
"Global task time not enabled (see SQUID_ENABLE_GLOBAL_TIME in TasksConfig.h)");
910template <
typename tRet,
typename T =
void>
913 static_assert(static_false<T>::value,
"Global task time not enabled (see SQUID_ENABLE_GLOBAL_TIME in TasksConfig.h)");
916template <
typename tFn,
typename T =
void>
919 static_assert(static_false<T>::value,
"Global task time not enabled (see SQUID_ENABLE_GLOBAL_TIME in TasksConfig.h)");
925template <
typename tRet>
930 co_await AddStopTask(in_task);
934 if(in_cancelFn && in_cancelFn())
938 auto taskStatus = in_task.
Resume();
951 co_await AddStopTask(in_task);
955 if(in_cancelFn && in_cancelFn())
959 auto taskStatus = in_task.
Resume();
968template <
typename tRet, eTaskRef RefType, eTaskResumable Resumable>
972 return CancelIfImpl(std::move(in_task), in_cancelFn);
976template <
typename tRet,
typename tTimeFn>
980 return std::string(
"timeout = ") + (in_timeout ? std::to_string(in_timeout.value()) :
"none") +
", task = " + taskHandle.GetDebugStack();
983 co_await AddStopTask(in_task);
990 if(in_timeout.has_value())
992 co_return co_await Timeout(std::move(in_task), in_timeout.value(), in_timeFn);
995 auto taskStatus = in_task.
Resume();
1003template <
typename tTimeFn>
1007 return std::string(
"timeout = ") + (in_timeout ? std::to_string(in_timeout.value()) :
"none") +
", task = " + taskHandle.GetDebugStack();
1010 co_await AddStopTask(in_task);
1019 co_return co_await Timeout(std::move(in_task), in_timeout.value(), in_timeFn);
1022 auto taskStatus = in_task.
Resume();
1031template <
typename tRet, eTaskRef RefType, eTaskResumable Resumable>
1034 return StopIfImpl(std::move(in_task), in_cancelFn, {}, (float(*)())
nullptr);
1037#if SQUID_ENABLE_GLOBAL_TIME
1038template <
typename tRet, eTaskRef RefType, eTaskResumable Resumable>
1041 return StopIfImpl(std::move(in_task), in_cancelFn, in_timeout,
GlobalTime());
1044template <
typename tRet, eTaskRef RefType, eTaskResumable Resumable>
1047 static_assert(static_false<tRet>::value,
"Global task time not enabled (see SQUID_ENABLE_GLOBAL_TIME in TasksConfig.h)");
1048 return StopIfImpl(std::move(in_task), in_cancelFn, in_timeout,
nullptr);
1052template <
typename tRet, eTaskRef RefType, eTaskResumable Resumable,
typename tTimeFn>
1057 return StopIfImpl(std::move(in_task), in_cancelFn, in_timeout, in_timeFn);
Task & operator=(Task &&in_otherTask) noexcept
Move assignment operator.
Definition: Task.h:257
Task(const Task &in_otherTask)
Copy constructor (TaskHandle/WeakTaskHandle only)
Definition: Task.h:232
auto StopIf(tTaskCancelFn in_cancelFn, tTaskTime in_timeout, tTimeFn in_timeFn) &&
Returns wrapper task that requests a stop on this task when the given function returns true,...
Definition: Task.h:477
auto CancelIfStopRequested() &&
Definition: Task.h:422
eTaskStatus Resume()
Resumes the task (Task/WeakTask only)
Definition: Task.h:309
Task & operator=(nullptr_t) noexcept
Null-pointer assignment operator (makes the handle invalid)
Definition: Task.h:243
auto CancelIf(tTaskCancelFn in_cancelFn) &&
Definition: Task.h:416
bool IsStopRequested() const
Returns whether a stop request has been issued for the task.
Definition: Task.h:285
std::string GetDebugStack(std::optional< TaskDebugStackFormatter > in_formatter={}) const
Gets this task's debug stack (use TASK_NAME to set a task's debug name)
Definition: Task.h:322
auto StopIf(tTaskCancelFn in_cancelFn, tTaskTime in_timeout) &&
Returns wrapper task that requests a stop on this task when the given function returns true,...
Definition: Task.h:452
void Kill()
Immediately terminates the task.
Definition: Task.h:296
Task & operator=(const Task &in_otherTask)
Copy assignment operator (TaskHandle/WeakTaskHandle only)
Definition: Task.h:249
void RequestStop()
Issues a request for the task to terminate gracefully as soon as possible.
Definition: Task.h:289
Task(nullptr_t)
Null-pointer constructor (constructs an invalid handle)
Definition: Task.h:219
~Task()
Destructor.
Definition: Task.h:266
std::string GetDebugName(std::optional< TaskDebugStackFormatter > in_formatter={}) const
Gets this task's debug name (use TASK_NAME to set the debug name)
Definition: Task.h:316
bool IsValid() const
Returns whether the underlying coroutine is valid.
Definition: Task.h:273
bool IsDone() const
Returns whether the task has terminated.
Definition: Task.h:281
std::optional< tRet > TakeReturnValue()
Attempts to take the task's return value (throws error if return value is either orphaned or was alre...
Definition: Task.h:304
Task(Task &&in_otherTask) noexcept
Move constructor.
Definition: Task.h:238
Task()
Default constructor (constructs an invalid handle)
Definition: Task.h:216
auto StopIf(tTaskCancelFn in_cancelFn) &&
Returns wrapper task that requests a stop on this task when the given function returns true,...
Definition: Task.h:440
Task< tValue > Select(std::vector< TaskSelectEntry< tValue > > in_entries)
Awaiter task that behaves like WaitForAny(), but returns a value associated with whichever awaiter fi...
Definition: Task.h:798
Task WaitUntil(tTaskReadyFn in_readyFn)
Awaiter function that waits until a given functor returns true.
Definition: Task.h:823
auto Timeout(Task< tRet > &&in_task, tTaskTime in_seconds, tTimeFn in_timeFn)
Awaiter function that wraps a given task, canceling it after N seconds in a given time-stream....
Definition: Task.h:864
Task WaitForever()
Awaiter function that waits forever (only for use in tasks that will be killed externally)
Definition: Task.h:843
Task WaitWhile(tTaskReadyFn in_readyFn)
Awaiter function that waits until a given functor returns false.
Definition: Task.h:826
Task WaitForAll(std::vector< TaskSingleEntry > in_entries)
Awaiter task that manages a set of other awaiters and waits until all of them are done.
Definition: Task.h:769
Task< tTaskTime > WaitSeconds(tTaskTime in_seconds, tTimeFn in_timeFn)
Awaiter function that waits N seconds in a given time-stream.
Definition: Task.h:850
Task DelayCall(tTaskTime in_delaySeconds, tFn in_fn, tTimeFn in_timeFn)
Awaiter function that calls a given function after N seconds in a given time-stream.
Definition: Task.h:874
Task WaitForAny(std::vector< TaskSingleEntry > in_entries)
Awaiter task that manages a set of other awaiters and waits until at least one of them is done.
Definition: Task.h:746
Task< void, eTaskRef::Weak, eTaskResumable::No > WeakTaskHandle
Non-resumable handle that holds a weak reference to a task (always void return type)
Definition: Task.h:81
Task< void, eTaskRef::Weak, eTaskResumable::Yes > WeakTask
Resumable handle that holds a weak reference to a task (always void return type)
Definition: Task.h:80
eTaskResumable
Whether a handle can be resumed (all live tasks have exactly one resumable handle and 0+ non-resumabl...
Definition: Task.h:60
#define TASK_NAME(...)
Macro that instruments a task with a debug name string. Usually at the top of every task coroutine as...
Definition: Task.h:25
eTaskRef
Whether a handle references a task using a strong or weak reference.
Definition: Task.h:53
std::function< bool()> tTaskCancelFn
CancelIf/StopIf condition function type.
Definition: Task.h:73
eTaskStatus
Status of a task (whether it is currently suspended or done)
Definition: Task.h:67
@ Yes
Handle is resumable.
@ No
Handle is not resumable.
@ Weak
Handle will not the task alive.
@ Strong
Handle will keep the task alive (so long as there exists a valid Resumable handle)
@ Suspended
Task is currently suspended.
@ Done
Task has terminated and coroutine frame has been destroyed.
tTaskTime GetTimeSince(tTaskTime in_t, tTimeFn in_timeFn)
Helper function to elapsed time in a given time-stream.
Definition: Task.h:613
auto GlobalTime()
Global time-stream function used internally by Squid::Tasks (requires SQUID_ENABLE_GLOBAL_TIME)
Definition: Task.h:625
tTaskTime GetGlobalTime()
User-defined global time-stream function (must be implemented if SQUID_ENABLE_GLOBAL_TIME is set,...
Awaiter class that immediately (without suspending) yields a stop context.
Definition: Task.h:117
Context for a task's stop requests (undefined behavior if used after the underlying task is destroyed...
Definition: Task.h:97
Awaiter class that suspends unconditionally.
Definition: Task.h:91