@ -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; | |||||
} |