| @ -0,0 +1,157 @@ | |||
| #pragma once | |||
| #include <functional> | |||
| #include <optional> | |||
| #include <list> | |||
| #include <mutex> | |||
| #include <memory> | |||
| #include <random> | |||
| namespace rp_piano { | |||
| class coro; | |||
| class abstract_channel; | |||
| template<typename T> | |||
| struct locked_list; | |||
| } | |||
| extern "C" { | |||
| void spawn(rp_piano::coro&&); | |||
| } | |||
| constexpr size_t queue_count = 4; | |||
| extern std::array<rp_piano::locked_list<rp_piano::coro>, queue_count> sched_queue; | |||
| namespace rp_piano { | |||
| template<typename T> | |||
| struct locked_list { | |||
| std::list<T> data; | |||
| std::mutex mtx; | |||
| std::optional<T> pop() { | |||
| std::lock_guard<std::mutex> _g(mtx); | |||
| if(data.size()) | |||
| { | |||
| T ret = std::move(data.front()); | |||
| data.pop_front(); | |||
| return ret; | |||
| } | |||
| return std::nullopt; | |||
| } | |||
| void push(std::list<T>&& in) { | |||
| std::lock_guard<std::mutex> _g(mtx); | |||
| data.splice(std::end(data), in); | |||
| } | |||
| size_t size() { | |||
| std::lock_guard<std::mutex> _g(mtx); | |||
| return data.size(); | |||
| } | |||
| }; | |||
| class abstract_channel { | |||
| public: | |||
| std::shared_ptr<locked_list<coro>> waiters; | |||
| abstract_channel() | |||
| : waiters(std::make_shared<locked_list<coro>>()) | |||
| {} | |||
| std::optional<coro> get_waiter(); | |||
| void put_waiter(std::list<coro>); | |||
| void receive_cb(); | |||
| virtual ~abstract_channel() = default; | |||
| }; | |||
| template<typename T> | |||
| class channel : public abstract_channel { | |||
| locked_list<T> values; | |||
| public: | |||
| void send(T&& v) { | |||
| values.push({std::forward<T>(v)}); | |||
| } | |||
| void send(std::list<T>&& v) { | |||
| values.push(std::forward<T>(v)); | |||
| } | |||
| std::optional<T> receive() { | |||
| receive_cb(); | |||
| return values.pop(); | |||
| } | |||
| }; | |||
| class coro { | |||
| using corotype = std::function<void(coro&)>; | |||
| std::list<coro> continuations; | |||
| corotype implementation = [](coro&){}; | |||
| std::optional<abstract_channel> go_wait; | |||
| bool resched = false; | |||
| public: | |||
| template<typename T> | |||
| coro(T v) | |||
| : implementation(v) | |||
| {} | |||
| coro() = default; | |||
| coro& then(coro&& v) | |||
| { | |||
| continuations.emplace_back(std::move(v)); | |||
| return continuations.back(); | |||
| } | |||
| void operator() () { | |||
| static thread_local std::default_random_engine eng{}; | |||
| if(go_wait) | |||
| { | |||
| go_wait.value().put_waiter({*this}); | |||
| go_wait.reset(); | |||
| return; | |||
| } | |||
| implementation(*this); | |||
| if(resched) | |||
| { | |||
| resched = false; | |||
| spawn(std::move(*this)); | |||
| return; | |||
| } | |||
| if(go_wait) | |||
| { | |||
| go_wait.value().put_waiter({*this}); | |||
| go_wait.reset(); | |||
| } else { | |||
| sched_queue[eng() & (sched_queue.size() - 1)].push(std::move(continuations)); | |||
| } | |||
| return; | |||
| } | |||
| coro& rewake_with(abstract_channel chan) { | |||
| go_wait = chan; | |||
| return *this; | |||
| } | |||
| void reschedule() { | |||
| resched = true; | |||
| } | |||
| }; | |||
| inline void abstract_channel::receive_cb() { | |||
| static thread_local std::default_random_engine generator; | |||
| if(waiters) | |||
| { | |||
| while(true) | |||
| { | |||
| auto waitee = waiters->pop(); | |||
| if(!waitee) | |||
| { | |||
| return; | |||
| } | |||
| sched_queue[generator() & (sched_queue.size() - 1)].push({waitee.value()}); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,14 @@ | |||
| #pragma once | |||
| #include <vector> | |||
| #include <string_view> | |||
| #include "rigid_paradise/piano/function.hpp" | |||
| extern "C" { | |||
| int piano(std::vector<std::string_view>&&); | |||
| void spawn(rp_piano::coro&&); | |||
| } | |||
| template<typename T> | |||
| rp_piano::coro start(T&& v) { | |||
| return rp_piano::coro(std::forward<T>(v)); | |||
| } | |||
| @ -0,0 +1,124 @@ | |||
| #include "rigid_paradise/piano/piano.h" | |||
| #include "rigid_paradise/piano/function.hpp" | |||
| #include <thread> | |||
| #include <atomic> | |||
| #include <chrono> | |||
| #include <iostream> | |||
| using namespace std::chrono_literals; | |||
| rp_piano::locked_list<rp_piano::abstract_channel> registry; | |||
| std::array<rp_piano::locked_list<rp_piano::coro>, queue_count> sched_queue; | |||
| std::atomic_bool running; | |||
| void clear_registry() { | |||
| auto sz = registry.data.size(); | |||
| while( sz != 0 ){ | |||
| sz--; | |||
| auto tmp = registry.pop(); | |||
| if(tmp) | |||
| if(tmp.value().waiters.use_count() != 1) | |||
| registry.push( { tmp.value() } ); | |||
| } | |||
| return; | |||
| } | |||
| void rp_piano::abstract_channel::put_waiter(std::list<rp_piano::coro> coros) { | |||
| waiters->push(std::move(coros)); | |||
| } | |||
| std::optional<rp_piano::coro> rp_piano::abstract_channel::get_waiter() { | |||
| return waiters->pop(); | |||
| } | |||
| void spawn(rp_piano::coro&& coro) | |||
| { | |||
| static thread_local std::default_random_engine eng{}; | |||
| sched_queue[eng() & (sched_queue.size() - 1)].push({std::move(coro)}); | |||
| } | |||
| int main(int argc, char** argv) | |||
| { | |||
| std::vector<std::string_view> args{argv, argv+argc}; | |||
| std::vector<std::thread> threads; | |||
| std::vector<std::shared_ptr<std::atomic_int>> activity; | |||
| std::mutex mtx; | |||
| std::atomic_bool poll_activity = false; | |||
| running.store(true); | |||
| for(auto n = 0; n < std::thread::hardware_concurrency(); ++n) | |||
| { | |||
| activity.push_back(std::make_shared<std::atomic_int>()); | |||
| threads.emplace_back([&, n](){ | |||
| size_t idx; | |||
| while(running) | |||
| { | |||
| auto data = sched_queue[idx & (sched_queue.size() - 1)].pop(); | |||
| if(data) | |||
| { | |||
| data.value()(); | |||
| if(poll_activity) | |||
| { | |||
| activity[n]->fetch_add(1); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| std::this_thread::yield(); | |||
| } | |||
| ++idx; | |||
| } | |||
| }); | |||
| } | |||
| auto ret = piano(std::move(args)); | |||
| while(running) | |||
| { | |||
| size_t cnt = 0; | |||
| for(auto& queue : sched_queue) | |||
| { | |||
| auto q = queue.size(); | |||
| std::cout << q << "\t"; | |||
| cnt+=q; | |||
| } | |||
| if(poll_activity) | |||
| { | |||
| bool test = false; | |||
| for(auto& a : activity) | |||
| { | |||
| test |= a->load(); | |||
| } | |||
| if(!test) | |||
| { | |||
| if(!cnt) | |||
| { | |||
| running.store(false); | |||
| } | |||
| } | |||
| poll_activity.store(false); | |||
| } | |||
| else | |||
| { | |||
| if(!cnt) | |||
| { | |||
| for(auto& a : activity) | |||
| { | |||
| *a = 0; | |||
| } | |||
| poll_activity.store(true); | |||
| } | |||
| } | |||
| std::cout << "pa="<< poll_activity.load() << "\tr="<< running.load() << std::endl; | |||
| std::this_thread::sleep_for(2s); | |||
| } | |||
| for(auto& t : threads) | |||
| { | |||
| t.join(); | |||
| } | |||
| return ret; | |||
| } | |||
| @ -0,0 +1,48 @@ | |||
| #include "rigid_paradise/piano/piano.h" | |||
| #include <iostream> | |||
| #include <thread> | |||
| #include <chrono> | |||
| using namespace std::chrono_literals; | |||
| std::mutex cout_tex; | |||
| volatile int dummy; | |||
| void a() | |||
| { | |||
| dummy = 1; | |||
| } | |||
| int piano(std::vector<std::string_view>&& args) { | |||
| std::cout<<"Hello piano!"<<std::endl; | |||
| auto root = rp_piano::coro{ | |||
| [](rp_piano::coro&){ | |||
| std::lock_guard _l(cout_tex); | |||
| std::cout<<"Hello coro"<<std::endl; | |||
| } | |||
| }; | |||
| root.then( | |||
| [](rp_piano::coro&){ | |||
| std::lock_guard _l(cout_tex); | |||
| std::cout<<"Hello continuation"<<std::endl; | |||
| } | |||
| ).then( | |||
| [](rp_piano::coro&){ | |||
| std::lock_guard _l(cout_tex); | |||
| std::cout<<"Hello continuation of continuation"<<std::endl; | |||
| auto i = 100000000; | |||
| while(i>0) { | |||
| spawn( | |||
| [=](rp_piano::coro& me){ | |||
| a(); | |||
| me.reschedule(); | |||
| } | |||
| ); | |||
| --i; | |||
| } | |||
| } | |||
| ); | |||
| spawn( | |||
| std::move(root) | |||
| ); | |||
| return 0; | |||
| } | |||