xo-expression2/include/xo/reactor/LastReducer.hpp

154 lines
5.3 KiB
C++

/* @file LastReducer.hpp */
#pragma once
#include "Reducer.hpp"
#include <array>
namespace xo {
namespace reactor {
/* implementation record used in LastReducer.
* LastReducer (see below) remembers a single event,
* + will be updated on successive calls to
* LastReducer.include_event()
*
* need to remember the _first_ (& therefore earliest)
* event timestamp in such a wave, since that establishes when simulator
* should deliver the event -- even if event is subsequently
* overwritten.
*
* once event is delivered, timestamp can reset
*
* otherwise if upstream producer sends events with
* future timestamps, can get indefinite postponement
* with simulation clock failing to catch up to event time.
*
*/
template<typename Event>
class EventRecd {
public:
using utc_nanos = xo::time::utc_nanos;
public:
EventRecd() = default;
EventRecd(utc_nanos tm, Event ev) : trigger_tm_{tm}, ev_{ev} {}
EventRecd(utc_nanos tm, Event && ev) : trigger_tm_{tm}, ev_{std::move(ev)} {}
public:
/* if sim, deliver event when simulation clock reaches
* .trigger_tm; .trigger_tm can be earlier than .ev time
*/
utc_nanos trigger_tm_;
/* event to deliver */
Event ev_;
};
/* reducer that just remembers the last event
*
* Require:
* - Event is null-contructible
* - Event is copyable
*
* LastReducer provides reentrancy support. This support doesn't operate
* if Event copy is not deep, e.g. for Event = rp<Foo>n
*
* .include_event()
* /-------\ -----------------> /------\
* | empty | | full |
* \-------/ <----------------- \------/
* . .annex_one() .
* . .
* .is_empty()=true .is_empty()=false
*/
template<typename Event, typename EventTimeFn = StructEventTimeFn<Event>>
class LastReducer : public ReducerBase<Event, EventTimeFn> {
public:
using utc_nanos = xo::time::utc_nanos;
public:
LastReducer() = default;
LastReducer(EventTimeFn const & evtfn) : ReducerBase<Event, EventTimeFn>(evtfn) {}
bool is_empty() const { return empty_flag_; }
/* require: .is_empty() = false */
utc_nanos next_tm() const {
return this->last_ev_[this->last_ix_].trigger_tm_;
//return this->event_tm(this->last_ev_[this->last_ix_]);
}
/* #of events stored in this reducer (0 or 1) */
uint32_t n_event() const { return this->empty_flag_ ? 0 : 1; }
Event const & last_annexed_ev() const {
return this->last_ev_[1 - this->last_ix_].ev_;
}
EventRecd<Event> & include_event_aux(Event const & ev) {
EventRecd<Event> & evr
= this->last_ev_[this->last_ix_];
if (this->empty_flag_) {
/* evr.trigger_tm will be preserved across
* successive calls to .include_event();
* until .annex_one()
*/
evr.trigger_tm_ = this->event_tm(ev);
this->empty_flag_ = false;
}
return evr;
} /*include_event_aux*/
void include_event(Event const & ev) {
EventRecd<Event> & evr
= this->include_event_aux(ev);
evr.ev_ = ev;
} /*include_event*/
void include_event(Event && ev) {
EventRecd<Event> & evr
= this->include_event_aux(ev);
evr.ev_ = std::move(ev);
} /*include_event*/
Event & annex_one() {
std::uint32_t annexed_ix = this->last_ix_;
/* since .empty_flag is true,
* next call to .include_event_aux() will
* capture new timestamp
*/
this->empty_flag_ = true;
this->last_ix_ = (1 - this->last_ix_);
return this->last_ev_[annexed_ix].ev_;
} /*annex_one*/
// ----- Inherited from ReducerBase -----
//utc_nanos event_tm(Event const & ev) const { return this->event_tm_fn_(ev); }
private:
/* true when reducer contains 0 queued events,
* not counting any annexed event
*/
bool empty_flag_ = true;
/* .last_ev[.last_ix] updated by .include_event()
*/
std::uint32_t last_ix_ = 0;
/* remember two events
* (a) a single queued event (updated by .include_event())
* (b) a single removed event (reported by .annex_one())
*
* roles of .last_ev[0], .last_ev[1] reverse each time .annex_one() runs
*/
std::array<EventRecd<Event>, 2> last_ev_;
}; /*LastReducer*/
} /*namespace reactor*/
} /*namespace xo*/
/* end LastReducer.hpp */