diff --git a/include/ircd/ctx/when.h b/include/ircd/ctx/when.h index 20e8d3495..a87381c7e 100644 --- a/include/ircd/ctx/when.h +++ b/include/ircd/ctx/when.h @@ -13,55 +13,18 @@ namespace ircd::ctx { - template future when_any(it first, const it &last); template future when_all(it first, const it &last); + template future when_any(it first, const it &last); } -/// Returns a future which becomes ready when all of the futures in the -/// collection become ready. This future has a void payload to minimize -/// its cost since this indication is positively unate. -template -ircd::ctx::future -ircd::ctx::when_all(it first, - const it &last) +// Internal interface +namespace ircd::ctx::when { - static const auto then - { - [](promise &p) - { - if(!p.valid()) - return; - - if(refcount(p.state()) < 2) - return p.set_value(); - - return remove(p.state(), p); - } - }; - - promise p; - const auto set_then - { - [&p](it &f) - { - state(*f).then = [p] - (shared_state_base &sb) mutable - { - if(sb.then) - then(p); - }; - } - }; - - future ret(p); - for(; first != last; ++first) - if(is(state(*first), future_state::PENDING)) - set_then(first); - - if(refcount(p.state()) <= 1) - p.set_value(); - - return ret; + template auto &state(const future &); + void all_then(promise &p); + template void any_then(promise &p, it &f); + template void set_all_then(promise &p, it &f); + template void set_any_then(promise &p, it &f); } /// Returns a future which becomes ready when any of the futures in the @@ -79,47 +42,101 @@ ircd::ctx::future ircd::ctx::when_any(it first, const it &last) { - static const auto then - { - [](promise &p, it &f) - { - if(!p.valid()) - return; - - set(state(*f), future_state::OBSERVED); - p.set_value(f); - } - }; - promise p; - const auto set_then - { - [&p](it &f) - { - state(*f).then = [p, f] // alloc - (shared_state_base &sb) mutable - { - if(sb.then) - then(p, f); - }; - } - }; - future ret(p); for(auto f(first); f != last; ++f) if(is(state(*f), future_state::READY)) { - set(state(*f), future_state::OBSERVED); + set(when::state(*f), future_state::OBSERVED); p.set_value(f); return ret; } for(; first != last; ++first) if(is(state(*first), future_state::PENDING)) - set_then(first); + when::set_any_then(p, first); if(refcount(p.state()) <= 1) p.set_value(first); return ret; } + +/// Returns a future which becomes ready when all of the futures in the +/// collection become ready. This future has a void payload to minimize +/// its cost since this indication is positively unate. +template +ircd::ctx::future +ircd::ctx::when_all(it first, + const it &last) +{ + promise p; + future ret(p); + for(; first != last; ++first) + if(is(state(*first), future_state::PENDING)) + when::set_all_then(p, first); + + if(refcount(p.state()) <= 1) + p.set_value(); + + return ret; +} + +template +void +ircd::ctx::when::set_any_then(promise &p, + it &f) +{ + when::state(*f).then = [p, f] // TODO: quash this alloc + (shared_state_base &sb) mutable + { + if(sb.then) + any_then(p, f); + }; +} + +template +void +ircd::ctx::when::set_all_then(promise &p, + it &f) +{ + when::state(*f).then = [p] // TODO: quash this alloc + (shared_state_base &sb) mutable + { + if(sb.then) + all_then(p); + }; +} + +template +void +ircd::ctx::when::any_then(promise &p, + it &f) +{ + if(!p.valid()) + return; + + set(when::state(*f), future_state::OBSERVED); + p.set_value(f); +} + +inline void +ircd::ctx::when::all_then(promise &p) +{ + if(!p.valid()) + return; + + if(refcount(p.state()) < 2) + return p.set_value(); + + return remove(p.state(), p); +} + +/// In order for this template to be reusable with std::set iterations we +/// have to make a const_cast at some point; this internal function does that. +template +auto & +ircd::ctx::when::state(const future &f) +{ + return const_cast &>(f).state(); +}