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>
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
51 class PayloadDeliveringProxy : public Scheduleable
52 {
53 public:
54
55 void scheduleRelativeTick(sparta::Scheduler::Tick rel_tick,
56 sparta::Scheduler * scheduler) override
57 {
58 sparta_assert((cancelled_ == false) && (scheduled_ == false),
59 "This Payload handle is already scheduled or was previously cancelled. "
60 "To schedule again, you must create a new one");
61 Scheduleable::scheduleRelativeTick(rel_tick, scheduler);
62 scheduled_ = true;
63 }
64
65
66 PayloadDeliveringProxy(const Scheduleable & prototype,
67 PhasedPayloadEvent<DataT> * parent) :
68 Scheduleable(prototype),
69 parent_(parent),
70 target_consumer_event_handler_(prototype.getHandler()),
71 loc_(parent_->inflight_pl_.end())
72 {
73 // Reset the base class consumer event handler to be
74 // this class' delivery proxy
77 }
78
79 private:
80
81 // Make the parent class a friend
82 friend class PhasedPayloadEvent;
83
84 // If this Scheduleable is managed by a
85 // ScheduleableHandle, then this method is called when the
86 // Handle goes out of scope. The proxy is only reclaimed
87 // if it's not on the Scheduler and there are not handles
88 // pointing to it.
89 void reclaim_() override {
90 if(!scheduled_ && (getScheduleableHandleCount_() == 0)) {
91 parent_->reclaimProxy_(loc_);
92 cancelled_ = false;
93 }
94 }
95
96 // This Scheduleable was cancelled via an indirect cancel
97 // on the Scheduler
98 void eventCancelled_() override {
99 scheduled_ = false;
100 cancelled_ = true;
101 reclaim_();
102 }
103
104 // Set a payload for a delayed delivery
105 void setPayload_(const DataT & pl) {
106 sparta_assert(scheduled_ == false);
107 payload_ = new (&payload_storage_) DataT(pl);
108 }
109
110 // Destroy payload
111 void destroyPayload_() {
112 sparta_assert(scheduled_ == false);
113 std::destroy_at(payload_);
114 }
115
116 // Get a payload for a delayed delivery
117 const DataT & getPayload_() const {
118 return *payload_;
119 }
120
121
122 // Set the location in the inflight list of the parent for
123 // quick removal
124 void setInFlightLocation_(const typename ProxyInflightList::iterator & loc) {
125 loc_ = loc;
126 }
127
128 // Called by the SPARTA scheduler. Deliver the payload to
129 // the user's callback. Then, recycle this Proxy
130 void deliverPayload_() {
131 sparta_assert(scheduled_ == true,
132 "Some construct is trying to deliver a payload twice: "
133 << parent_->name_ << " to handler: "
134 << target_consumer_event_handler_.getName());
135 scheduled_ = false;
136 target_consumer_event_handler_((const void*)payload_);
137 reclaim_();
138 }
139
140 PhasedPayloadEvent<DataT> * parent_ = nullptr;
141 const SpartaHandler target_consumer_event_handler_;
142 DataT * payload_;
143 alignas(DataT) std::byte payload_storage_[sizeof(DataT)];
144 typename ProxyInflightList::iterator loc_;
145 bool scheduled_ = false;
146 bool cancelled_ = false;
147 };
148
149
151 ScheduleableHandle allocateProxy_(const DataT & dat)
152 {
153 PayloadDeliveringProxy * proxy = nullptr;
154 if(SPARTA_EXPECT_TRUE(free_idx_ != 0)) {
155 --free_idx_;
156 proxy = free_pl_[free_idx_];
157 }
158 else {
159 // See if we hit the ceiling
160 if(allocation_idx_ == allocated_proxies_.size()) {
161 addProxies_();
162 }
163
164 // Take one from the allocation list
165 proxy = allocated_proxies_[allocation_idx_].get();
166 ++allocation_idx_;
167
168 sparta_assert(allocation_idx_ < inflight_pl_.max_size(),
169 "The PayloadEvent: '" << getLocation() <<
170 "' has allocated over " << inflight_pl_.max_size() <<
171 " outstanding events -- does that seem right?");
172 }
173 proxy->setInFlightLocation_(inflight_pl_.emplace_back(proxy));
174 proxy->setPayload_(dat);
175
176 return proxy;
177 }
178
180 friend class PayloadDeliveringProxy;
181
184 void reclaimProxy_(typename ProxyInflightList::iterator & pl_location) {
185 if (SPARTA_EXPECT_FALSE(pl_location == inflight_pl_.end())) {
186 return;
187 }
188 (*pl_location)->destroyPayload_();
189 free_pl_[free_idx_++] = *pl_location;
190 inflight_pl_.erase(pl_location);
191 pl_location = inflight_pl_.end();
192 }
193
194 public:
195
196 /*
197 * \brief Create a PhasedPayloadEvent to deliver data at a particular time
198 * \param event_set The sparta::EventSet this PhasedPayloadEvent belongs to
199 * \param name The name of this event (as it shows in the EventSet)
200 * \param sched_phase The SchedulingPhase this PhasedPayloadEvent belongs to
201 * \param consumer_event_handler A SpartaHandler to the consumer's event_handler
202 * \param delay The relative time (in Cycles) from "now" to schedule
203 *
204 * The suggestion is to use the derived class sparta::PayloadEvent
205 * instead of this class directly.
206 */
207 PhasedPayloadEvent(TreeNode * event_set,
208 const std::string & name,
209 SchedulingPhase sched_phase,
210 const SpartaHandler & consumer_event_handler,
211 Clock::Cycle delay = 0) :
212 EventNode(event_set, name, sched_phase),
213 name_(name + "[" + consumer_event_handler.getName() + "]"),
214 prototype_(consumer_event_handler, delay, sched_phase)
215 {
216 sparta_assert(consumer_event_handler.argCount() == 1,
217 "You must assign a PhasedPayloadEvent a consumer handler "
218 "that takes exactly one argument");
219 prototype_.setScheduleableClock(getClock());
221 }
222
225 for(PayloadDeliveringProxy * proxy : inflight_pl_) {
226 std::destroy_at(proxy->payload_);
227 }
228 }
229
236
250 ScheduleableHandle preparePayload(const DataT & payload) {
251 return allocateProxy_(payload);
252 }
253
257 {
258 prototype_ >> consumer;
259 return consumer;
260 }
261
263 // Get the underlying Scheduleable prototype
265 return prototype_;
266 }
267
276 void setContinuing(bool continuing) {
277 getScheduleable().setContinuing(continuing);
278 }
279
287 uint32_t getNumOutstandingEvents() const {
288 return inflight_pl_.size();
289 }
290
296 bool isScheduled(Clock::Cycle rel_cycle) const {
297 for(auto * proxy : inflight_pl_) {
298 if(proxy->isScheduled(rel_cycle)) {
299 return true;
300 }
301 }
302 return false;
303 }
304
307 bool isScheduled() const {
308 return getNumOutstandingEvents() > 0;
309 }
310
315 uint32_t cancel()
316 {
317 const uint32_t cancel_cnt = inflight_pl_.size();
318 // Cancelling the event will change the inflight_pl_
319 // list, hence we cannot use a range for loop
320 auto bit = inflight_pl_.begin();
321 while(bit != inflight_pl_.end()) {
322 auto proxy = *bit;
323 ++bit;
324 proxy->cancel();
325 }
326 return cancel_cnt;
327 }
328
336 uint32_t cancel(Clock::Cycle rel_cycle) {
337 const uint32_t cancel_cnt = inflight_pl_.size();
338 auto bit = inflight_pl_.begin();
339 while(bit != inflight_pl_.end()) {
340 auto proxy = *bit;
341 ++bit;
342 // Cancelling the event will change the inflight_pl_
343 // list, hence we cannot use a range for loop
344 proxy->cancel(rel_cycle);
345 }
346 return cancel_cnt;
347 }
348
358 uint32_t cancelIf(const DataT & criteria)
359 {
360 uint32_t cancel_cnt = 0;
361 // Cancelling the event will change the inflight_pl_
362 // list, hence we cannot use a range for loop
363 auto bit = inflight_pl_.begin();
364 while(bit != inflight_pl_.end()) {
365 auto proxy = *bit;
366 ++bit;
367 if(proxy->getPayload_() == criteria) {
368 proxy->cancel();
369 ++cancel_cnt;
370 }
371 }
372
373 return cancel_cnt;
374 }
375
385 std::vector<Scheduleable*> getHandleIf(const DataT & criteria) {
386 std::vector<Scheduleable*> ple_vector;
387 for(auto * proxy : inflight_pl_)
388 {
389 if(proxy->getPayload_() == criteria) {
390 ple_vector.push_back(proxy);
391 }
392 }
393 return ple_vector;
394 }
395
405 bool confirmIf(const DataT & criteria) {
406 for(auto * proxy : inflight_pl_)
407 {
408 if(proxy->getPayload_() == criteria) {
409 return true;
410 }
411 }
412 return false;
413 }
414
451 uint32_t cancelIf(std::function<bool(const DataT &)> compare) {
452 uint32_t cancel_cnt = 0;
453 // Cancelling the event will change the inflight_pl_
454 // list, hence we cannot use a range for loop
455 auto bit = inflight_pl_.begin();
456 while(bit != inflight_pl_.end()) {
457 auto proxy = *bit;
458 ++bit;
459 if(compare(proxy->getPayload_())) {
460 proxy->cancel();
461 ++cancel_cnt;
462 }
463 }
464 return cancel_cnt;
465 }
466
506 std::vector<Scheduleable*> getHandleIf(std::function<bool(const DataT &)> compare) {
507 std::vector<Scheduleable*> ple_vector;
508 for(auto * proxy : inflight_pl_) {
509 if(compare(proxy->getPayload_())) {
510 ple_vector.push_back(proxy);
511 }
512 }
513 return ple_vector;
514 }
515
552 uint32_t confirmIf(std::function<bool(const DataT &)> compare) {
553 for(auto * proxy : inflight_pl_)
554 {
555 if(compare(proxy->getPayload_())) {
556 return true;
557 }
558 }
559 return false;
560 }
561
562 private:
563
564 friend class Scheduler;
565 /*
566 * \brief Create an PayloadEvent to deliver a payload in the future.
567 *
568 * \param event_set The sparta::EventSet this PhasedPayloadEvent belongs to
569 * \param scheduler Pointer to the sparta::Scheduler that would schedule this event
570 * \param name The name of this event (as it shows in the EventSet)
571 * \param sched_phase The SchedulingPhase this PhasedPayloadEvent belongs to
572 * \param consumer_event_handler A SpartaHandler to the consumer's event_handler
573 * \param delay The relative time (in Cycles) from "now" to schedule
574 *
575 * \note This constructor is restricted to be used by the sparta::Scheduler only
576 * in order to support sparta::GlobalEvent
577 */
578 PhasedPayloadEvent(TreeNode * event_set,
579 Scheduler * scheduler,
580 const std::string & name,
581 SchedulingPhase sched_phase,
582 const SpartaHandler & consumer_event_handler,
583 Clock::Cycle delay = 0) :
584 EventNode(event_set, name, sched_phase),
585 name_(name + "[" + consumer_event_handler.getName() + "]"),
586 prototype_(consumer_event_handler, delay, sched_phase)
587 {
588
589 sparta_assert(consumer_event_handler.argCount() == 1,
590 "You must assign a PhasedPayloadEvent a consumer handler ""that takes exactly one argument");
591 prototype_.setScheduler(scheduler);
592 }
593
594 private:
595
597 void createResource_() override {
598 prototype_.setScheduleableClock(getClock());
600
601 // Make sure no proxies are outstanding and all are allocated
602 sparta_assert(inflight_pl_.empty());
603
604 }
605
606 void addProxies_()
607 {
608 const uint32_t old_size = allocated_proxies_.size();
609 const uint32_t new_size = payload_proxy_allocation_cadence_ + old_size;
610 allocated_proxies_.resize(new_size);
611 for(uint32_t i = old_size; i < new_size; ++i) {
612 allocated_proxies_[i].reset(new PayloadDeliveringProxy(prototype_, this));
613 }
614 free_pl_.resize(new_size, nullptr);
615 }
616
618 std::string name_;
619
621 Scheduleable prototype_;
622
623 ProxyAllocation allocated_proxies_;
624 ProxyFreeList free_pl_;
625 ProxyInflightList inflight_pl_{1100};
626
627 // Use 16, a power of 2 for allocation of more objects. No
628 // rhyme or reason, but this seems to be a sweet spot in
629 // performance.
630 const uint32_t payload_proxy_allocation_cadence_ = 16;
631 uint32_t free_idx_ = 0;
632 uint32_t allocation_idx_ = 0;
633
634 };
635}
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...
Contains a collection implementation of various compile-time metaprogramming and Type-Detection APIs ...
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
EventNode(TreeNode *event_set, const std::string &name, sparta::SchedulingPhase sched_phase)
Create an Event Node.
Definition EventNode.hpp:45
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.
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:205
std::string getLocation() const override final
TreeNode()=delete
Not default-constructable.
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:224
iterator begin()
Obtain a beginning iterator.
Definition FastList.hpp:214
iterator emplace_back(ArgsT &&...args)
emplace an object at the back
Definition FastList.hpp:373
size_t size() const
Definition FastList.hpp:239
iterator erase(const const_iterator &entry)
Erase an element with the given iterator.
Definition FastList.hpp:257
size_t max_size() const
Definition FastList.hpp:242
Macros for handling exponential backoff.
SchedulingPhase
The SchedulingPhases used for events (Tick, Update, PortUpdate, etc)