41#ifndef AISTATEFULTASK_H
42#define AISTATEFULTASK_H
44#include "threadpool/Timer.h"
45#include "threadsafe/aithreadsafe.h"
46#include "threadsafe/AIMutex.h"
47#include "threadpool/AIQueueHandle.h"
48#include "utils/AIRefCount.h"
49#include "utils/macros.h"
50#include "utils/FuzzyBool.h"
51#include "utils/is_power_of_two.h"
102 static constexpr condition_type thread_pool_full_condition = 0x80000000;
108 boost::intrusive_ptr<AIStatefulTask> task;
111 void signal()
const { task->signal(condition); }
210 PRAGMA_DIAGNOSTIC_PUSH_IGNORE_maybe_uninitialized
216 PRAGMA_DIAGNOSTIC_POP
228 void print_on(std::ostream& os)
const;
230 friend std::ostream& operator<<(std::ostream& os,
Conditions conditions)
232 os << std::hex << conditions.m_conditions << std::dec <<
" (";
233 if (AI_LIKELY(utils::is_power_of_two(conditions.m_conditions)))
235 conditions.print_on(os);
241 friend std::ostream& operator<<(std::ostream&,
Conditions);
245 return {
this, conditions };
250 struct multiplex_state_st {
252 Handler current_handler;
255 multiplex_state_st() : base_state(
bs_reset), current_handler(Handler::idle), wait_condition(nullptr) { }
258 struct sub_state_st {
274 using multiplex_state_type = aithreadsafe::Wrapper<multiplex_state_st, aithreadsafe::policy::Primitive<std::mutex>>;
275 multiplex_state_type mState;
279 using sub_state_type = aithreadsafe::Wrapper<sub_state_st, aithreadsafe::policy::Primitive<std::mutex>>;
280 sub_state_type mSubState;
285 AIMutex mMultiplexMutex;
287 std::recursive_mutex mRunMutex;
289 using clock_type = std::chrono::steady_clock;
290 using duration_type = clock_type::duration;
292 clock_type::rep mSleep;
296 boost::intrusive_ptr<AIStatefulTask> mParent;
300 std::function<void (
bool)> mCallback;
303 Handler mDefaultHandler;
304 Handler mTargetHandler;
309 std::thread::id mThreadId;
311 bool mDebugShouldRun;
313 bool mDebugSignalPending;
314 bool mDebugSetStatePending;
315 bool mDebugRefCalled;
320#if defined(CWDEBUG) && !defined(DOXYGEN)
324#if CW_DEBUG && !defined(DOXYGEN)
326 bool m_may_not_be_deleted;
330 duration_type mDuration;
334 char const* m_tracy_fiber_name;
340 static thread_local char const* s_tl_tracy_fiber_name;
354 mDebugLastState(
bs_killed), mDebugShouldRun(false), mDebugAborted(false), mDebugSignalPending(false),
355 mDebugSetStatePending(false), mDebugRefCalled(false),
361 m_may_not_be_deleted(false),
363 mDuration(duration_type::zero())
365 , m_tracy_fiber_name(nullptr)
370 void set_tracy_fiber_name(
char const* tracy_fiber_name);
380 ASSERT(!m_may_not_be_deleted);
402 void run(Handler default_handler, std::function<
void (
bool)> cb_function);
694 bool active(
Handler handler)
const {
return multiplex_state_type::crat(mState)->current_handler == handler; }
702 bool is_immediate()
const {
return multiplex_state_type::crat(mState)->current_handler.is_immediate(); }
706 bool default_is_immediate()
const {
return mDefaultHandler.is_immediate(); }
718 sub_state_type::crat sub_state_r(mSubState);
719 return sub_state_r->finished ? &AIStatefulTask::mOnAbort : 0;
727 bool aborted()
const {
return sub_state_type::crat(mSubState)->aborted; }
732 bool executing()
const {
return mMultiplexMutex.is_self_locked(); }
746 static char const* event_str(event_type event);
799 char const* parent_tracy_fiber_name = s_tl_tracy_fiber_name;
800 if (AI_UNLIKELY(parent_tracy_fiber_name))
804 s_tl_tracy_fiber_name =
nullptr;
807 multiplex(event, handler);
809 if (AI_UNLIKELY(parent_tracy_fiber_name))
811 s_tl_tracy_fiber_name = parent_tracy_fiber_name;
813 TracyFiberEnter(parent_tracy_fiber_name);
824 bool sleep(clock_type::time_point current_time,
bool only_task)
830 else if (mSleep <= current_time.time_since_epoch().count() || only_task)
835 void add(duration_type delta) { mDuration += delta; }
837 void add_task_to_thread_pool(AIQueueHandle queue_handle, uint8_t
const failure_count = 0);
845template<
typename Type>
846concept TaskType = std::is_base_of_v<AIStatefulTask, Type>;
850#if defined(CWDEBUG) && !defined(DOXYGEN)
851NAMESPACE_DEBUG_CHANNELS_START
853NAMESPACE_DEBUG_CHANNELS_END
893template<task::TaskType TaskType,
typename... ARGS>
894boost::intrusive_ptr<TaskType>
create(ARGS&&... args)
896 DoutEntering(dc::statefultask,
"statefultask::create<" << ::NAMESPACE_DEBUG::type_name_of<TaskType>() <<
897 ((LibcwDoutStream << ... << (std::string(
", ") + ::NAMESPACE_DEBUG::type_name_of<ARGS>())),
">(") << join(
", ", args...) <<
')');
898 TaskType* task =
new TaskType(std::forward<ARGS>(args)...);
899 AllocTag2(task,
"Created with statefultask::create");
901 task->set_tracy_fiber_name(task->task_name());
904 Dout(dc::statefultask|continued_cf,
"Returning task pointer " << (
void*)task);
906 Dout(dc::finish,
" [" <<
static_cast<AIStatefulTask*
>(task) <<
"].");
908 Dout(dc::finish,
".");
913template<task::TaskType TaskType,
typename... ARGS>
914boost::intrusive_ptr<TaskType> create_from_tuple(std::tuple<ARGS...>&& args)
916 DoutEntering(dc::statefultask,
"statefultask::create_from_tuple<" << ::NAMESPACE_DEBUG::type_name_of<TaskType>() <<
917 ((LibcwDoutStream << ... << (std::string(
", ") + ::NAMESPACE_DEBUG::type_name_of<ARGS>())),
">(") << args <<
')');
918 TaskType* task =
new TaskType(std::make_from_tuple<TaskType>(std::move(args)));
919 AllocTag2(task,
"Created with statefultask::create_from_tuple");
921 task->set_tracy_fiber_name(task->task_name());
924 Dout(dc::statefultask|continued_cf,
"Returning task pointer " << (
void*)task);
926 Dout(dc::finish,
" [" <<
static_cast<AIStatefulTask*
>(task) <<
"].");
928 Dout(dc::finish,
'.');
938#ifndef AISTATEFULTASK_H_definitions
939#define AISTATEFULTASK_H_definitions
943 return stateful_task_mutex.is_self_locked(handle);
Mutex for stateful tasks. Declaration of class AIStatefulTaskMutex.
std::function< bool()> AIWaitConditionFunc
The type of the functor that must be passed as first parameter to AIStatefulTask::wait_until.
Definition: AIStatefulTask.h:68
Definition: AIEngine.h:110
Definition: AIFriendOfStatefulTask.h:61
Definition: AIStatefulTaskMutex.h:86
Definition: AIStatefulTask.h:96
duration_type getDuration() const
Definition: AIStatefulTask.h:754
bool running() const
Definition: AIStatefulTask.h:665
bool is_immediate() const
Definition: AIStatefulTask.h:702
on_abort_st
What to do when a child task is aborted.
Definition: AIStatefulTask.h:139
@ signal_parent
Call signal(condition_type) on the parent anyway.
Definition: AIStatefulTask.h:141
@ do_nothing
Abort without notifying the parent task.
Definition: AIStatefulTask.h:142
@ abort_parent
Call abort() on the parent.
Definition: AIStatefulTask.h:140
virtual char const * task_name_impl() const =0
This can be used to get a human readable name of the most-derived class. It must be guaranteed to alw...
virtual void force_killed()
Called from AIEngine::flush().
Definition: AIStatefulTask.cxx:1297
static char const * state_str(base_state_type state)
Definition: AIStatefulTask.cxx:1819
static constexpr state_type state_end
The next state value to use for derived classes.
Definition: AIStatefulTask.h:137
bool waiting() const
Definition: AIStatefulTask.cxx:484
bool aborted() const
Definition: AIStatefulTask.h:727
AIStatefulTask(bool debug)
Definition: AIStatefulTask.h:352
virtual void multiplex_impl(state_type run_state)=0
Called for base state bs_multiplex.
uint32_t state_type
The type of run_state.
Definition: AIStatefulTask.h:98
virtual void abort_impl()
Called for base state bs_abort.
Definition: AIStatefulTask.cxx:1287
char const * task_name() const
Definition: AIStatefulTask.h:759
bool active(Handler handler) const
Definition: AIStatefulTask.h:694
bool executing() const
Definition: AIStatefulTask.h:732
virtual void finish_impl()
Called for base state bs_finish.
Definition: AIStatefulTask.cxx:1292
bool finished() const
Definition: AIStatefulTask.h:716
uint32_t condition_type
The type of the skip_wait and idle bit masks.
Definition: AIStatefulTask.h:99
bool waiting_or_aborting() const
Definition: AIStatefulTask.cxx:490
virtual char const * state_str_impl(state_type run_state) const
Called to stringify a run state for debugging output. Must be overridden.
Definition: AIStatefulTask.cxx:1273
base_state_type
The type of mState.
Definition: AIStatefulTask.h:125
@ bs_killed
State after the call back, or when aborted before being initialized.
Definition: AIStatefulTask.h:132
@ bs_multiplex
State after initialize_impl and before finish() or abort().
Definition: AIStatefulTask.h:128
@ bs_abort
State after abort() and leaving inside multiplex_impl (if there), and before abort_impl().
Definition: AIStatefulTask.h:129
@ bs_initialize
State after run and before/during initialize_impl.
Definition: AIStatefulTask.h:127
@ bs_finish
State after finish() (assuming abort() isn't called) and leaving multiplex_impl (if there),...
Definition: AIStatefulTask.h:130
@ bs_callback
State after finish_impl() and before the call back.
Definition: AIStatefulTask.h:131
@ bs_reset
Idle state before run is called. Reference count is zero (except for a possible external boost::intru...
Definition: AIStatefulTask.h:126
virtual ~AIStatefulTask()
Destructor.
Definition: AIStatefulTask.h:375
virtual char const * condition_str_impl(condition_type condition) const
Called to stringify a condition type for debugging output.
Definition: AIStatefulTask.cxx:1264
virtual void initialize_impl()
Called for base state bs_initialize.
Definition: AIStatefulTask.cxx:1280
void set_state(state_type new_state)
Definition: AIStatefulTask.cxx:1354
utils::FuzzyBool is_self_locked(AIStatefulTaskMutex const &stateful_task_mutex, AIStatefulTaskMutexNode const *handle)
void finish()
Definition: AIStatefulTask.cxx:1725
void kill()
Definition: AIStatefulTask.cxx:1302
bool signal(condition_type condition)
Definition: AIStatefulTask.cxx:1643
void abort()
Definition: AIStatefulTask.cxx:1690
void run(AIStatefulTask *parent, condition_type condition, on_abort_st on_abort=abort_parent)
Definition: AIStatefulTask.h:423
void run(Handler default_handler=Handler::immediate)
Definition: AIStatefulTask.h:430
void run(std::function< void(bool)> cb_function)
The same as above but use the immediate Handler.
Definition: AIStatefulTask.h:405
void run(Handler default_handler, std::function< void(bool)> cb_function)
Definition: AIStatefulTask.cxx:1192
void wait_until(AIWaitConditionFunc const &wait_condition, condition_type conditions)
Definition: AIStatefulTask.cxx:1627
void wait_until(AIWaitConditionFunc const &wait_condition, condition_type conditions, state_type new_state)
Definition: AIStatefulTask.h:512
void wait(condition_type conditions)
Definition: AIStatefulTask.cxx:1507
void yield()
Definition: AIStatefulTask.cxx:1748
void yield_frame(AIEngine *engine, unsigned int frames)
Definition: AIStatefulTask.cxx:1800
void target(Handler handler)
Definition: AIStatefulTask.cxx:1764
bool yield_if_not(Handler handler)
Definition: AIStatefulTask.cxx:1790
void yield_ms(AIEngine *engine, unsigned int ms)
Definition: AIStatefulTask.cxx:1809
Tasks defined by the library project are put into this namespace.
Definition: AIStatefulTask.h:857
boost::intrusive_ptr< TaskType > create(ARGS &&... args)
Convenience function to create tasks.
Definition: AIStatefulTask.h:894
Definition: AIStatefulTaskMutex.h:42
Definition: AIStatefulTask.h:107
Definition: AIStatefulTask.h:223
Definition: AIStatefulTask.h:164
Handle m_handle
Extra data that depends on m_type.
Definition: AIStatefulTask.h:189
special_t
A typed use by the constuctor Handler(special_t).
Definition: AIStatefulTask.h:173
@ immediate
Construct an immediate Handler.
Definition: AIStatefulTask.h:175
@ idle
Construct an idle Handler.
Definition: AIStatefulTask.h:174
type_t
The type of m_type.
Definition: AIStatefulTask.h:166
@ engine_h
An engine Handler.
Definition: AIStatefulTask.h:169
@ immediate_h
An immediate Handler.
Definition: AIStatefulTask.h:168
@ idle_h
An idle Handler.
Definition: AIStatefulTask.h:167
@ thread_pool_h
A thread pool Handler.
Definition: AIStatefulTask.h:170
friend std::ostream & operator<<(std::ostream &os, Handler const &handler)
Write a Handler to an ostream.
Definition: AIStatefulTask.cxx:1118
bool operator==(Handler handler) const
Return true when equivalent to handler.
Definition: AIStatefulTask.h:212
Handler(AIQueueHandle queue_handle)
Construct a Handler from an AIQueueHandle.
Definition: AIStatefulTask.h:197
bool is_immediate() const
Return true if this is an immediate Handler.
Definition: AIStatefulTask.h:201
Handler(special_t special)
Construct a special Handler.
Definition: AIStatefulTask.h:193
bool is_thread_pool() const
Return true if this is a thread pool handler.
Definition: AIStatefulTask.h:203
Handler(AIEngine *engine)
Construct a Handler from an AIEngine pointer.
Definition: AIStatefulTask.h:195
AIQueueHandle get_queue_handle() const
Return the AIQueueHandle to use (only call when appropriate).
Definition: AIStatefulTask.h:205
type_t m_type
The type of this Handler.
Definition: AIStatefulTask.h:190
bool is_engine() const
Return true if this is an engine Handler.
Definition: AIStatefulTask.h:199
Contains extra data that depends on the type of the Handler.
Definition: AIStatefulTask.h:178
AIQueueHandle queue_handle
The actual thread pool queue when this is a thread pool Handler.
Definition: AIStatefulTask.h:180
Handle(AIEngine *engine_)
Construct a Handle from an AIEngine pointer.
Definition: AIStatefulTask.h:185
AIEngine * engine
The actual engine when this is an engine Handler.
Definition: AIStatefulTask.h:179
Handle()
Construct an uninitialized Handle.
Definition: AIStatefulTask.h:183
Handle(AIQueueHandle queue_handle_)
Construct a Handle from a thread pool handle.
Definition: AIStatefulTask.h:187