#pragma once #include "executor.hpp" // https://hackmd.io/@sysprog/concurrency-atomics namespace tf { // ---------------------------------------------------------------------------- // Async // ---------------------------------------------------------------------------- // Function: async template auto Executor::async(P&& params, F&& f) { _increment_topology(); // async task with runtime: [] (tf::Runtime&) { ... } if constexpr (std::is_invocable_v) { using R = std::invoke_result_t; std::packaged_task p(std::forward(f)); auto fu{p.get_future()}; _schedule_async_task(animate( std::forward

(params), nullptr, nullptr, 0, std::in_place_type_t{}, [p=make_moc(std::move(p))](tf::Runtime& rt) mutable { p.object(rt); } )); return fu; } // async task without runtime: [] () { ... } else { using R = std::invoke_result_t; std::packaged_task p(std::forward(f)); auto fu{p.get_future()}; _schedule_async_task(animate( std::forward

(params), nullptr, nullptr, 0, std::in_place_type_t{}, [p=make_moc(std::move(p))]() mutable { p.object(); } )); return fu; } } // Function: async template auto Executor::async(F&& f) { return async(DefaultTaskParams{}, std::forward(f)); } // ---------------------------------------------------------------------------- // Silent Async // ---------------------------------------------------------------------------- // Function: silent_async template void Executor::silent_async(P&& params, F&& f) { _increment_topology(); auto node = animate( std::forward

(params), nullptr, nullptr, 0, // handle std::in_place_type_t{}, std::forward(f) ); _schedule_async_task(node); } // Function: silent_async template void Executor::silent_async(F&& f) { silent_async(DefaultTaskParams{}, std::forward(f)); } // ---------------------------------------------------------------------------- // Async Helper Methods // ---------------------------------------------------------------------------- // Procedure: _schedule_async_task inline void Executor::_schedule_async_task(Node* node) { // Here we don't use _this_worker since _schedule will check if the // given worker belongs to this executor. (pt::worker) ? _schedule(*pt::worker, node) : _schedule(node); } // Procedure: _tear_down_async inline void Executor::_tear_down_async(Node* node) { // from runtime if(node->_parent) { node->_parent->_join_counter.fetch_sub(1, std::memory_order_release); } // from executor else { _decrement_topology(); } recycle(node); } // ---------------------------------------------------------------------------- // Silent Dependent Async // ---------------------------------------------------------------------------- // Function: silent_dependent_async template ...>, void>* > tf::AsyncTask Executor::silent_dependent_async(F&& func, Tasks&&... tasks) { return silent_dependent_async( DefaultTaskParams{}, std::forward(func), std::forward(tasks)... ); } // Function: silent_dependent_async template && all_same_v...>, void>* > tf::AsyncTask Executor::silent_dependent_async( P&& params, F&& func, Tasks&&... tasks ){ std::array array = { std::forward(tasks)... }; return silent_dependent_async( std::forward

(params), std::forward(func), array.begin(), array.end() ); } // Function: silent_dependent_async template , AsyncTask>, void>* > tf::AsyncTask Executor::silent_dependent_async(F&& func, I first, I last) { return silent_dependent_async(DefaultTaskParams{}, std::forward(func), first, last); } // Function: silent_dependent_async template && !std::is_same_v, AsyncTask>, void>* > tf::AsyncTask Executor::silent_dependent_async( P&& params, F&& func, I first, I last ) { _increment_topology(); size_t num_dependents = std::distance(first, last); AsyncTask task(animate( std::forward

(params), nullptr, nullptr, num_dependents, std::in_place_type_t{}, std::forward(func) )); for(; first != last; first++){ _process_async_dependent(task._node, *first, num_dependents); } if(num_dependents == 0) { _schedule_async_task(task._node); } return task; } // ---------------------------------------------------------------------------- // Dependent Async // ---------------------------------------------------------------------------- // Function: dependent_async template ...>, void>* > auto Executor::dependent_async(F&& func, Tasks&&... tasks) { return dependent_async(DefaultTaskParams{}, std::forward(func), std::forward(tasks)...); } // Function: dependent_async template && all_same_v...>, void>* > auto Executor::dependent_async(P&& params, F&& func, Tasks&&... tasks) { std::array array = { std::forward(tasks)... }; return dependent_async( std::forward

