#include #include #include #include #include "../trunk/c++script.h" static bool debug_await = false; static bool debug_destruct = false; static bool debug_dump = false; using namespace std; template struct coro_await_resume{ coroutine_handle<> caller; promise_type *prom; coro_await_resume(coroutine_handle<> _h, promise_type *_prom) : caller(_h), prom(_prom) {} bool await_ready() noexcept{ if (debug_await) cerr << format("\tawait_resume num={} await_ready\n",num); return false; } coroutine_handle<> await_suspend (coroutine_handle<> h) noexcept { if (debug_await) cerr << format("\tawait_resume num={} await_suspend may_destroy={}\n",num,prom->may_destroy); if (prom->may_destroy) h.destroy(); return caller; } void await_resume() noexcept{ if (debug_await) cerr << format ("\tawait_resume num={} await_resume\n",num); if constexpr (!std::is_same_v){ prom->had_value = prom->has_value; prom->has_value = false; } } }; template struct promise_yval_base{ bool may_destroy = false; // if true, all CORO object referencing this promise are gone. // See SHARED_HANDLE bool running = true; bool has_value = false; bool had_value = false; YVAL yield_val{}; coroutine_handle<> caller = noop_coroutine(); bool has_data(){ return had_value; } YVAL get(){ return yield_val; }; auto yield_value (YVAL _val){ if (has_value) cerr << "************************** has_value\n"; has_value = true; yield_val = _val; return coro_await_resume(caller,this); } }; template struct promise_yval_base{ bool may_destroy = false; bool running = true; bool has_value = false; bool had_value = false; coroutine_handle<> caller = noop_coroutine(); }; template struct promise_yval; template struct promise_yval: promise_yval_base{ RVAL retval{}; RVAL get_result(){ return retval; } void return_value(RVAL val){ //static_assert(false,"coroutine with different co_yield and co_return types not supported"); this->has_value = true; this->retval = val; this->running = false; } }; template struct promise_yval: promise_yval_base{ void return_void(){ this->running = false; } }; template struct promise_yval{ bool may_destroy = false; bool running = true; coroutine_handle<> caller = noop_coroutine(); void return_void(){ running = false; }; }; template struct promise_yval: promise_yval_base{ void return_value(VAL val){ this->has_value = true; this->yield_val = val; this->running = false; } }; template struct CORO; struct suspend_test{ coroutine_handle<> me; suspend_test(coroutine_handle<> _me): me(_me){} bool await_ready(){ return false; } auto await_suspend(coroutine_handle<> h){ cerr << "suspend_test suspend\n"; return me; } void await_resume(){ } }; template concept HasResumeMethod = requires(T obj) { obj.await_resume(); }; template concept Hasasio = requires(T obj) { obj.asio; }; struct STARTING{ coroutine_handle<> handle; int num; STARTING(coroutine_handle<> _handle, int _num) : handle(_handle), num(_num){} }; static std::deque starting; // Coroutine started but stuck in suspend_always static void remove_handle(coroutine_handle<> handle) { for (auto it=starting.begin(); it != starting.end(); it++){ if (it->handle.address() == handle.address()){ if (debug_await) cout << format ("remove_handle num={}\n",it->num); starting.erase(it); break; } } } struct COROSTATS{ void *promise; int num; bool *awaiting; bool *asio_awaiting; COROSTATS(void *_p, int _num, bool &_awaiting, bool &_asio_awaiting) : promise(_p), num(_num), awaiting(&_awaiting), asio_awaiting(&_asio_awaiting) {} COROSTATS() = delete; }; static vector corostats; static void corostats_remove (void *promise) { for (auto it = corostats.begin(); it != corostats.end(); it++){ if (it->promise == promise){ corostats.erase(it); break; } } } static void corostats_dump() { for (auto &c:corostats){ cout << format ("\t\t\tCORO num={} awaiting={} asio_awaiting={}\n",c.num,*c.awaiting,*c.asio_awaiting); } } static bool corostats_is_asio_awaiting() { bool ret = false; for (auto &c:corostats){ if (*c.asio_awaiting){ ret = true; break; } } return ret; } template struct promise_type_def: promise_yval{ promise_type_def(){ corostats.emplace_back(this,num,awaiting,asio_awaiting); } ~promise_type_def(){ if (debug_destruct) cerr << format ("destruct promise_type num={}\n",num); corostats_remove (this); } auto get_return_object(){ auto h = coroutine_handle::from_promise(*this); if constexpr (std::is_same_v){ starting.emplace_back(h,num); } return CORO(this,h); } init_suspend initial_suspend(){ if (debug_await) cerr << format ("\tinitial_suspend num={}\n",num); if constexpr (std::is_same_v){ auto h = coroutine_handle::from_promise(*this); return init_suspend(h); }else{ return {}; } } auto final_suspend() noexcept { if (debug_await) cerr << format("\tfinal_suspend num={} may_destroy={}\n",num,this->may_destroy); if constexpr (std::is_same_v){ // We could use suspend_never for this case // but there is a problem. If a coroutine A is awaiting this coroutine // and suspend_never is used, then A won't notice we are ending // So we are using coro_await_resume here as well. return coro_await_resume(this->caller,this); }else{ return coro_await_resume(this->caller,this); } } void unhandled_exception(){ this->running = false; } /* Complex situation. You have a coroutine with suspend_never (this is the default) CORO<...,suspend_never> coro(){ ... co_await something; ... } CORO<> caller(){ auto g = coro(); co_await g; } You can be sure that when you execute the "co_await g" statement, the coroutine coro() is now executing "co_await something". The following game is performed by the various awaitable class. -While "something" is running, coroutine coro() will be suspended. -When "co_await g" will be executed, the avaiable will see that coro() is not ready (await_ready() returns false). caller() will suspend and coro() will be resumed. Now coro() will think that "something" is done, calls its await_resume() and continue. The following code intercepts all awaitables and set/unset the flag awaiting in the promise. When the caller performs "co_await g", it will know that coro() is currently awaiting and won't try to resume it. The await_suspend() is returning noop_coroutine() instead of the handle of coro(). */ bool awaiting = false; bool asio_awaiting = false; // We are awaiting on an asio request // so we may have to resume the io_context template struct transform_await{ await expr; using resume_ret = decltype(expr.await_resume()); using suspend_ret = decltype(expr.await_suspend(coroutine_handle<>{})); promise_type_def &prom; transform_await(await&&_expr, promise_type_def &_prom): expr(_expr), prom(_prom){ if constexpr (Hasasio){ prom.asio_awaiting = true; } } transform_await(await&_expr, promise_type_def &_prom): expr(_expr), prom(_prom) { if constexpr (Hasasio){ prom.asio_awaiting = true; } } bool await_ready(){ return expr.await_ready(); } suspend_ret await_suspend(coroutine_handle h){ if constexpr (is_same_v){ prom.awaiting = true; expr.await_suspend(h); }else if constexpr(is_same_v){ auto ret = expr.await_suspend(h); if (ret){ prom.awaiting = true; } return ret; }else{ auto ret = expr.await_suspend(h); prom.awaiting = true; return ret; } } resume_ret await_resume(){ prom.awaiting = false; prom.asio_awaiting = false; return expr.await_resume(); } }; template auto await_transform(T && expr){ if constexpr (HasResumeMethod){ return transform_await(expr,*this); }else{ return transform_await(expr.operator co_await(),*this); } } }; //static CORO<10,void,void,suspend_test> endnow(auto &g); static CORO<10,void,void,suspend_never> endnow(auto &g); template struct CORO_common{ using promise_type = promise_type_def; promise_type *prom; coroutine_handle handle; operator bool (){ return prom->running; }; bool is_awaiting(){ return prom->awaiting; } bool is_asio_awaiting(){ return prom->asio_awaiting; } void resume(){ handle.resume(); } struct coro_await{ coroutine_handle<> handle; promise_type *prom; coro_await(coroutine_handle<> _h, promise_type *_prom) : handle(_h), prom(_prom) { remove_handle(handle); }; bool await_ready(){ if constexpr (!std::is_same_v){ if (debug_await) cerr << format("\tawait_ready num={} running={} has_value={}\n",num,prom->running,prom->has_value); return !prom->running || prom->has_value; }else{ if (debug_await) cerr << format("\tawait_ready num={} running={}\n",num,prom->running); return !prom->running; } } coroutine_handle<> await_suspend (coroutine_handle<> h){ if (debug_await) cerr << format("\tawait_suspend num={} awaiting={}\n",num,prom->awaiting); prom->caller = h; return prom->awaiting ? noop_coroutine() : handle; } YVAL await_resume(){ if constexpr (!std::is_same_v){ if (debug_await) cerr << format ("\tawait_resume num={} val={}\n",num,prom->yield_val); prom->had_value = prom->has_value; prom->has_value = false; return prom->yield_val; }else{ if (debug_await) cerr << format ("\tawait_resume num={}\n",num); } } }; coro_await operator co_await(){ return coro_await(handle,prom); }; }; template struct CORO_base; template struct CORO_base: CORO_common{ using promise_type = promise_type_def; //promise_type *prom; YVAL get(){ return this->prom->get(); } bool has_data(){ return this->prom->has_data(); } struct CORO_end{ }; struct CORO_iter{ CORO_base *coro; CORO_iter(CORO_base *_coro) : coro(_coro) {} bool operator == (const CORO_end &){ return !(*coro); } CORO_iter operator ++(){ //cout << format("iter has_data1={} {}\n",coro->has_data(),coro->prom->has_value); if (*coro){ endnow(*coro); #if 0 cout << format ("waitnow coro->get={}\n",coro->get()); if (g){ cout << "endnow running\n"; }else{ cout << "endnow stopped\n"; } #endif } //cout << format("iter has_data2={} {}\n",coro->has_data(),coro->prom->has_value); return *this; } YVAL operator * () const { return coro->get(); } }; CORO_iter begin() { auto ret = CORO_iter(this); ++ret; return ret; } CORO_end end() const { return CORO_end(); } }; template struct CORO_base: CORO_common{ using promise_type = promise_type_def; RVAL get_result(){ return this->prom->get_result(); } }; template struct CORO_base: CORO_common{ using promise_type = promise_type_def; }; template struct CORO: CORO_base{ using promise_type = promise_type_def; struct SHARED_HANDLE{ promise_type *prom; coroutine_handle handle; SHARED_HANDLE(promise_type *_p, coroutine_handle _h): prom(_p),handle(_h){} ~SHARED_HANDLE(){ // All CORO object referencing a coroutine are gone. // If a coroutine is still running, we can't destroy it // By setting may_destroy, we allow the coroutine to self destroy later. if (prom->running){ if (debug_destruct) cerr << format ("coro num={} delay destroy\n",num); prom->may_destroy = true; }else{ if (debug_destruct) cerr << format ("coro num={} handle.destroy()\n",num); handle.destroy(); } } }; std::shared_ptr p_handle; CORO(promise_type *_p, coroutine_handle _h){ this->prom = _p; this->handle = _h; p_handle = make_shared(_p,_h); } ~CORO(){ if (debug_destruct) cerr << format ("destruct CORO {}\n",num); } }; #include static boost::asio::io_context *pt_ctx = nullptr; static bool debug_endnow = false; // suspend_never //static CORO<10,void,void,suspend_test> endnow(auto &g) static CORO<10,void,void,suspend_never> endnow(auto &g) { if (debug_endnow) cout << format("endnow start awaiting={}\n",g.is_awaiting()); if (pt_ctx != nullptr && g.is_asio_awaiting()){ if (debug_endnow) cout << "endnow pt_ctx->run()\n"; pt_ctx->run_one(); if (debug_endnow) cout << "endnow pt_ctx->run() done\n"; } co_await g; if (debug_endnow) cout << "endnow end\n"; co_return; } static bool debug_event = false; template struct CORO_EVENT{ CORO_EVENT(){} CORO_EVENT(const CORO_EVENT &) = delete; CORO_EVENT & operator =(const CORO_EVENT &) = delete; struct event_await{ CORO_EVENT &event; event_await *next = nullptr; coroutine_handle<> handle = noop_coroutine(); event_await(CORO_EVENT &_event): event(_event){} bool await_ready(){ return false; } void await_suspend (coroutine_handle<> h){ handle = h; next = event.first; event.first = this; } VAL await_resume(){ return event.retval; } }; VAL retval{}; bool result_available=false; RES result{}; struct event_await *first = nullptr; event_await operator co_await(){ return event_await(*this); } void send(VAL val){ if (debug_event) cerr << format ("event.send={}\n",val); retval = std::move(val); auto pt = first; first = nullptr; while (pt != nullptr){ //cerr << "resume one\n"; auto next = pt->next; pt->handle.resume(); pt = next; } if (debug_event) cerr << format ("event.send={} done\n",val); } void set_result(RES res){ result_available=true; result=std::move(res); } bool has_result() const { return result_available; } RES get_result() { result_available = false; return result; } }; CORO<100,void,void,suspend_always> loop_event_one_a(CORO_EVENT &event, int num) { cout << format (" wait event_one_a {}\n",num); auto val = co_await event; cout << format (" done event_one_a {} val={}\n",num,val); } CORO<100> loop_event_one(CORO_EVENT &event, int num) { cout << format (" wait event_one {}\n",num); auto val = co_await event; cout << format (" done event_one {} val={}\n",num,val); } CORO<100> loop_event(CORO_EVENT &event, int num) { co_await loop_event_one_a(event,num); co_await loop_event_one(event,num); while (true){ cout << format (" wait event {}\n",num); auto val = co_await event; cout << format (" done event {} val={}\n",num,val); if (val == 99) break; } } CORO<101> loop_coro_events(int nb) { CORO_EVENT event; for (int i=0; i loop0(int nb) { int i = 0; int last = nb - 1; for (; i loop0_mult(int nb) { auto g = loop0 (nb); while (g){ int val = co_await g; cout << format("\t\tloop0_mult val={}\n",val); co_yield val*10; } } CORO<1> loop1(int nb) { auto g = loop0_mult (nb); while (g){ int val = co_await g; if (g.has_data()){ cout << format("\tloop1 val={} get={}\n",val,g.get()); } } } CORO<2,int> loop2(int nb) { cout << "\tloop2 execute\n"; int last = nb - 1; for (int i=0; i loop3 () { cout << format("loop3 execute\n"); auto g1 = loop1(4); co_await g1; auto g2 = loop2(30); while (g2){ cout << "loop3 await g2\n"; int val = co_await g2; cout << format("loop3 get={} val={}\n",g2.get(),val); } cout << "loop3 fin\n"; } #include CORO<40> coro_nothing() { cout << "coro_nothing\n"; co_return; } #include void do_nothing(int); std::generator stdgen(int nb) { for (int i=0; i genere_minimal(int start, int end) { for (int i=start; i await_suspend (coroutine_handle<> h){ t.async_wait([h](auto ec){ h.resume(); }); remove_handle(h); if (starting.size() > 0){ auto reth = starting.front(); cout << format("resume a starting corouting num={}\n",reth.num); starting.pop_front(); return reth.handle; }else{ // Must resume the io executor //pt_ctx->run_one(); return noop_coroutine(); } } void await_resume(){ } }; static time_t time_start = 0; static int difft() { return time(nullptr) - time_start; } static string dbgid(int id, int level) { return format ("id={},{}{:{}}",id,difft(),"",4+level*4); } static bool debug_genere = false; CORO<4,int,void,suspend_always> genere(int id, int start, int end, int waitsec,boost::asio::io_context &ctx) { auto dbg = [id](){ return dbgid(id,2); }; if (debug_genere) cout << format ("{}genere debut\n",dbg()); boost::asio::steady_timer t(ctx); for (int i=start; i 0){ #if 1 t.expires_after(chrono::milliseconds(waitsec)); if (debug_genere) cout << format ("{}genere avant simul_wait\n",dbg()); co_await simul_await(t); //co_await t.async_wait(boost::asio::use_awaitable); if (debug_genere) cout << format ("{}genere après simul_wait\n",dbg()); #else auto g = stdgen(10); co_await g; #endif } if (debug_genere) cout << format ("{}genere avant co_yield i={}\n",dbg(),i); co_yield i; if (debug_genere) cout << format("{}genere apres co_yield\n",dbg()); } co_yield end; if (debug_genere) cout << format ("{}genere fin\n",dbg()); } #include "../trunk/getnow.h" void print_iter (auto &g) { int nbrep=0; for (auto i:g) { cout << format ("print i={} nbrep={} has_data={}\n",i,nbrep,g.has_data()); //nbrep++; //if (nbrep == 3) exit(0); } } CORO<5> print(int waitsec, boost::asio::io_context &ctx, int nb) { if (nb > 1000){ { auto g = genere_minimal(0,nb); auto start = getnow(); long total = 0; for (auto i:g) total += i; auto end = getnow(); showtime ("print range",start,end); cout << format ("total={}\n",total); cout << format ("{} par seconde\n",getnow_formatnum((double)nb/(end-start)*1000000)); } { auto g = genere_minimal(0,nb); auto start = getnow(); long total = 0; while (g){ //cout << "avant co_await\n"; int val = co_await g; //cout << format("print co_await {} val={} has_data={}\n",(bool)g,val,g.has_data()); if (g.has_data()) total += val; } //cout << "end1\n"; auto end = getnow(); //cout << "end2\n"; showtime ("print co_await",start,end); //cout << "end3\n"; cout << format ("total={}\n",total); cout << format ("{} par seconde\n",getnow_formatnum((double)nb/(end-start)*1000000)); } }else{ auto g = genere(0,1,nb,waitsec,ctx); if (nb==6){ while (g){ int val = co_await g; cout << format ("print val={} has_value={}\n",val,g.has_data()); } }else{ print_iter(g); } } co_return; } void execstdgen(int nb) { auto g = stdgen(nb); auto start = getnow(); long total = 0; for (auto i:g){ total += i;; } auto end = getnow(); showtime ("stdgen",start,end); cout << format ("total={}\n",total); cout << format ("{} par seconde\n",(double)nb/(end-start)*1000000); } CORO<1000> waitevent(CORO_EVENT &event) { cerr << "waitevent start\n"; int val = co_await event; cerr << format("waitevent end val={}\n",val); } CORO<20,void,int> coreturn() { co_return 99; } template void co_spawn(coro &&co) { CORO_EVENT event; co(event); event.send(1); } #if 0 CORO<1,int,float> corobug() { co_return 1.0; } #endif CORO<103> simul_wait(boost::asio::io_context &ctx, CORO_EVENT &event, int waitmilli) { cout << "\tsimul_wait\n"; if (waitmilli > 0){ boost::asio::steady_timer t(ctx); t.expires_after(chrono::milliseconds(waitmilli)); co_await simul_await(t); } cout << "\tsimul_wait end\n"; int val = co_await event; cout << format("\tsimul_wait end 2 val={}\n",val); co_return; } CORO<104,int> simul_send(boost::asio::io_context &ctx, CORO_EVENT &event, int waitmilli) { cout << "\tsimul_send\n"; if (waitmilli > 0){ boost::asio::steady_timer t(ctx); t.expires_after(chrono::milliseconds(waitmilli)); co_await simul_await(t); } cout << "\tsimul_send end\n"; event.send (22); cout << "\tsimul_send end 2\n"; co_yield 1; } CORO<105> simul_testwait(boost::asio::io_context &ctx, CORO_EVENT &event, int waitmilli) { cout << "simul_testwait start\n"; co_await simul_wait (ctx,event,waitmilli); cout << "simul_testwait end\n"; } CORO<106> simul_sendtest(boost::asio::io_context &ctx, CORO_EVENT &event, int waitmilli) { //for (auto i:simul_send(ctx,event)) cout << format ("yield {}\n",i); #if 0 for (int val:simul_send(ctx,event)){ cout << format("simul_sendtest end val={}\n",val); } #else int val = co_await simul_send(ctx,event,waitmilli); cout << format("simul_sendtest end val={}\n",val); #endif co_return; } static void simul_test(boost::asio::io_context &ctx, int waitmilli, int waitmilli2) { cout << "simul_test start\n"; CORO_EVENT event; for (int i=0; i<3; i++){ simul_testwait(ctx,event,waitmilli); } simul_sendtest(ctx,event,waitmilli2); cout << "simul_test end\n"; } #define RUN run_one CORO<11> test_endnow(int id, CORO_EVENT &event, auto &g, boost::asio::io_context &ctx) { auto dbg = [id](){ return dbgid(id,1); }; if (debug_endnow) cout << format ("{}test_endnow debut\n",dbg()); while (g){ if (0 && g.is_asio_awaiting()){ if (debug_endnow) cout << format ("test_endnow id={} ctx.run_one()\n",id); ctx.RUN(); if (debug_endnow) cout << format ("test_endnow id={} ctx.run_one() done\n",id); } if (debug_endnow) cout << format ("{}test_endnow avant co_await g\n",dbg()); int res = co_await g; if (g.has_data()) event.set_result(res); if (debug_endnow) cout << format ("{}test_endnow res={} has_data={}\n",dbg(),res,g.has_data()); if (debug_endnow) cout << format ("{}test_endnow avant co_await event\n",dbg()); int val = co_await event; if (debug_endnow) cout << format ("{}test_endnow event={}\n",dbg(),val); if (val == -1) break; } if (debug_endnow) cout << format("{}test_endnow fin\n",dbg()); } CORO<12> testendnow(int id, boost::asio::io_context &ctx, int nbrep, int waitsec) { auto dbg = [id](){ return dbgid(id,0); }; if (debug_endnow) cout << format ("{}testendnow start\n",dbg()); auto g = genere(id,1,nbrep,waitsec,ctx); CORO_EVENT event; auto gg = test_endnow(id,event,g,ctx); auto start = getnow(); int nb=1; int total = 0; while (g){ if (debug_endnow) cout << format ("{}testendnow g.is_awaiting={} gg.is_awaiting()={}\n",dbg(),g.is_awaiting(),gg.is_awaiting()); if (0 && !g.is_awaiting() && gg.is_awaiting()){ g.resume(); }else if (g.is_asio_awaiting() && gg.is_awaiting()){ if (debug_endnow) cout << format ("{}testendnow ctx.run_one()\n",dbg()); if (debug_dump) corostats_dump(); do{ int nbtask = ctx.RUN(); if (debug_endnow) cout << format ("{}testendnow ctx.run_one() nbtasks={}\n",dbg(),nbtask); } while (corostats_is_asio_awaiting()); if (0 && corostats_is_asio_awaiting()){ if (debug_endnow) cout << format ("{}testendnow poll + ctx.run_one()\n",dbg()); ctx.RUN(); } if (debug_endnow) cout << format ("{}testendnow ctx.run_one() done\n",dbg()); }else{ if (debug_endnow) cout << format ("{}testendnow not awaiting corostats_is_asio_awaiting={}\n",dbg(),corostats_is_asio_awaiting()); } if (event.has_result()){ auto res = event.get_result(); total += res; if (nbrep < 1000) cout << format ("{}testendnow res={} starting.size()={}\n",dbg(),res,starting.size()); } event.send(nb); nb++; } event.send(-1); auto end = getnow(); showtime ("testendnow",start,end); cout << format ("{} par seconde\n",getnow_formatnum((double)nb/(end-start)*1000000)); cout << format ("total = {}\n",total); co_return; } #include int main (int argc, char *argv[]) { cpps::progdesc ("Prototype pour comprendre les coroutines"); cpps::option range ('r',"range","test range",false,false); cpps::option stdgen ('s',"stdgen","test std::generator",false,false); cpps::option event ('e',"event","test CORO_EVENT",false,false); cpps::option spawn ('p',"spawn","test co_spawn",false,false); cpps::option rettest ('R',"rettest","test co_return",false,false); cpps::option simul ('S',"simul","Simulation d'un serveur",false,false); cpps::option test_endnow('t',"endnow","test endnow",false,false); cpps::options_section("Variations"); cpps::option waitsec ('w',"waitsec","Milli-secondes à atendre dans le test range",0,false); cpps::option waitsec2 ('W',"waitsec2","Milli-secondes à atendre dans le test range",0,false); cpps::option nbrep ('n',"nbrep","Nombre de répétitions",5,false); cpps::option nbpar ('P',"nbpar","Nombre de processus en parallèle",1,false); cpps::options_section("Debug"); cpps::option verb_await (' ',"debug_await","Affiche debug await",false,false); cpps::option verb_endnow (' ',"debug_endnow","Affiche debug endnow",false,false); cpps::option verb_event (' ',"debug_event","Affiche debug event",false,false); cpps::option verb_genere (' ',"debug_genere","Affiche debug genere",false,false); cpps::option verb_destruct (' ',"debug_destruct","Affiche destructeurs",false,false); cpps::option verb_dump (' ',"debug_dump","Présente l'état des coroutines",false,false); cpps::endoptions(argc,argv); debug_await = verb_await.val; debug_endnow = verb_endnow.val; debug_genere = verb_genere.val; debug_event = verb_event.val; debug_destruct = verb_destruct.val; debug_dump = verb_dump.val; if (stdgen.val){ execstdgen(nbrep.val); }else if (spawn.val){ //CORO_EVENT event; cerr << "avant co_spawn\n"; co_spawn(waitevent); cerr << "apres co_spawn\n"; //event.send(1); }else if (range.val){ boost::asio::io_context io_context(1); print(waitsec.val,io_context,nbrep.val); io_context.run(); }else if (event.val){ loop_events(nbrep.val); }else if (rettest.val){ auto g = coreturn(); cout << format ("coreturn {}\n",g.get_result()); }else if (simul.val){ boost::asio::io_context io_context(1); simul_test(io_context,waitsec.val,waitsec2.val); io_context.run(); cout << "end io_context.run()\n"; }else if (test_endnow.val){ time_start = time(nullptr); boost::asio::io_context io_context(1); pt_ctx = &io_context; auto work_guard = boost::asio::make_work_guard(io_context); if (nbpar.val == 1){ testendnow(0,io_context,nbrep.val,waitsec.val); }else{ for (int i=0; i(&g1),sizeof(g1),static_cast(g1.prom)); } return 0; }