initial implementation

This commit is contained in:
Roland Conybeare 2023-10-10 15:20:32 -04:00
commit 532d48529f
29 changed files with 2329 additions and 0 deletions

View file

@ -0,0 +1,93 @@
/* @file AbstractEventProcessor.cp */
#include "AbstractEventProcessor.hpp"
#include "xo/indentlog/print/tostr.hpp"
#include <unordered_map>
#include <map>
namespace xo {
using ref::rp;
using ref::brw;
using xo::tostr;
using std::uint32_t;
namespace reactor {
namespace {
/* search all event processors ep reachable (dowstream) from x,
* add to *m;
*/
void
map_network_helper(brw<AbstractEventProcessor> x,
uint32_t * tsort_ix,
std::unordered_map<AbstractEventProcessor*, uint32_t> * m)
{
if (m->contains(x.get()))
return;
auto fn = [tsort_ix, m]
(brw<AbstractEventProcessor> ep)
{
map_network_helper(ep, tsort_ix, m);
};
x->visit_direct_consumers(fn);
/* postorder! */
(*m)[x.get()] = ++(*tsort_ix);
} /*map_network_helper*/
} /*namespace*/
std::vector<rp<AbstractEventProcessor>>
AbstractEventProcessor::map_network(rp<AbstractEventProcessor> const & x)
{
std::unordered_map<AbstractEventProcessor *, std::uint32_t> network_map;
/* index event processors in reverse topological order:
* if B is (directly or indirectly) downstream from A,
* then tsort_ix(B) < tsort_ix(A)
*/
uint32_t tsort_ix = 0;
/* depth-first traversal, detect and short-circuit on dup paths */
map_network_helper(x.borrow(), &tsort_ix, &network_map);
/* invariant: tsort_ix = #of event processors in network */
uint32_t n = tsort_ix;
/* network_map, now in a topologically sorted order */
std::map<uint32_t, AbstractEventProcessor *> tsorted_map;
{
for(auto const & x : network_map) {
uint32_t tsort_ix = x.second;
AbstractEventProcessor * ep = x.first;
tsorted_map[n - tsort_ix] = ep;
}
}
std::vector<rp<AbstractEventProcessor>> retval;
{
for(auto const & x : tsorted_map)
retval.push_back(x.second);
}
return retval;
} /*map_network*/
void
AbstractEventProcessor::display(std::ostream & os) const
{
os << "<AbstractSource>";
} /*display*/
std::string
AbstractEventProcessor::display_string() const
{
return tostr(*this);
} /*display_string*/
} /*namespace reactor*/
} /*namespace xo*/
/* end AbstractEventProcessor.cpp */

View file

@ -0,0 +1,84 @@
/* @file AbstractSource.cpp */
#include "AbstractSource.hpp"
#include "xo/indentlog/scope.hpp"
#include "xo/webutil/StreamEndpointDescr.hpp"
//#include "indentlog/scope.hpp"
namespace xo {
using xo::web::StreamEndpointDescr;
using xo::reactor::AbstractSink;
using xo::ref::rp;
//using xo::scope;
//using xo::tostr;
namespace reactor {
StreamEndpointDescr
AbstractSource::stream_endpoint_descr(std::string const & url_prefix)
{
auto subscribe_fn
= ([this]
(rp<AbstractSink> const & ws_sink)
{
//scope lscope("AbstractSource::stream_endpoint_descr.subscribe_fn");
/* ws_sink created by websocket, sends events to websocket as json
* see [websock/WebsocketSink]
*/
return this->attach_sink(ws_sink);
});
auto unsubscribe_fn
= ([this]
(CallbackId id)
{
this->detach_sink(id);
});
return StreamEndpointDescr(url_prefix,
subscribe_fn,
unsubscribe_fn);
} /*stream_endpoint_descr*/
uint64_t
AbstractSource::deliver_n(uint64_t n)
{
uint64_t retval = 0;
for (uint64_t i=0; i<n; ++i) {
uint64_t n1 = this->deliver_one();
if (n1 == 0) {
/* short-circuit if source has less than n
* events available
*/
break;
}
retval += n1;
}
return retval;
} /*deliver_n*/
uint64_t
AbstractSource::deliver_all()
{
uint64_t retval = 0;
for (;;) {
uint64_t n1 = this->deliver_one();
if (n1 == 0)
break;
retval += n1;
}
return retval;
} /*deliver_all*/
} /*namespace reactor*/
} /*namespace xo*/
/* end AbstractSource.cpp */

View file

@ -0,0 +1,21 @@
# xo-reactor/src/reactor/CMakeLists.txt
set(SELF_LIB reactor)
set(SELF_SRCS
AbstractEventProcessor.cpp AbstractSource.cpp ReactorSource.cpp
Sink.cpp
Reactor.cpp PollingReactor.cpp
init_reactor.cpp)
xo_add_shared_library(${SELF_LIB} ${PROJECT_VERSION} 1 ${SELF_SRCS})
# ----------------------------------------------------------------
# external dependencies
# note: changes to xo_dependency() calls here
# must coordinate with find_dependency() calls
# in xo-reactor/cmake/reactorConfig.cmake.in
#
xo_dependency(${SELF_LIB} reflect)
xo_dependency(${SELF_LIB} webutil)
xo_dependency(${SELF_LIB} callback)

