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>
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
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,
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
76 CREATE_SPARTA_HANDLER(PayloadDeliveringProxy, deliverPayload_);
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_ = pl;
108 }
109
110 // Get a payload for a delayed delivery
111 const DataT & getPayload_() const {
112 return payload_;
113 }
114
115
116 // Set the location in the inflight list of the parent for
117 // quick removal
118 void setInFlightLocation_(const typename ProxyInflightList::iterator & loc) {
119 loc_ = loc;
120 }
121
122 // Called by the SPARTA scheduler. Deliver the payload to
123 // the user's callback. Then, recycle this Proxy
124 void deliverPayload_() {
125 sparta_assert(scheduled_ == true,
126 "Some construct is trying to deliver a payload twice: "
127 << parent_->name_ << " to handler: "
128 << target_consumer_event_handler_.getName());
129 scheduled_ = false;
130 target_consumer_event_handler_((const void*)&payload_);
131 reclaim_();
132 }
133
134 PhasedPayloadEvent<DataT> * parent_ = nullptr;
135 const SpartaHandler target_consumer_event_handler_;
136 DataT payload_;
137 typename ProxyInflightList::iterator loc_;
138 bool scheduled_ = false;
139 bool cancelled_ = false;
140 };
141
142
144 ScheduleableHandle allocateProxy_(const DataT & dat)
145 {
146 PayloadDeliveringProxy * proxy = nullptr;
147 if(SPARTA_EXPECT_TRUE(free_idx_ != 0)) {
148 --free_idx_;
149 proxy = free_pl_[free_idx_];
150 }
151 else {
152 // See if we hit the ceiling
153 if(allocation_idx_ == allocated_proxies_.size()) {
154 addProxies_();
155 }
156
157 // Take one from the allocation list
158 proxy = allocated_proxies_[allocation_idx_].get();
159 ++allocation_idx_;
160
161 sparta_assert(allocation_idx_ < inflight_pl_.max_size(),
162 "The PayloadEvent: '" << getLocation() <<
163 "' has allocated over " << inflight_pl_.max_size() <<
164 " outstanding events -- does that seem right?");
165 }
166 proxy->setInFlightLocation_(inflight_pl_.emplace_back(proxy));
167 proxy->setPayload_(dat);
168
169 return proxy;
170 }
171
174
177 void reclaimProxy_(typename ProxyInflightList::iterator & pl_location) {
178 if (SPARTA_EXPECT_FALSE(pl_location == inflight_pl_.end())) {
179 return;
180 }
182 (*pl_location)->setPayload_(nullptr);
183 }
184 free_pl_[free_idx_++] = *pl_location;
185 inflight_pl_.erase(pl_location);
186 pl_location = inflight_pl_.end();
187 }
188
189 public:
190
191 /*
192 * \brief Create a PhasedPayloadEvent to deliver data at a particular time
193 * \param event_set The sparta::EventSet this PhasedPayloadEvent belongs to
194 * \param name The name of this event (as it shows in the EventSet)
195 * \param sched_phase The SchedulingPhase this PhasedPayloadEvent belongs to
196 * \param consumer_event_handler A SpartaHandler to the consumer's event_handler
197 * \param delay The relative time (in Cycles) from "now" to schedule
198 *
199 * The suggestion is to use the derived class sparta::PayloadEvent
200 * instead of this class directly.
201 */
202 PhasedPayloadEvent(TreeNode * event_set,
203 const std::string & name,
204 SchedulingPhase sched_phase,
205 const SpartaHandler & consumer_event_handler,
206 Clock::Cycle delay = 0) :
207 EventNode(event_set, name, sched_phase),
208 name_(name + "[" + consumer_event_handler.getName() + "]"),
209 prototype_(consumer_event_handler, delay, sched_phase)
210 {
211 sparta_assert(consumer_event_handler.argCount() == 1,
212 "You must assign a PhasedPayloadEvent a consumer handler "
213 "that takes exactly one argument");
214 prototype_.setScheduleableClock(getClock());
216 }
217
220
227
241 ScheduleableHandle preparePayload(const DataT & payload) {
242 return allocateProxy_(payload);
243 }
244
248 {
249 prototype_ >> consumer;
250 return consumer;
251 }
252
254 // Get the underlying Scheduleable prototype
256 return prototype_;
257 }
258
267 void setContinuing(bool continuing) {
268 getScheduleable().setContinuing(continuing);
269 }
270
278 uint32_t getNumOutstandingEvents() const {
279 return inflight_pl_.size();
280 }
281
287 bool isScheduled(Clock::Cycle rel_cycle) const {
288 for(auto * proxy : inflight_pl_) {
289 if(proxy->isScheduled(rel_cycle)) {
290 return true;
291 }
292 }
293 return false;
294 }
295
298 bool isScheduled() const {
299 return getNumOutstandingEvents() > 0;
300 }
301
306 uint32_t cancel()
307 {
308 const uint32_t cancel_cnt = inflight_pl_.size();
309 // Cancelling the event will change the inflight_pl_
310 // list, hence we cannot use a range for loop
311 auto bit = inflight_pl_.begin();
312 while(bit != inflight_pl_.end()) {
313 auto proxy = *bit;
314 ++bit;
315 proxy->cancel();
316 }
317 return cancel_cnt;
318 }
319
327 uint32_t cancel(Clock::Cycle rel_cycle) {
328 const uint32_t cancel_cnt = inflight_pl_.size();
329 auto bit = inflight_pl_.begin();
330 while(bit != inflight_pl_.end()) {
331 auto proxy = *bit;
332 ++bit;
333 // Cancelling the event will change the inflight_pl_
334 // list, hence we cannot use a range for loop
335 proxy->cancel(rel_cycle);
336 }
337 return cancel_cnt;
338 }
339
349 uint32_t cancelIf(const DataT & criteria)
350 {
351 uint32_t cancel_cnt = 0;
352 // Cancelling the event will change the inflight_pl_
353 // list, hence we cannot use a range for loop
354 auto bit = inflight_pl_.begin();
355 while(bit != inflight_pl_.end()) {
356 auto proxy = *bit;
357 ++bit;
358 if(proxy->getPayload_() == criteria) {
359 proxy->cancel();
360 ++cancel_cnt;
361 }
362 }
363
364 return cancel_cnt;
365 }
366
376 std::vector<Scheduleable*> getHandleIf(const DataT & criteria) {
377 std::vector<Scheduleable*> ple_vector;
378 for(auto * proxy : inflight_pl_)
379 {
380 if(proxy->getPayload_() == criteria) {
381 ple_vector.push_back(proxy);
382 }
383 }
384 return ple_vector;
385 }
386
396 bool confirmIf(const DataT & criteria) {
397 for(auto * proxy : inflight_pl_)
398 {
399 if(proxy->getPayload_() == criteria) {
400 return true;
401 }
402 }
403 return false;
404 }
405
442 uint32_t cancelIf(std::function<bool(const DataT &)> compare) {
443 uint32_t cancel_cnt = 0;
444 // Cancelling the event will change the inflight_pl_
445 // list, hence we cannot use a range for loop
446 auto bit = inflight_pl_.begin();
447 while(bit != inflight_pl_.end()) {
448 auto proxy = *bit;
449 ++bit;
450 if(compare(proxy->getPayload_())) {
451 proxy->cancel();
452 ++cancel_cnt;
453 }
454 }
455 return cancel_cnt;
456 }
457
497 std::vector<Scheduleable*> getHandleIf(std::function<bool(const DataT &)> compare) {
498 std::vector<Scheduleable*> ple_vector;
499 for(auto * proxy : inflight_pl_) {
500 if(compare(proxy->getPayload_())) {
501 ple_vector.push_back(proxy);
502 }
503 }
504 return ple_vector;
505 }
506
543 uint32_t confirmIf(std::function<bool(const DataT &)> compare) {
544 for(auto * proxy : inflight_pl_)
545 {
546 if(compare(proxy->getPayload_())) {
547 return true;
548 }
549 }
550 return false;
551 }
552
553 private:
554
555 friend class Scheduler;
556 /*
557 * \brief Create an PayloadEvent to deliver a payload in the future.
558 *
559 * \param event_set The sparta::EventSet this PhasedPayloadEvent belongs to
560 * \param scheduler Pointer to the sparta::Scheduler that would schedule this event
561 * \param name The name of this event (as it shows in the EventSet)
562 * \param sched_phase The SchedulingPhase this PhasedPayloadEvent belongs to
563 * \param consumer_event_handler A SpartaHandler to the consumer's event_handler
564 * \param delay The relative time (in Cycles) from "now" to schedule
565 *
566 * \note This constructor is restricted to be used by the sparta::Scheduler only
567 * in order to support sparta::GlobalEvent
568 */
569 PhasedPayloadEvent(TreeNode * event_set,
570 Scheduler * scheduler,
571 const std::string & name,
572 SchedulingPhase sched_phase,
573 const SpartaHandler & consumer_event_handler,
574 Clock::Cycle delay = 0) :
575 EventNode(event_set, name, sched_phase),
576 name_(name + "[" + consumer_event_handler.getName() + "]"),
577 prototype_(consumer_event_handler, delay, sched_phase)
578 {
579
580 sparta_assert(consumer_event_handler.argCount() == 1,
581 "You must assign a PhasedPayloadEvent a consumer handler ""that takes exactly one argument");
582 prototype_.setScheduler(scheduler);
583 }
584
585 private:
586
588 void createResource_() override {
589 prototype_.setScheduleableClock(getClock());
591
592 // Make sure no proxies are outstanding and all are allocated
593 sparta_assert(inflight_pl_.empty());
594
595 }
596
597 void addProxies_()
598 {
599 const uint32_t old_size = allocated_proxies_.size();
600 const uint32_t new_size = payload_proxy_allocation_cadence_ + old_size;
601 allocated_proxies_.resize(new_size);
602 for(uint32_t i = old_size; i < new_size; ++i) {
603 allocated_proxies_[i].reset(new PayloadDeliveringProxy(prototype_, this));
604 }
605 free_pl_.resize(new_size, nullptr);
606 }
607
609 std::string name_;
610
612 Scheduleable prototype_;
613
614 ProxyAllocation allocated_proxies_;
615 ProxyFreeList free_pl_;
616 ProxyInflightList inflight_pl_{1100};
617
618 // Use 16, a power of 2 for allocation of more objects. No
619 // rhyme or reason, but this seems to be a sweet spot in
620 // performance.
621 const uint32_t payload_proxy_allocation_cadence_ = 16;
622 uint32_t free_idx_ = 0;
623 uint32_t allocation_idx_ = 0;
624
625 };
626}
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...
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.
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
const Clock * getClock() override
Walks up parents (starting with self) until a parent with an associated local clock is found,...
const std::string & getName() const override
Gets the name of this node.
iterator end()
Obtain an end iterator.
Definition FastList.hpp:223
iterator begin()
Obtain a beginning iterator.
Definition FastList.hpp:213
iterator emplace_back(ArgsT &&...args)
emplace and object at the back
Definition FastList.hpp:372
size_t size() const
Definition FastList.hpp:238
NodeIterator< false > iterator
Iterator type.
Definition FastList.hpp:209
iterator erase(const const_iterator &entry)
Erase an element with the given iterator.
Definition FastList.hpp:256
size_t max_size() const
Definition FastList.hpp:241
Macros for handling exponential backoff.
SchedulingPhase
The SchedulingPhases used for events (Tick, Update, PortUpdate, etc)
This templated struct lets us know about whether the datatype is actually an ordinary object or point...