The Sparta Modeling Framework
Loading...
Searching...
No Matches
PhasedPayloadEvent.hpp
Go to the documentation of this file.
1// <PayloadEvent.h> -*- C++ -*-
2
3
11#pragma once
12
13#include <memory>
17#include "sparta/utils/MetaStructs.hpp"
21
22namespace sparta
23{
24
37 template<class DataT>
38 class PhasedPayloadEvent : public EventNode
39 {
40 private:
41
43 using ProxyAllocation = std::vector<std::unique_ptr<PayloadDeliveringProxy>>;
44 using ProxyFreeList = std::vector<PayloadDeliveringProxy *>;
45 using ProxyInflightList = sparta::utils::FastList <PayloadDeliveringProxy *>;
46
49 static constexpr size_t INITIAL_OUTSTANDING = 16;
50
55 class PayloadDeliveringProxy : public Scheduleable
56 {
57 public:
58
59 void scheduleRelativeTick(sparta::Scheduler::Tick rel_tick,
60 sparta::Scheduler * scheduler) override
61 {
62 sparta_assert((cancelled_ == false) && (scheduled_ == false),
63 "This Payload handle is already scheduled or was previously cancelled. "
64 "To schedule again, you must create a new one");
65 Scheduleable::scheduleRelativeTick(rel_tick, scheduler);
66 scheduled_ = true;
67 }
68
69
70 PayloadDeliveringProxy(const Scheduleable & prototype,
71 PhasedPayloadEvent<DataT> * parent) :
72 Scheduleable(prototype),
73 parent_(parent),
74 target_consumer_event_handler_(prototype.getHandler()),
75 loc_(parent_->inflight_pl_.end())
76 {
77 // Reset the base class consumer event handler to be
78 // this class' delivery proxy
81 }
82
83 private:
84
85 // Make the parent class a friend
86 friend class PhasedPayloadEvent;
87
88 // If this Scheduleable is managed by a
89 // ScheduleableHandle, then this method is called when the
90 // Handle goes out of scope. The proxy is only reclaimed
91 // if it's not on the Scheduler and there are not handles
92 // pointing to it.
93 void reclaim_() override {
94 if(!scheduled_ && (getScheduleableHandleCount_() == 0)) {
95 parent_->reclaimProxy_(loc_);
96 cancelled_ = false;
97 }
98 }
99
100 // This Scheduleable was cancelled via an indirect cancel
101 // on the Scheduler
102 void eventCancelled_() override {
103 scheduled_ = false;
104 cancelled_ = true;
105 reclaim_();
106 }
107
108 // Set a payload for a delayed delivery
109 void setPayload_(const DataT & pl) {
110 sparta_assert(scheduled_ == false);
111 payload_ = new (&payload_storage_) DataT(pl);
112 }
113
114 // Destroy payload
115 void destroyPayload_() {
116 sparta_assert(scheduled_ == false);
117 std::destroy_at(payload_);
118 }
119
120 // Get a payload for a delayed delivery
121 const DataT & getPayload_() const {
122 return *payload_;
123 }
124
125
126 // Set the location in the inflight list of the parent for
127 // quick removal
128 void setInFlightLocation_(const typename ProxyInflightList::iterator & loc) {
129 loc_ = loc;
130 }
131
132 // Called by the SPARTA scheduler. Deliver the payload to
133 // the user's callback. Then, recycle this Proxy
134 void deliverPayload_() {
135 sparta_assert(scheduled_ == true,
136 "Some construct is trying to deliver a payload twice: "
137 << parent_->name_ << " to handler: "
138 << target_consumer_event_handler_.getName());
139 scheduled_ = false;
140 target_consumer_event_handler_((const void*)payload_);
141 reclaim_();
142 }
143
144 PhasedPayloadEvent<DataT> * parent_ = nullptr;
145 const SpartaHandler target_consumer_event_handler_;
146 DataT * payload_;
147 alignas(DataT) std::byte payload_storage_[sizeof(DataT)];
148 typename ProxyInflightList::iterator loc_;
149 bool scheduled_ = false;
150 bool cancelled_ = false;
151 };
152
153
155 ScheduleableHandle allocateProxy_(const DataT & dat)
156 {
157 PayloadDeliveringProxy * proxy = nullptr;
158 if(SPARTA_EXPECT_TRUE(free_idx_ != 0)) {
159 --free_idx_;
160 proxy = free_pl_[free_idx_];
161 }
162 else {
163 // See if we hit the ceiling
164 if(allocation_idx_ == allocated_proxies_.size()) {
165 addProxies_();
166 }
167
168 // Take one from the allocation list
169 proxy = allocated_proxies_[allocation_idx_].get();
170 ++allocation_idx_;
171
172 sparta_assert(allocation_idx_ < inflight_pl_.max_size(),
173 "The PayloadEvent: '" << getLocation() <<
174 "' has allocated over " << inflight_pl_.max_size() <<
175 " outstanding events -- does that seem right?");
176 }
177 proxy->setInFlightLocation_(inflight_pl_.emplace_back(proxy));
178 proxy->setPayload_(dat);
179
180 return proxy;
181 }
182
184 friend class PayloadDeliveringProxy;
185
188 void reclaimProxy_(typename ProxyInflightList::iterator & pl_location) {
189 if (SPARTA_EXPECT_FALSE(pl_location == inflight_pl_.end())) {
190 return;
191 }
192 (*pl_location)->destroyPayload_();
193 free_pl_[free_idx_++] = *pl_location;
194 inflight_pl_.erase(pl_location);
195 pl_location = inflight_pl_.end();
196 }
197
198 public:
199
202 static constexpr size_t DEFAULT_MAX_OUTSTANDING = 16384;
203
204 /*
205 * \brief Create a PhasedPayloadEvent to deliver data at a particular time
206 * \param event_set The sparta::EventSet this PhasedPayloadEvent belongs to
207 * \param name The name of this event (as it shows in the EventSet)
208 * \param sched_phase The SchedulingPhase this PhasedPayloadEvent belongs to
209 * \param consumer_event_handler A SpartaHandler to the consumer's event_handler
210 * \param delay The relative time (in Cycles) from "now" to schedule
211 * \param max_outstanding Ceiling on simultaneously in-flight payloads
212 *
213 * The suggestion is to use the derived class sparta::PayloadEvent
214 * instead of this class directly.
215 */
216 PhasedPayloadEvent(TreeNode * event_set,
217 const std::string & name,
218 SchedulingPhase sched_phase,
219 const SpartaHandler & consumer_event_handler,
220 Clock::Cycle delay = 0,
221 size_t max_outstanding = DEFAULT_MAX_OUTSTANDING) :
222 EventNode(event_set, name, sched_phase),
223 name_(name + "[" + consumer_event_handler.getName() + "]"),
224 prototype_(consumer_event_handler, delay, sched_phase),
225 inflight_pl_(INITIAL_OUTSTANDING, max_outstanding)
226 {
227 sparta_assert(consumer_event_handler.argCount() == 1,
228 "You must assign a PhasedPayloadEvent a consumer handler "
229 "that takes exactly one argument");
230 prototype_.setScheduleableClock(getClock());
232 }
233
236 for(PayloadDeliveringProxy * proxy : inflight_pl_) {
237 std::destroy_at(proxy->payload_);
238 }
239 }
240
247
261 ScheduleableHandle preparePayload(const DataT & payload) {
262 return allocateProxy_(payload);
263 }
264
268 {
269 prototype_ >> consumer;
270 return consumer;
271 }
272
274 // Get the underlying Scheduleable prototype
276 return prototype_;
277 }
278
287 void setContinuing(bool continuing) {
288 getScheduleable().setContinuing(continuing);
289 }
290
298 uint32_t getNumOutstandingEvents() const {
299 return inflight_pl_.size();
300 }
301
307 bool isScheduled(Clock::Cycle rel_cycle) const {
308 for(auto * proxy : inflight_pl_) {
309 if(proxy->isScheduled(rel_cycle)) {
310 return true;
311 }
312 }
313 return false;
314 }
315
318 bool isScheduled() const {
319 return getNumOutstandingEvents() > 0;
320 }
321
326 uint32_t cancel()
327 {
328 const uint32_t cancel_cnt = inflight_pl_.size();
329 // Cancelling the event will change the inflight_pl_
330 // list, hence we cannot use a range for loop
331 auto bit = inflight_pl_.begin();
332 while(bit != inflight_pl_.end()) {
333 auto proxy = *bit;
334 ++bit;
335 proxy->cancel();
336 }
337 return cancel_cnt;
338 }
339
347 uint32_t cancel(Clock::Cycle rel_cycle) {
348 const uint32_t cancel_cnt = inflight_pl_.size();
349 auto bit = inflight_pl_.begin();
350 while(bit != inflight_pl_.end()) {
351 auto proxy = *bit;
352 ++bit;
353 // Cancelling the event will change the inflight_pl_
354 // list, hence we cannot use a range for loop
355 proxy->cancel(rel_cycle);
356 }
357 return cancel_cnt;
358 }
359
369 uint32_t cancelIf(const DataT & criteria)
370 {
371 uint32_t cancel_cnt = 0;
372 // Cancelling the event will change the inflight_pl_
373 // list, hence we cannot use a range for loop
374 auto bit = inflight_pl_.begin();
375 while(bit != inflight_pl_.end()) {
376 auto proxy = *bit;
377 ++bit;
378 if(proxy->getPayload_() == criteria) {
379 proxy->cancel();
380 ++cancel_cnt;
381 }
382 }
383
384 return cancel_cnt;
385 }
386
396 std::vector<Scheduleable*> getHandleIf(const DataT & criteria) {
397 std::vector<Scheduleable*> ple_vector;
398 for(auto * proxy : inflight_pl_)
399 {
400 if(proxy->getPayload_() == criteria) {
401 ple_vector.push_back(proxy);
402 }
403 }
404 return ple_vector;
405 }
406
416 bool confirmIf(const DataT & criteria) {
417 for(auto * proxy : inflight_pl_)
418 {
419 if(proxy->getPayload_() == criteria) {
420 return true;
421 }
422 }
423 return false;
424 }
425
462 uint32_t cancelIf(std::function<bool(const DataT &)> compare) {
463 uint32_t cancel_cnt = 0;
464 // Cancelling the event will change the inflight_pl_
465 // list, hence we cannot use a range for loop
466 auto bit = inflight_pl_.begin();
467 while(bit != inflight_pl_.end()) {
468 auto proxy = *bit;
469 ++bit;
470 if(compare(proxy->getPayload_())) {
471 proxy->cancel();
472 ++cancel_cnt;
473 }
474 }
475 return cancel_cnt;
476 }
477
517 std::vector<Scheduleable*> getHandleIf(std::function<bool(const DataT &)> compare) {
518 std::vector<Scheduleable*> ple_vector;
519 for(auto * proxy : inflight_pl_) {
520 if(compare(proxy->getPayload_())) {
521 ple_vector.push_back(proxy);
522 }
523 }
524 return ple_vector;
525 }
526
563 uint32_t confirmIf(std::function<bool(const DataT &)> compare) {
564 for(auto * proxy : inflight_pl_)
565 {
566 if(compare(proxy->getPayload_())) {
567 return true;
568 }
569 }
570 return false;
571 }
572
573 private:
574
575 friend class Scheduler;
576 /*
577 * \brief Create an PayloadEvent to deliver a payload in the future.
578 *
579 * \param event_set The sparta::EventSet this PhasedPayloadEvent belongs to
580 * \param scheduler Pointer to the sparta::Scheduler that would schedule this event
581 * \param name The name of this event (as it shows in the EventSet)
582 * \param sched_phase The SchedulingPhase this PhasedPayloadEvent belongs to
583 * \param consumer_event_handler A SpartaHandler to the consumer's event_handler
584 * \param delay The relative time (in Cycles) from "now" to schedule
585 * \param max_outstanding Ceiling on simultaneously in-flight payloads
586 *
587 * \note This constructor is restricted to be used by the sparta::Scheduler only
588 * in order to support sparta::GlobalEvent
589 */
590 PhasedPayloadEvent(TreeNode * event_set,
591 Scheduler * scheduler,
592 const std::string & name,
593 SchedulingPhase sched_phase,
594 const SpartaHandler & consumer_event_handler,
595 Clock::Cycle delay = 0,
596 size_t max_outstanding = DEFAULT_MAX_OUTSTANDING) :
597 EventNode(event_set, name, sched_phase),
598 name_(name + "[" + consumer_event_handler.getName() + "]"),
599 prototype_(consumer_event_handler, delay, sched_phase),
600 inflight_pl_(INITIAL_OUTSTANDING, max_outstanding)
601 {
602
603 sparta_assert(consumer_event_handler.argCount() == 1,
604 "You must assign a PhasedPayloadEvent a consumer handler ""that takes exactly one argument");
605 prototype_.setScheduler(scheduler);
606 }
607
608 private:
609
611 void createResource_() override {
612 prototype_.setScheduleableClock(getClock());
614
615 // Make sure no proxies are outstanding and all are allocated
616 sparta_assert(inflight_pl_.empty());
617
618 }
619
620 void addProxies_()
621 {
622 const uint32_t old_size = allocated_proxies_.size();
623 const uint32_t new_size = payload_proxy_allocation_cadence_ + old_size;
624 allocated_proxies_.resize(new_size);
625 for(uint32_t i = old_size; i < new_size; ++i) {
626 allocated_proxies_[i].reset(new PayloadDeliveringProxy(prototype_, this));
627 }
628 free_pl_.resize(new_size, nullptr);
629 }
630
632 std::string name_;
633
635 Scheduleable prototype_;
636
637 ProxyAllocation allocated_proxies_;
638 ProxyFreeList free_pl_;
639 ProxyInflightList inflight_pl_;
640
641 // Use 16, a power of 2 for allocation of more objects. No
642 // rhyme or reason, but this seems to be a sweet spot in
643 // performance.
644 const uint32_t payload_proxy_allocation_cadence_ = 16;
645 uint32_t free_idx_ = 0;
646 uint32_t allocation_idx_ = 0;
647
648 };
649}
File that defines the EventNode class.
File that defines the FastList class – an alternative to std::list when the user knows the size of th...
File that defines the Scheduleable class.
File that defines the phases used in simulation.
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
#define SPARTA_EXPECT_TRUE(x)
A macro for hinting to the compiler a particular condition should be considered most likely true.
#define SPARTA_EXPECT_FALSE(x)
A macro for hinting to the compiler a particular condition should be considered most likely false.
#define CREATE_SPARTA_HANDLER(clname, meth)
File that defines the StartupEvent class.
File that defines a ValidValue.
EventNode is the base class for all event types in SPARTA. Not to be used by the modeler....
Definition EventNode.hpp:36
static Scheduler * determineScheduler(const Clock *clk)
Center point of Scheduler location.
Definition EventNode.hpp:71
Class to schedule a Scheduleable in the future with a payload, but the class itself is not typed on t...
Definition Scheduler.hpp:63
uint32_t getNumOutstandingEvents() const
Return the number of unfired/unscheduled Payloads.
ScheduleableHandle preparePayload(const DataT &payload)
Prepare a Scheduleable Payload for scheduling either now or later.
uint32_t cancel(Clock::Cycle rel_cycle)
Cancel inflight PayloadEvents at the given relative cycle.
bool isScheduled() const
Is this PhasedPayloadEvent have anything scheduled?
PhasedPayloadEvent< DataT > & operator=(const PhasedPayloadEvent< DataT > &)=delete
No assignments, no copies.
void setContinuing(bool continuing)
This event, if continuing == true, will keep the simulation running.
uint32_t cancelIf(const DataT &criteria)
Cancel any scheduled Payload that matches the given criteria.
uint32_t confirmIf(std::function< bool(const DataT &)> compare)
Confirm if any scheduled payload matches the given criteria.
friend class PayloadDeliveringProxy
Allow the proxy to reclaim itself.
std::vector< Scheduleable * > getHandleIf(std::function< bool(const DataT &)> compare)
Return a vector of scheduleable handles that match the given function.
bool isScheduled(Clock::Cycle rel_cycle) const
Determine if this PhasedPayloadEvent is driven on the given cycle.
uint32_t cancel()
Cancel all inflight PayloadEvents.
PhasedPayloadEvent(const PhasedPayloadEvent< DataT > &)=delete
No assignments, no copies.
Scheduleable & operator>>(Scheduleable &consumer)
uint32_t cancelIf(std::function< bool(const DataT &)> compare)
Cancel any scheduled Payload that matches the given function.
static constexpr size_t DEFAULT_MAX_OUTSTANDING
std::vector< Scheduleable * > getHandleIf(const DataT &criteria)
Return a vector of scheduleable handles that match the given criteria.
Scheduleable & getScheduleable() override
Get the scheduleable associated with this event node.
bool confirmIf(const DataT &criteria)
Confirm if any scheduled payload matches the given criteria.
PhasedPayloadEvent(PhasedPayloadEvent< DataT > &&)=delete
No assignments, no copies.
A light-weight reference counting handle for Scheduleables – DOES NOT delete.
A class that defines the basic scheduling interface to the Scheduler. Not intended to be used by mode...
void setContinuing(bool continuing)
This event, if continuing == true, will keep the simulation running.
void setScheduler(Scheduler *sched)
Set the Scheduler of this Scheduleable, and set the local vertex_ to a new vertex from the Vertex Fac...
virtual void scheduleRelativeTick(const Scheduler::Tick rel_tick, Scheduler *const scheduler)
Schedule this event on a relative scheduler tick.
const SpartaHandler & getHandler() const
Get the consumer handler/callback associated with this event.
void setScheduleableClock(const Clock *clk)
Set the clock and scheduler of this Scheduleable.
uint32_t getScheduleableHandleCount_() const
SpartaHandler consumer_event_handler_
The Consumer callback registered with the Event.
Scheduleable(const SpartaHandler &consumer_event_handler, Clock::Cycle delay, SchedulingPhase sched_phase, bool is_unique_event=false)
Construct a Scheduleable object.
A class that lets you schedule events now and in the future.
uint64_t Tick
Typedef for our unit of time.
Node in a composite tree representing a sparta Tree item.
Definition TreeNode.hpp:204
std::string getLocation() const override final
const std::string & getName() const override
Gets the name of this node.
const Clock * getClock() override
Walks up parents (starting with self) until a parent with an associated local clock is found,...
iterator end()
Obtain an end iterator.
Definition FastList.hpp:246
iterator begin()
Obtain a beginning iterator.
Definition FastList.hpp:236
iterator emplace_back(ArgsT &&...args)
emplace an object at the back
Definition FastList.hpp:393
size_t size() const
Definition FastList.hpp:261
iterator erase(const const_iterator &entry)
Erase an element with the given iterator.
Definition FastList.hpp:279
size_t max_size() const
Definition FastList.hpp:264
Macros for handling exponential backoff.
SchedulingPhase
The SchedulingPhases used for events (Tick, Update, PortUpdate, etc)