View file

@ -0,0 +1,88 @@
/* @file PollingReactor.cpp */
#include "PollingReactor.hpp"
namespace xo {
using ref::brw;
using std::size_t;
using std::uint64_t;
using std::int64_t;
namespace reactor {
bool
PollingReactor::add_source(brw<ReactorSource> src)
{
/* make sure src does not already appear in .source_v[] */
for(ReactorSourcePtr const & x : this->source_v_) {
if(x.get() == src.get()) {
throw std::runtime_error("PollingReactor::add_source; source already present");
return false;
}
}
src->notify_reactor_add(this);
this->source_v_.push_back(src.get());
return true;
} /*add_source*/
bool
PollingReactor::remove_source(brw<ReactorSource> src)
{
auto ix = std::find(this->source_v_.begin(),
this->source_v_.end(),
src);
if(ix != this->source_v_.end()) {
src->notify_reactor_remove(this);
this->source_v_.erase(ix);
return true;
}
return false;
} /*remove_source*/
int64_t
PollingReactor::find_nonempty_source(size_t start_ix)
{
size_t z = this->source_v_.size();
/* search sources [ix .. z) */
for(size_t ix = start_ix; ix < z; ++ix) {
brw<ReactorSource> src = this->source_v_[ix];
if(src->is_nonempty())
return ix;
}
/* search source [0 .. ix) */
for(size_t ix = 0, n = std::min(start_ix, z); ix < n; ++ix) {
brw<ReactorSource> src = this->source_v_[ix];
if(src->is_nonempty())
return ix;
}
return -1;
} /*find_nonempty_source*/
uint64_t
PollingReactor::run_one()
{
int64_t ix = this->find_nonempty_source(this->next_ix_);
if(ix >= 0) {
brw<ReactorSource> src = this->source_v_[ix];
return src->deliver_one();
} else {
return 0;
}
} /*run_one*/
} /*namespace reactor*/
} /*namespace xo*/
/* end PollingReactor.cpp */

26
src/reactor/Reactor.cpp Normal file
View file

@ -0,0 +1,26 @@
/* file Reactor.cpp
*
* author: Roland Conybeare, Sep 2022
*/
#include "Reactor.hpp"
namespace xo {
namespace reactor {
void
Reactor::run_n(int32_t n)
{
if (n == -1) {
for (;;) {
this->run_one();
}
} else {
for (int32_t i=0; i<n; ++i) {
this->run_one();
}
}
} /*run_n*/
} /*namespace reactor*/
} /*namespace xo*/
/* end Reactor.cpp */

View file

@ -0,0 +1,34 @@
/* @file Source.cpp */
#include "ReactorSource.hpp"
#include "xo/indentlog/print/time.hpp"
#include <cstdint>
namespace xo {
using xo::time::utc_nanos;
namespace reactor {
utc_nanos
ReactorSource::online_current_tm() const
{
/* for an online source:
* .is_exhausted() must always be false;
* this implies that .sim_current_tm() should
* not be called in the first place
*/
assert(false);
return time::timeutil::epoch();
} /*online_current_tm*/
std::uint64_t
ReactorSource::online_advance_until(utc_nanos /*tm*/,
bool /*replay_flag*/)
{
return 0;
} /*online_advance_until*/
} /*namespace reactor*/
} /*namespace xo*/
/* end Source.cpp */

18
src/reactor/Sink.cpp Normal file
View file

@ -0,0 +1,18 @@
/* @file Sink.cpp */
#include "Sink.hpp"
#include "xo/refcnt/Refcounted.hpp"
namespace xo {
namespace reactor {
#ifdef NOT_USING
ref::rp<SinkToConsole<std::pair<xo::time::utc_nanos, double>>>
TemporaryTest::realization_printer()
{
return new SinkToConsole<std::pair<xo::time::utc_nanos, double>>();
} /*realization_printer*/
#endif
} /*namespace reactor*/
} /*namespace xo*/
/* end Sink.cpp */

View file

@ -0,0 +1,31 @@
/* file init_reactor.cpp
*
* author: Roland Conybeare, Aug 2022
*/
#include "init_reactor.hpp"
#include "xo/reflect/init_reflect.hpp"
namespace xo {
void
InitSubsys<S_reactor_tag>::init()
{
/* TODO: reflect reactor types */
} /*init*/
InitEvidence
InitSubsys<S_reactor_tag>::require()
{
InitEvidence retval;
/* subsystem dependencies for reactor/ */
retval ^= InitSubsys<S_reflect_tag>::require();
/* reactor/'s own initialization code */
retval ^= Subsystem::provide<S_reactor_tag>("reactor", &init);
return retval;
} /*require*/
} /*namespace xo*/
/* end init_reactor.cpp */