(params), std::forward(func), array.begin(), array.end() ); } // Function: dependent_async template , AsyncTask>, void>* > auto Executor::dependent_async(F&& func, I first, I last) { return dependent_async(DefaultTaskParams{}, std::forward(func), first, last); } // Function: dependent_async template && !std::is_same_v, AsyncTask>, void>* > auto Executor::dependent_async(P&& params, F&& func, I first, I last) { _increment_topology(); size_t num_dependents = std::distance(first, last); // async with runtime: [] (tf::Runtime&) {} if constexpr (std::is_invocable_v) { using R = std::invoke_result_t; std::packaged_task p(std::forward(func)); auto fu{p.get_future()}; AsyncTask task(animate( std::forward

(params), nullptr, nullptr, num_dependents, std::in_place_type_t{}, [p=make_moc(std::move(p))] (tf::Runtime& rt) mutable { p.object(rt); } )); for(; first != last; first++) { _process_async_dependent(task._node, *first, num_dependents); } if(num_dependents == 0) { _schedule_async_task(task._node); } return std::make_pair(std::move(task), std::move(fu)); } // async without runtime: [] () {} else { using R = std::invoke_result_t>; std::packaged_task p(std::forward(func)); auto fu{p.get_future()}; AsyncTask task(animate( std::forward

(params), nullptr, nullptr, num_dependents, std::in_place_type_t{}, [p=make_moc(std::move(p))] () mutable { p.object(); } )); for(; first != last; first++) { _process_async_dependent(task._node, *first, num_dependents); } if(num_dependents == 0) { _schedule_async_task(task._node); } return std::make_pair(std::move(task), std::move(fu)); } } // ---------------------------------------------------------------------------- // Dependent Async Helper Functions // ---------------------------------------------------------------------------- // Procedure: _process_async_dependent inline void Executor::_process_async_dependent( Node* node, tf::AsyncTask& task, size_t& num_dependents ) { auto& state = std::get_if(&(task._node->_handle))->state; add_successor: auto target = Node::AsyncState::UNFINISHED; // acquires the lock if(state.compare_exchange_weak(target, Node::AsyncState::LOCKED, std::memory_order_acq_rel, std::memory_order_acquire)) { task._node->_successors.push_back(node); state.store(Node::AsyncState::UNFINISHED, std::memory_order_release); } // dep's state is FINISHED, which means dep finished its callable already // thus decrement the node's join counter by 1 else if (target == Node::AsyncState::FINISHED) { num_dependents = node->_join_counter.fetch_sub(1, std::memory_order_acq_rel) - 1; } // another worker adding its async task to the same successors of this node else { goto add_successor; } } // Procedure: _tear_down_dependent_async inline void Executor::_tear_down_dependent_async(Worker& worker, Node* node) { auto handle = std::get_if(&(node->_handle)); // this async task comes from Executor auto target = Node::AsyncState::UNFINISHED; while(!handle->state.compare_exchange_weak(target, Node::AsyncState::FINISHED, std::memory_order_acq_rel, std::memory_order_relaxed)) { target = Node::AsyncState::UNFINISHED; } // spawn successors whenever their dependencies are resolved worker._cache = nullptr; for(size_t i=0; i_successors.size(); ++i) { if(auto s = node->_successors[i]; s->_join_counter.fetch_sub(1, std::memory_order_acq_rel) == 1 ) { if(worker._cache) { _schedule(worker, worker._cache); } worker._cache = s; } } // now the executor no longer needs to retain ownership if(handle->use_count.fetch_sub(1, std::memory_order_acq_rel) == 1) { recycle(node); } _decrement_topology(); } } // end of namespace tf -----------------------------------------------------