SIMLIB/C++ 3.09
Loading...
Searching...
No Matches
calendar.cc
Go to the documentation of this file.
1/////////////////////////////////////////////////////////////////////////////
2//! \file calendar.cc Calendar queue implementation
3//
4// Copyright (c) 1991-2008 Petr Peringer
5//
6// This library is licensed under GNU Library GPL. See the file COPYING.
7//
8
9/// Implementation of class CalendarList
10/// <br> interface is static - using global functions in SQS namespace
11///
12/// <br> uses double-linked list and dynamic calendar queue [brown1988]
13//
14// FIXME: Warning: experimental code, needs improvements
15// TODO: improve interface
16// TODO: reference-counting entities?
17
18////////////////////////////////////////////////////////////////////////////
19// interface
20//
21
22#include "simlib.h"
23#include "internal.h"
24#include <cmath>
25#include <cstring>
26
27//#define MEASURE // comment this to switch off
28#ifdef MEASURE
29
30// this is for measuring performance of operations only
31namespace {
32#include "rdtsc.h" // RDTSC instruction on i586+ FIXME
33
34static unsigned long long T_MEASURE;
35
36inline void START_T() {
37 T_MEASURE = rdtsc();
38}
39
40inline double STOP_T() {
41 T_MEASURE = rdtsc() - T_MEASURE;
42 // here can be unit conversion code
43 // iuse CPU clocks for now
44 return (double)T_MEASURE;
45}
46
47enum {
48 OP_RESIZE=1,
49 OP_ESTIMATE=2,
50 OP_SWITCH2CQ=4,
51 OP_SWITCH2LIST=8,
52};
53int OP_MEASURE = 0;
54
55} // local namespace
56#endif
57
58////////////////////////////////////////////////////////////////////////////
59// implementation
60//
61
62namespace simlib3 {
63
65
66/// common interface for all calendar (PES) implementations
67class Calendar { // abstract base class
68 public:
69 bool Empty() const { return _size == 0; }
70 unsigned Size() const { return _size; }
71 /// enqueue
72 virtual void ScheduleAt(Entity *e, double t) = 0;
73 /// dequeue first
74 virtual Entity * GetFirst() = 0;
75 /// dequeue
76 virtual Entity * Get(Entity *e) = 0;
77 /// remove all scheduled entities
78 virtual void clear(bool destroy_entities=false) = 0;
79 virtual const char* Name() =0;
80#ifndef NDEBUG
81 /// for debugging only
82 virtual void debug_print() = 0; // print the calendar contents
83#endif
84 /// time of activation of first item
85 double MinTime() const { return mintime; }
86 protected:
87 /// set cache for faster access
88 void SetMinTime(double t) { mintime=t; }
89
90 ///////////////////////////////////////////////////////////////////////////
91 // data
92 protected:
93 unsigned _size; //!< number of scheduled items
94 private:
95 double mintime; //!< activation time of first event
96 ///////////////////////////////////////////////////////////////////////////
97 // singleton:
98 public:
99 static Calendar * instance(); //!< create/get single instance (singleton)
100 /// check if the instance exists
101 static bool instance_exists() {
102 return _instance != 0;
103 }
104 protected:
106 virtual ~Calendar() {} //!< clear is called in derived class dtr
107 static void delete_instance(); //!< destroy single instance
108 private:
109 static Calendar * _instance; //!< pointer to single instance
110 ///////////////////////////////////////////////////////////////////////////
111 friend void SetCalendar(const char *name); // sets _instance
112};
113
114/////////////////////////////////////////////////////////////////////////////
115/// calendar item - PRIVATE for any implementation
116/// <br> we use double-linked circular list
117struct EventNoticeLinkBase { // base class with list pointers only
118 // used as list head in CalendarList and as part of list items
119 EventNoticeLinkBase * pred; //!< previous object in list
120 EventNoticeLinkBase * succ; //!< next object in list
121
122 EventNoticeLinkBase() : pred(this), succ(this) {}
123
124 /// remove from calendar list
125 void remove() {
126 pred->succ = succ;
127 succ->pred = pred;
128 pred = succ = this; // NOT linked
129 }
130
131 /// insert at position p
133 // todo: move this test (it is not needed always)
134 if(pred != this)
135 remove(); // is in calendar, remove first
136 // --- insert before item s
137 succ = p;
138 pred = p->pred;
139 pred->succ = this;
140 p->pred = this;
141 }
142};
143
144/// activation record
146 /// which entity is scheduled
148 /// activation time of entity
149 double time;
150 /// priority at the time of scheduling
152
153 EventNotice(Entity *p, double t) :
154 //inherited: pred(this), succ(this), // == NOT linked
155 entity(p), // which entity
156 time(t), // activation time
157 priority(p->Priority) // current scheduling priority
158 {
160 }
161
162 /// create link from entity to activation record
164 entity->_evn = this; // reverse link to this activation record
165 }
166 /// delete link from entity to activation record
168 entity->_evn = 0; // unlink reverse link
169 }
170
172 if(pred != this) {
173 remove(); // remove from calendar
175 }
176 }
177
178 /// set new values to existing (unlinked) record
179 void Set(Entity *e, double t) {
180 pred=succ=this;
181 entity = e; // which entity
182 time = t; // activation time
183 priority = e->Priority; // current scheduling priority
185 }
186
187 static EventNotice *Create(Entity *p, double t);
188 static void Destroy(EventNotice *en);
189
190 private:
191 EventNotice(const EventNotice&); // disable
193};
194
195
196// TODO: update interface to be compatible with std:: allocators
197/// allocate activation records fast
198// TODO: use std::list
200 static const unsigned MAXSIZELIMIT = 1000000;
201 EventNoticeLinkBase *l; // single-linked list of freed items
202 unsigned freed;
203 public:
206 clear(); // delete freelist
207 }
208
209 /// free EventNotice, add to freelist for future allocation
210 void free(EventNotice *en) {
211 if(en->pred!=en) { // if scheduled
212 en->remove(); // unlink from calendar list
214 }
215 if(freed>MAXSIZELIMIT) // limit the size of freelist
216 delete en;
217 else {
218 // add to freelist
219 en->succ=l;
220 l=en;
221 freed++;
222 }
223 }
224 /// EventNotice allocation or reuse from freelist
225 EventNotice *alloc(Entity *p, double t) {
226 if(l==0)
227 return new EventNotice(p, t);
228 else {
229 // get from freelist
230 freed--;
231 EventNotice *ptr = static_cast<EventNotice *>(l);
232 l = l->succ;
233 ptr->Set(p,t);
234 return ptr;
235 }
236 }
237 /// clear: delete all free-list items
238 void clear() {
239 while(l!=0) {
240 EventNotice *p = static_cast<EventNotice*>(l);
241 l=l->succ;
242 delete p;
243 }
244 }
245} allocator; // global allocator TODO: improve -> singleton
246
247
248
249////////////////////////////////////////////////////////////////////////////
250/// class CalendarListImplementation --- sorted list
251// +-----------------------------------------------------+
252// | +---+ +---+ +---+ |
253// +-->| |<-- ............ -->| |<---->| |<--+
254// +--+---+---------------------+ +---+ +---+
255// | CalendarListImplementation | |EvN| |EvN|
256// +----------------------------+ +---+ +---+
257// double-linked circular list (should be faster than std::list)
258// items sorted by: 1) time
259// 2) priority
260// 3) FIFO
261//
262// no checking
263//
265 EventNoticeLinkBase l; //!< head of circular list
266 public:
267 class iterator { //!< bidirectional iterator
269 public:
271 // prefix version only
272 iterator &operator++() { p = p->succ; return *this; }
273 iterator &operator--() { p = p->pred; return *this; }
274 // implicit operator= and copy constructor are OK
276 // access to activation record
277 return static_cast<EventNotice *>(p);
278 }
279 bool operator != (iterator q) { return p!=q.p; }
280 bool operator == (iterator q) { return p==q.p; }
281 }; // iterator
282 iterator begin() { return l.succ; }
283 iterator end() { return &l; }
284 bool empty() { return begin() == end(); }
285 ///////////////////////////////////////////////////////////
286
287 double first_time() { return (*begin())->time; }
288 EventNotice *first() { return *begin(); }
289
290private:
291 /// search --- linear search for insert position
293 if(empty())
294 return end();
295 double t = en->time;
296 iterator evn = --end(); // search from back
297 while(evn!=end() && (*evn)->time > t) { // search time
298 --evn;
299 }
300 Entity::Priority_t prio = en->priority;
301 while(evn!=end() && (*evn)->time==t && // equal time...
302 (*evn)->priority < prio) { // ...search priority
303 --evn;
304 }
305 ++evn;
306 return evn; // return insert position or end()
307 }
308
309public:
310 /// enqueue operation
311 void insert(Entity *e, double t) {
313 iterator pos = search(evn);
314 evn->insert(*pos); // insert before pos
315 }
316
317 /// special dequeue operation for rescheduling
319 EventNotice::Destroy(e->GetEventNotice()); // disconnect, remove item
320 return e;
321 }
322
323 /// dequeue operation
325 Entity *e = first()->entity;
326 EventNotice::Destroy(first()); // disconnect, remove item
327 return e;
328 }
329
331 /// remove all items
332 /// @param destroy deallocates entities if true
333 void clear(bool destroy) {
334 while (!empty()) {
335 Entity *e = remove_first();
336 if (destroy && e->isAllocated()) delete e; // delete entity
337 }
338 }
339 ~ CalendarListImplementation() {
340 clear(true);
341 allocator.clear(); // clear freelist
342 }
343#ifndef NDEBUG
344 /// print of calendar contents - FOR DEBUGGING ONLY
345 void debug_print();
346#endif
347
348 /// fast special operations for list swap (Dangerous!):
350 EventNotice *en = first();
351 en->remove(); // disconnect only, no other changes
352 return en;
353 }
354 /// fast special enqueue operation
356 iterator pos = search(evn);
357 evn->insert(*pos); // insert before pos
358 }
359};
360
361////////////////////////////////////////////////////////////////////////////
362/// class CalendarList --- list implementation of calendar
363//
364class CalendarList : public Calendar {
366 public:
367 /// enqueue
368 virtual void ScheduleAt(Entity *p, double t) override;
369
370 /// dequeue
371 virtual Entity *Get(Entity *p) override; // remove process p from calendar
372 /// dequeue first entity
373 virtual Entity *GetFirst() override;
374 /// remove all
375 virtual void clear(bool destroy=false) override; // remove/destroy all items
376
377 /// create calendar instance
378 // TODO: we need this only if calendar use is allowed before Init()
379 static CalendarList * create() { // create instance
380 Dprintf(("CalendarList::create()"));
381 CalendarList *cal = new CalendarList;
382 SIMLIB_atexit(delete_instance); // last SIMLIB module cleanup calls it
383 //else SIMLIB_error(DuplicateCalendar);
384 return cal;
385 }
386 virtual const char* Name() override { return "CalendarList"; }
387
388 private:
390 Dprintf(("CalendarList::CalendarList()"));
391 SetMinTime( SIMLIB_MAXTIME ); // empty
392 }
394 Dprintf(("CalendarList::~CalendarList()"));
395 clear(true);
396 }
397
398public:
399#ifndef NDEBUG
400 virtual void debug_print() override; // print of calendar contents - FOR DEBUGGING ONLY
401#endif
402};
403
404
405////////////////////////////////////////////////////////////////////////////
406// CalendarList implementation
407//
408
409
410
411////////////////////////////////////////////////////////////////////////////
412/// creates new EventNotice,
414{
415 EventNotice *evn = e->GetEventNotice();
416
417 if(evn) { // Entity is scheduled already --- REUSE EventNotice
418 evn->remove(); // remove from calendar list, should stay connected
419 evn->time = t; // change activation time
420 evn->priority = e->Priority; // use current scheduling priority
421// Dprintf(("EventNotice::Create(entity=%p, time=%g, [priority=%d]): %p [RECYCLED]",
422// evn->entity, evn->time, evn->priority, this));
423 }
424 else {
425 evn = allocator.alloc(e,t);
426// Dprintf(("EventNotice::Create(entity=%p, time=%g, [priority=%d]): %p [NEW]",
427// evn->entity, evn->time, evn->priority, this));
428 }
429 return evn;
430}
431
432////////////////////////////////////////////////////////////////////////////
433/// delete EventNotice, if inserted in calendar remove it first
434//
436{
437 allocator.free(en); // disconnect, remove item
438}
439
440////////////////////////////////////////////////////////////////////////////
441/// schedule entity e at time t
443{
444// Dprintf(("CalendarList::ScheduleAt(%s,%g)", e->Name().c_str(), t));
445 if(t<Time)
447 l.insert(e,t);
448 ++_size;
449 // update mintime:
450 if(t < MinTime())
452}
453
454////////////////////////////////////////////////////////////////////////////
455/// delete first entity
457{
458// Dprintf(("CalendarList::GetFirst(): size=%u", Size()));
459
460 if(Empty())
462
463 Entity *e = l.remove_first();
464 --_size;
465
466 if(Empty())
468 else
470 return e;
471}
472
473////////////////////////////////////////////////////////////////////////////
474/// remove entity e from calendar
476{
477// Dprintf(("CalendarList::Get(e=%s): size=%u",e->Name().c_str(), Size()));
478
479 if(Empty())
480 SIMLIB_error(EmptyCalendar); // internal --> TODO:remove
481 if(e->Idle())
483
484 l.remove(e);
485 --_size;
486
487 if(Empty())
489 else
491 return e;
492}
493
494////////////////////////////////////////////////////////////////////////////
495/// remove all event notices, and optionally destroy them
496//
497// FIXME: TODO analyze possible problems with call in destructor
498//
499void CalendarList::clear(bool destroy)
500{
501 Dprintf(("CalendarList::clear(destroy=%s)", destroy?"true":"false"));
502 l.clear(destroy);
503 _size = 0;
505}
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531/////////////////////////////////////////////////////////////////////////////
532// CalendarQueue tunable parameters:
533
534// when to switch from list to calendar queue
535const unsigned LIST_MAX = 512; // TODO:tune parameter: 64-1024
536const unsigned MINBUCKETS = LIST_MAX>4?LIST_MAX:4; // should be power of 2 and >=4
537const unsigned LIST_MIN = LIST_MAX/2; // TODO:tune parameter
538
539// average bucket list length
540const double COEF_PAR = 1.5; // TODO:tune parameter: cca 1.5
541
542// bucket width = MUL_PAR * average delta t
543const double MUL_PAR = 1.0; // TODO:tune parameter: 1.0--5.0
544
545////////////////////////////////////////////////////////////////////////////
546/// CQ implementation of calendar
547class CalendarQueue : public Calendar {
549 BucketList *buckets; // bucket array
550 BucketList list; // list for small number of items
551 unsigned nbuckets; // current number of buckets
552 unsigned hi_bucket_mark; // high bucket threshold for resize
553 unsigned low_bucket_mark; // low bucket threshold for resize
554 unsigned nextbucket; // next bucket to check for first item
555
556 unsigned numop; // number of operations performed from last tuning
557 double bucket_width; // parameter: width of each bucket
558 double buckettop; // top time of current bucket
559 double last_dequeue_time; // for deleta computation
560 double sumdelta; // sum for bucket_width estimation
561 unsigned ndelta; // count
562
563 private:
564 bool list_impl() { return buckets==NULL; }
565
566 /// Convert time to bucket number
567 inline int time2bucket (double t) {
568 return static_cast<int>(fmod(t/bucket_width, static_cast<double>(nbuckets)));
569 }
570
571 /// Compute bucket top limit
572 // the *1.5 is good for bucket number >= 3
573 // XXXX++++++--XXXX X = time can be in bucket 1
574 // 1111222233331111
575 inline double time2bucket_top(double t) {
576 return t + 1.5*bucket_width;
577 }
578
579 double estimate_bucket_width();
580 void switchtocq();
581 void switchtolist();
582
583 public:
584 /// enqueue
585 virtual void ScheduleAt(Entity *p, double t) override;
586
587 /// dequeue
588 virtual Entity *Get(Entity *p) override; // remove process p from calendar
589 /// dequeue first
590 virtual Entity *GetFirst() override;
591 /// remove all
592 virtual void clear(bool destroy=false) override; // remove/destroy all items
593
594 /// create calendar instance
595 // TODO: we need this only if calendar use is allowed before Init()
596 static CalendarQueue * create() { // create instance
597 Dprintf(("CalendarQueue::create()"));
599 SIMLIB_atexit(delete_instance); // last SIMLIB module cleanup calls it
600 //TODO:SIMLIB_error(DuplicateCalendar);
601 return l;
602 }
603
604 virtual const char* Name() override { return "CalendarQueue"; }
605 private:
608
609 void Resize(int grow=0); // grow/shrink operation
610 void SearchMinTime(double starttime); // search for new minimum
611
612public:
613#ifndef NDEBUG
614 virtual void debug_print() override; // print of calendar contents - FOR DEBUGGING ONLY
615 void visualize(const char *msg);
616#endif
617}; // CalendarQueue
618
619
620// TODO: tune this parameter
621#define MAX_OP (Size()/2)
622
623/////////////////////////////////////////////////////////////////////////////
624/// Initialize calendar queue
626 buckets(0), // ptr to bucketarray
627 nbuckets(0), // number of buckets
628
629 hi_bucket_mark(0), // high bucket threshold
630 low_bucket_mark(0), // low bucket threshold
631
632 nextbucket(0), // next bucket to check
633 numop(0), // number of operations from last tuning
634 bucket_width(0.0), // width of each bucket
635 buckettop(0.0), // high time limit of current bucket
636 last_dequeue_time(-1.0),
637 sumdelta(0.0), ndelta(0) // statistics
638{
639 Dprintf(("CalendarQueue::CalendarQueue()"));
640 SetMinTime( SIMLIB_MAXTIME ); // empty
641 // init: Allocate initial buckets
642 // Resize();
643}
644
645/// schedule
647{
648 Dprintf(("CalendarQueue::ScheduleAt(%s,%g)", e->Name().c_str(), t));
649 if(t<Time)
651
652 // if overgrown
653 if(_size>LIST_MAX && list_impl())
654 switchtocq();
655
656 if(list_impl()) {
657 // insert at right position
658 list.insert(e,t);
659 }
660 else {
661 // if too many items, resize bucket array
662 if(_size + 1 > hi_bucket_mark)
663 Resize(+1);
664
665 if(++numop > MAX_OP) // tune each MAX_OP edit operations
666 Resize();
667
668 // select bucket
670 // insert at right position
671 bp.insert(e,t);
672 }
673 ++_size;
674 // update mintime
675 if (MinTime() > t) {
676 SetMinTime(t);
677 }
678}
679
680
681////////////////////////////////////////////////////////////////////////////
682/// dequeue
684{
685// Dprintf(("CalendarQueue::GetFirst()"));
686 if(Empty())
688
689 if(_size<LIST_MIN && !list_impl())
690 switchtolist();
691
692 if(list_impl()) {
693 Entity * e = list.remove_first();
694 // update size
695 --_size;
696 if(Empty())
698 else
700 return e;
701 }
702
703 // else
704
705 // get statistics for tuning
706 double min_time = MinTime();
707 if(last_dequeue_time >= 0.0) {
708 double diff = min_time - last_dequeue_time;
709 if(diff>0.0) {
710 sumdelta += diff;
711 ndelta++;
712 }
713 }
714 last_dequeue_time=min_time;
715
716 // select bucket
717 nextbucket = time2bucket(min_time); // TODO: optimization
719 // get first item
720 Entity * e = bp.remove_first();
721 // update size
722 --_size;
724 Resize(-1);
725 if(++numop > MAX_OP)
726 Resize();
727 // update mintime
729 return e;
730}
731
732////////////////////////////////////////////////////////////////////////////
733/// remove entity e from calendar
734/// <br>called only if rescheduling
736{
737// Dprintf(("CalendarQueue::Get(e=%s)",e->Name().c_str()));
738 if(Empty())
739 SIMLIB_error(EmptyCalendar); // internal use only --> TODO:remove
740 if(e->Idle ())
742
743 if(_size<LIST_MIN && !list_impl())
744 switchtolist();
745
746 if(list_impl()) {
747 list.remove(e);
748 // update size
749 --_size;
750 // update mintime
751 if(Empty())
753 else
755 return e;
756 }
757
758 // else
759
760 // TODO: Get statistics!
761
762 double t = e->ActivationTime();
763 unsigned b = time2bucket(t);
764 BucketList & bp = buckets[b];
765 bp.remove(e);
766 // update size
767 --_size;
768 if (_size < low_bucket_mark) // should be resized
769 Resize(-1);
770 if(++numop > MAX_OP)
771 Resize();
772 // update mintime
773 if(t==MinTime()) // maybe first item removed - update mintime
775 return e;
776}
777
778/////////////////////////////////////////////////////////////////////////////
779/// compute new mintime
780// TODO: compute dequeue price here
781//
782void CalendarQueue::SearchMinTime (double starttime)
783{
784 if(Empty()) {
786 return;
787 }
788
789 // search for minimum from starttime
790 double tmpmin=SIMLIB_MAXTIME;
791// unsigned tmpnextbucket = 0;
792
793 nextbucket = time2bucket(starttime);
794 buckettop = time2bucket_top(starttime);
795
796 // search buckets
797 for (int n__ = nbuckets; n__ > 0; --n__) { // n__ is not used
799 if (!bp.empty()) {
800 // check first item in bucket
801 double t = bp.first_time();
802 if (t < buckettop) { // time is in this "year"
803 // debug only -- TODO: leave out after tests
804 if (t < starttime) SIMLIB_error("CalendarQueue implementation error in SearchMinTime");
805 // first item is OK
806 SetMinTime(t);
807 return;
808 }
809 // search minimum time of all buckets for fallback
810 if (t < tmpmin) {
811 tmpmin = t;
812// tmpnextbucket = nextbucket;
813 }
814 }
815
816 // go to next bucket (modulo nbuckets)
817 // TODO: can be optimized if nbuckets is power of 2
818 ++nextbucket;
819 if (nextbucket == nbuckets)
820 nextbucket = 0;
822 }
823
824 // all buckets checked and none record found in current "year"
825 // we use tmpmin
826 // (happens when the queued times are sparse)
827
828 SetMinTime(tmpmin);
829// nextbucket = tmpnextbucket;
830// buckettop = time2bucket_top (mintime);
831
832}
833
834/////////////////////////////////////////////////////////////////////////////
835/// compute new bucket width --- EXPERIMENTAL
836//
838 Dprintf(("Calendar bucket width estimation:"));
839#ifdef MEASURE
840 OP_MEASURE |= OP_ESTIMATE;
841#endif
842 if(ndelta>10 && sumdelta>0) { // do not use bad statistics
843 double bu_width = MUL_PAR * sumdelta/ndelta;
844 Dprintf((" estm1: %g", bu_width));
845 if(bu_width < 1e-12*MinTime())
846 SIMLIB_error("CalendarQueue:e1 bucketwidth < 1e-12*Time -- total loss of precision");
847 return bu_width;
848 }
849
850// =========================== EXPERIMENTAL CODE ========================
851 // no past statistics - use future events
852 const unsigned LIMIT = nbuckets>1000?1000:nbuckets; // limit # of intervals skipped (cca 1 "year")
853 double last_time = MinTime();
854 unsigned count=0; // number of non-zero intervals
855
856for(int a__ = 0; a__<2; ++a__) {
857 double tmpmin = SIMLIB_MAXTIME;
858 unsigned next_bucket = time2bucket(last_time);
859 double bucket_top = time2bucket_top(last_time);
860 // search all buckets
861 for (int n__ = nbuckets; n__ > 0; --n__) { // n__ is not used
862 BucketList & bp = buckets[next_bucket];
863 // check items in bucket
864 for(BucketList::iterator i=bp.begin(); i != bp.end(); ++i) {
865 double t=(*i)->time;
866 if (t > bucket_top||t < last_time) { // time is NOT in this "year"
867 // search minimum time of next years
868 if (t < tmpmin)
869 tmpmin = t;
870 break;
871 }
872 double diff = t - last_time;
873 if(diff>0.0) {
874 ++count;
875 }
876 last_time=t;
877 if(count>LIMIT) break;
878 }
879 if(count>LIMIT) break;
880 // go to next bucket (modulo nbuckets)
881 // TODO: can be optimized if nbuckets is power of 2
882 ++next_bucket;
883 if (next_bucket == nbuckets)
884 next_bucket = 0;
885 bucket_top += bucket_width;
886 }
887
888 if(count>10) {
889 double avg = (last_time-MinTime())/count;
890 Dprintf((" estm2: avg=%g", avg));
891 double bu_width = MUL_PAR * avg;
892 if(bu_width < 1e-12*MinTime())
893 SIMLIB_error("CalendarQueue:e2 bucketwidth < 1e-12*Time -- total loss of precision");
894 return bu_width;
895 }
896
897 if(tmpmin < SIMLIB_MAXTIME) {
898 Dprintf((" estm3: next tmpmin=%g", tmpmin));
899 last_time = tmpmin;
900 continue; // scan next year
901 } else
902 break;
903} // for ==================================================
904
905 // should (almost) never be reached
906 // we have problem - bad calendar structure
907 // TODO
908 return 1.0;
909}
910
911/////////////////////////////////////////////////////////////////////////////
912/// Resize bucket array
913// - parameter:
914// grow > 0 grow
915// grow == 0 update bucket_width, init
916// grow < 0 shrink
917// - does not change mintime !
918//
919// PROBLEM: TODO: too high-cost for 512-256-128 resize down !
920void CalendarQueue::Resize(int grow) // TODO: is it better to use target size?
921{
922
923 // visualize("before Resize");
924#ifdef MEASURE
925 OP_MEASURE |= OP_RESIZE;
926#endif
927
928 // first tune bucket_width
929 bool bucket_width_changed = false;
930 numop=0; // number of operations from last tuning/checking
931 // test/change bucket_width
932 double new_bucket_width = estimate_bucket_width();
933 // TODO: 1.3/0.7 -- this needs improvement
934 if(new_bucket_width>1.3*bucket_width || new_bucket_width<0.7*bucket_width) {
935 bucket_width = new_bucket_width;
936 bucket_width_changed = true;
937 }
938
939 // save old contents
940 unsigned oldnbuckets = nbuckets;
941 BucketList *oldbuckets = buckets;
942
943 // change nbuckets
944 if (grow>0)
945 nbuckets = oldnbuckets * 2;
946 if (grow<0)
947 nbuckets = oldnbuckets / 2;
948
949 // check low limit
950 if(nbuckets < MINBUCKETS)
951 nbuckets = MINBUCKETS; // minimal number of buckets is 4
952
953// TODO: switch to list if <LOWLIMIT
954// switch to calqueue if >HIGHLIMIT
955
956 Dprintf(("Calendar resize: nbuckets=%d->%d", oldnbuckets, nbuckets));
957 // nbuckets is a power of 2
958
959 if(oldnbuckets == nbuckets && !bucket_width_changed) // no change
960 return;
961
962 // allocate new bucket array
963 buckets = new BucketList[nbuckets]; // initialized by default constructors
964
965 // TODO: search optimal PARAMETERS
966 hi_bucket_mark = static_cast<unsigned>(nbuckets * COEF_PAR); // cca 1.5 TODO: benchmarking
967 low_bucket_mark = (nbuckets / 2) - 2;
968
969 // if initialization only, we can stop here
970 if (oldbuckets == NULL)
971 return;
972
973 // move old contents into new bucket array
974 _size = 0;
975 for (unsigned n = 0; n < oldnbuckets; ++n) {
976 BucketList &oldbp = oldbuckets[n];
977 while(!oldbp.empty()) {
978 // extract EventNotice from old bucket
979 EventNotice * en = oldbp.extract_first(); // no change of e,t,p
980 // select new bucket
982 // insert EventNotice at right position
983 bp.insert_extracted(en);
984 ++_size;
985 }
986 }
987 delete [] oldbuckets; // all are empty
988
989} // Resize
990
991
992/// switch to list implementation
994{
995 // _size, _mintime does not change
996 // assert (buckets != NULL);
997 // assert list.empty()
998#ifdef MEASURE
999 OP_MEASURE |= OP_SWITCH2LIST;
1000#endif
1001
1002 // fill list from CQ
1003 for (unsigned n = 0; n < nbuckets; ++n) {
1004 BucketList &oldbp = buckets[n];
1005 while(!oldbp.empty()) {
1006 // extract EventNotice from old bucket
1007 EventNotice * en = oldbp.extract_first(); // no change of e,t,p
1008 // insert EventNotice at right position
1009 list.insert_extracted(en); // list is short
1010 }
1011 }
1012 delete [] buckets; // all are empty
1013 buckets = NULL;
1014 nbuckets = 0;
1015
1016 // visualize("after switchtolist");
1017} // switch2list
1018
1019/// switch to calendar queue implementation
1021{
1022 // first some initialization:
1023#ifdef MEASURE
1024 OP_MEASURE |= OP_SWITCH2CQ;
1025#endif
1026
1027 // _size does not change
1028 // MinTime unchanged
1029 // assert (buckets == NULL);
1030
1031 // nit CQ statistics:
1032 last_dequeue_time = -1.0;
1033 sumdelta = 0.0;
1034 ndelta = 0;
1035
1036 numop=0; // number of operations from last tuning/checking
1037
1038 // compute bucket_width
1040 double t0=(*i)->time; // mintime
1041 unsigned count = 0;
1042 for(unsigned n=0; i!=list.end() && n<100; ++i, ++n) {
1043 double t1=(*i)->time;
1044 if(t1==t0) continue;
1045 t0 = t1;
1046 ++count;
1047 }
1048 if(count > 5) {
1049 double avg = (t0-MinTime())/count;
1050 bucket_width = MUL_PAR * avg;
1051 } else
1052 bucket_width = 1.0; // TODO: ?
1053
1054 // assert
1055 if(bucket_width < 1e-12*MinTime())
1056 SIMLIB_warning("CalendarQueue:switchtocq bucketwidth<1e-12*Time = loss of precision");
1057
1058 // search for first power of 2 greater than size
1059 for(nbuckets=4; nbuckets<_size; nbuckets<<=1) { /*empty*/ }
1060 // assert: nbuckets is power of 2
1061
1062 // allocate new bucket array
1063 buckets = new BucketList[nbuckets]; // initialized by default constructors
1064
1065 // TODO: search optimal PARAMETERS
1066 hi_bucket_mark = static_cast<unsigned>(nbuckets * COEF_PAR); // cca 1.5 TODO: benchmarking
1067 low_bucket_mark = (nbuckets / 2) - 2;
1068
1069 // move list contents into new bucket array
1070 while(!list.empty()) {
1071 // extract EventNotice from list
1072 EventNotice * en = list.extract_first(); // no change of e,t,p
1073 // select new bucket
1074 BucketList &bp = buckets[time2bucket(en->time)];
1075 // insert EventNotice at right position
1076 bp.insert_extracted(en);
1077 }
1078
1079 // visualize("after switchtocq");
1080} // switch2cq
1081
1082
1083/////////////////////////////////////////////////////////////////////////////
1084/// clear calendar queue
1085//
1086// DIFFERENCE from simple list implementation:
1087// - order of entity destruction is different
1088//
1089void CalendarQueue::clear(bool destroy)
1090{
1091 Dprintf(("CalendarQueue::clear(%s)",destroy?"true":"false"));
1092 // clear non-empty calendar
1093 if(!Empty()) {
1094 if(list_impl())
1095 list.clear(destroy);
1096 else
1097 // empty all buckets
1098 for(unsigned i=0; i<nbuckets; i++)
1099 buckets[i].clear(destroy);
1100 _size = 0;
1101 }
1102 // delete bucketarray
1103 delete [] buckets;
1104 buckets = NULL;
1105 nbuckets = 0;
1106 //
1107 // re-initialize
1108 // statistics:
1109 last_dequeue_time = -1.0;
1110 sumdelta = 0.0;
1111 ndelta = 0;
1112
1113 numop = 0; // for tuning
1114
1115 // list implementation is active now.
1117}
1118
1119/////////////////////////////////////////////////////////////////////////////
1120/// Destroy calendar queue
1122{
1123 Dprintf(("CalendarQueue::~CalendarQueue()"));
1124 clear(true);
1125 allocator.clear(); // clear freelist
1126}
1127
1128
1129/////////////////////////////////////////////////////////////////////////////
1130/////////////////////////////////////////////////////////////////////////////
1131/////////////////////////////////////////////////////////////////////////////
1132
1133
1134#ifndef NDEBUG
1135////////////////////////////////////////////////////////////////////////////
1136void CalendarListImplementation::debug_print() // print of list contents
1137{
1138 int n=0;
1139 for(iterator i = begin(); i!=end(); ++i) {
1140 Print(" [%03u]:", ++n ); // order
1141 Print("\t %s", (*i)->entity->Name().c_str() ); // print entity ID
1142 Print("\t at=%g", (*i)->time ); // schedule time
1143 Print("\n");
1144 }
1145 if(n==0)
1146 Print(" <empty>\n");
1147}
1148////////////////////////////////////////////////////////////////////////////
1149void CalendarList::debug_print() // print of calendar contents
1150{
1151 Print("CalendarList:\n");
1153 l.debug_print();
1154 Print("\n");
1155}
1156////////////////////////////////////////////////////////////////////////////
1157void CalendarQueue::debug_print() // print of calendar queue contents
1158{
1159 Print("CalendarQueue:\n");
1161 for(unsigned i=0; i<nbuckets; i++) {
1162 Print(" bucket#%03u:\n", i);
1163 buckets[i].debug_print();
1164 Print("\n");
1165 }
1166 Print("\n");
1167}
1168////////////////////////////////////////////////////////////////////////////
1169/// CalendarQueue::visualize -- output suitable for Gnuplot
1170void CalendarQueue::visualize(const char *msg)
1171{
1172 Print("# CalendarQueue::visualize %s\n",msg);
1173 if(list_impl())
1174 Print("# size=%u, mintime=%g (list)\n", Size(), MinTime());
1175 else
1176 Print("# size=%u, nbuckets=%d, mintime=%g, operations=%u, bucket_width=%g\n",
1178 if(Empty()) return;
1179 for(unsigned b=0; b<nbuckets; b++) {
1180 BucketList &bl = buckets[b];
1181 unsigned cnt=0;
1182 Print("%d:", b );
1183 for(BucketList::iterator i = bl.begin(); i!=bl.end(); ++i) {
1184 //Print("%g %d\n", (*i)->time, b ); // schedule time
1185 Print(" %g", (*i)->time ); // schedule time
1186 cnt++;
1187 }
1188 Print("\n");
1189 //if(cnt>0) Print("#[%u].len = %u\n", b, cnt );
1190 }
1191 Print("\n");
1192}
1193////////////////////////////////////////////////////////////////////////////
1194#endif
1195
1196
1197
1198////////////////////////////////////////////////////////////////////////////
1199
1200/// static pointer to singleton instance
1202
1203/// interface to singleton instance
1205 if(_instance==0) {
1206#if 1 // choose default
1207 _instance = CalendarList::create(); // create default calendar
1208#else
1209 _instance = CalendarQueue::create(); // create default calendar
1210#endif
1211 }
1212 return _instance;
1213}
1214
1215/// destroy single instance
1217 Dprintf(("Calendar::delete_instance()"));
1218 if(_instance) {
1219 delete _instance; // remove all, free
1220 _instance = 0;
1221 }
1222}
1223
1224
1225#if 0
1226class Calendars {
1227 typedef Calendar * (*create_fun_ptr_t)();
1228 std::map<string, create_fun_ptr_t> record;
1229 Calendars() {}
1230 Register(create_fun_ptr_t f, const char *name) {
1231 string n(name);
1232 for(string::iterator i=n.begin(); i!=n.end(); ++i)
1233 *i = std::tolower(*i);
1234 record[n] = f;
1235 }
1236 Calendar *create(const char *name) {
1237 string n(name);
1238 for(string::iterator i=n.begin(); i!=n.end(); ++i)
1239 *i = std::tolower(*i);
1240 if(record.count(n)==0) // not present
1241 SIMLIB_error("Bad calendar type name");
1242 create_fun_ptr_t f = record[n];
1243 return f();
1244 }
1245};
1246#endif
1247
1248/// choose calendar implementation
1249/// default is list
1250void SetCalendar(const char *name) {
1252 SIMLIB_Phase == SIMULATION ) SIMLIB_error("SetCalendar() can't be used after Init()");
1253
1254 if(Calendar::_instance) // already initialized
1256 if(name==0 || std::strcmp(name,"")==0 || std::strcmp(name,"default")==0)
1258 else if(std::strcmp(name,"list")==0)
1260 else if(std::strcmp(name,"cq")==0)
1262 else
1263 SIMLIB_error("SetCalendar: bad argument");
1264}
1265
1266
1267////////////////////////////////////////////////////////////////////////////
1268// public INTERFACE = exported functions...
1269//
1270
1271#ifdef MEASURE
1272// global measurement results (in simlib3 namespace)
1273unsigned cal_cost_size;
1274double cal_cost_time;
1275int cal_cost_flag;
1276const char * cal_cost_op;
1277#endif
1278
1279/// empty calendar predicate
1280bool SQS::Empty() { // used by Run() only
1281 return Calendar::instance()->Empty();
1282}
1283
1284/// schedule entity e at given time t using scheduling priority from e
1285/// @param e entity
1286/// @param t time of activation
1287void SQS::ScheduleAt(Entity *e, double t) { // used by scheduling operations
1288 if(!e->Idle())
1289 SIMLIB_error("ScheduleAt call if already scheduled");
1290#ifdef MEASURE
1291 START_T();
1292#endif
1294#ifdef MEASURE
1295 double ttt=STOP_T();
1296// Print("enqueue %d %g %d\n", Calendar::instance()->size(), ttt, OP_MEASURE);
1297cal_cost_size = Calendar::instance()->Size();
1298cal_cost_time = ttt;
1299cal_cost_flag = OP_MEASURE;
1300cal_cost_op = "enqueue";
1301OP_MEASURE=0;
1302// if(Calendar::instance()->size() < 300) Calendar::instance()->visualize("");
1303#endif
1304 _SetTime(NextTime, Calendar::instance()->MinTime());
1305}
1306
1307/// remove selected entity activation record from calendar
1308void SQS::Get(Entity *e) { // used by Run() only
1309#ifdef MEASURE
1310 START_T();
1311#endif
1312 Calendar::instance()->Get(e);
1313#ifdef MEASURE
1314 double ttt=STOP_T();
1315// Print("dequeue2 %d %g %d\n", Calendar::instance()->size(), ttt, OP_MEASURE);
1316cal_cost_size = Calendar::instance()->Size();
1317cal_cost_time = ttt;
1318cal_cost_flag = OP_MEASURE;
1319cal_cost_op = "delete";
1320OP_MEASURE=0;
1321#endif
1322 _SetTime(NextTime, Calendar::instance()->MinTime());
1323}
1324
1325/// remove entity with minimum activation time
1326/// @returns pointer to entity
1327Entity *SQS::GetFirst() { // used by Run()
1328#ifdef MEASURE
1329 START_T();
1330#endif
1331 Entity * ret = Calendar::instance()->GetFirst();
1332#ifdef MEASURE
1333 double ttt=STOP_T();
1334// Print("dequeue %d %g %d\n", Calendar::instance()->size(), ttt, OP_MEASURE);
1335cal_cost_size = Calendar::instance()->Size();
1336cal_cost_time = ttt;
1337cal_cost_flag = OP_MEASURE;
1338cal_cost_op = "dequeue";
1339OP_MEASURE=0;
1340#endif
1341 _SetTime(NextTime, Calendar::instance()->MinTime());
1342 return ret;
1343}
1344
1345/// remove all scheduled entities
1346void SQS::Clear() { // remove all
1347 Calendar::instance()->clear(true);
1348 _SetTime(NextTime, Calendar::instance()->MinTime());
1349}
1350
1351int SQS::debug_print() { // for debugging only
1353 return Calendar::instance()->Size();
1354}
1355
1356/// get activation time of entity - iff scheduled <br>
1357/// it is here, because Entity has no knowledge of calendar activation record structure
1358double Entity::ActivationTime() { // activation time
1359 //if(Idle()) SIMLIB_internal_error(); // passive entity
1360 if(Idle()) return SIMLIB_MAXTIME; // passive entity
1361 return GetEventNotice()->time;
1362}
1363
1364
1365} // end of namespace
1366
Test t(F)
#define MAX_OP
Definition: calendar.cc:621
EventNoticeLinkBase * p
< bidirectional iterator
Definition: calendar.cc:268
class CalendarListImplementation — sorted list
Definition: calendar.cc:264
Entity * remove(Entity *e)
special dequeue operation for rescheduling
Definition: calendar.cc:318
EventNotice * extract_first()
fast special operations for list swap (Dangerous!):
Definition: calendar.cc:349
EventNoticeLinkBase l
head of circular list
Definition: calendar.cc:265
void debug_print()
print of calendar contents - FOR DEBUGGING ONLY
Definition: calendar.cc:1136
void insert(Entity *e, double t)
enqueue operation
Definition: calendar.cc:311
Entity * remove_first()
dequeue operation
Definition: calendar.cc:324
void insert_extracted(EventNotice *evn)
fast special enqueue operation
Definition: calendar.cc:355
iterator search(EventNotice *en)
search — linear search for insert position
Definition: calendar.cc:292
void clear(bool destroy)
remove all items
Definition: calendar.cc:333
class CalendarList — list implementation of calendar
Definition: calendar.cc:364
virtual const char * Name() override
Definition: calendar.cc:386
CalendarListImplementation l
Definition: calendar.cc:365
virtual Entity * GetFirst() override
dequeue first entity
Definition: calendar.cc:456
virtual void clear(bool destroy=false) override
remove all
Definition: calendar.cc:499
virtual void debug_print() override
for debugging only
Definition: calendar.cc:1149
virtual void ScheduleAt(Entity *p, double t) override
enqueue
Definition: calendar.cc:442
virtual Entity * Get(Entity *p) override
dequeue
Definition: calendar.cc:475
static CalendarList * create()
create calendar instance
Definition: calendar.cc:379
CQ implementation of calendar.
Definition: calendar.cc:547
virtual void debug_print() override
for debugging only
Definition: calendar.cc:1157
CalendarQueue()
Initialize calendar queue.
Definition: calendar.cc:625
virtual void ScheduleAt(Entity *p, double t) override
enqueue
Definition: calendar.cc:646
~CalendarQueue()
Destroy calendar queue.
Definition: calendar.cc:1121
void SearchMinTime(double starttime)
compute new mintime
Definition: calendar.cc:782
double time2bucket_top(double t)
Compute bucket top limit.
Definition: calendar.cc:575
virtual const char * Name() override
Definition: calendar.cc:604
CalendarListImplementation BucketList
Definition: calendar.cc:548
void switchtocq()
switch to calendar queue implementation
Definition: calendar.cc:1020
void visualize(const char *msg)
CalendarQueue::visualize – output suitable for Gnuplot.
Definition: calendar.cc:1170
unsigned low_bucket_mark
Definition: calendar.cc:553
BucketList * buckets
Definition: calendar.cc:549
void Resize(int grow=0)
Resize bucket array.
Definition: calendar.cc:920
double estimate_bucket_width()
compute new bucket width — EXPERIMENTAL
Definition: calendar.cc:837
int time2bucket(double t)
Convert time to bucket number.
Definition: calendar.cc:567
static CalendarQueue * create()
create calendar instance
Definition: calendar.cc:596
virtual void clear(bool destroy=false) override
remove all
Definition: calendar.cc:1089
virtual Entity * Get(Entity *p) override
dequeue
Definition: calendar.cc:735
virtual Entity * GetFirst() override
dequeue first
Definition: calendar.cc:683
void switchtolist()
switch to list implementation
Definition: calendar.cc:993
common interface for all calendar (PES) implementations
Definition: calendar.cc:67
static bool instance_exists()
check if the instance exists
Definition: calendar.cc:101
virtual void debug_print()=0
for debugging only
static Calendar * instance()
create/get single instance (singleton)
Definition: calendar.cc:1204
virtual const char * Name()=0
static void delete_instance()
destroy single instance
Definition: calendar.cc:1216
virtual Entity * GetFirst()=0
dequeue first
unsigned Size() const
Definition: calendar.cc:70
virtual void ScheduleAt(Entity *e, double t)=0
enqueue
static Calendar * _instance
pointer to single instance
Definition: calendar.cc:109
bool Empty() const
Definition: calendar.cc:69
unsigned _size
number of scheduled items
Definition: calendar.cc:93
double mintime
activation time of first event
Definition: calendar.cc:95
virtual Entity * Get(Entity *e)=0
dequeue
double MinTime() const
time of activation of first item
Definition: calendar.cc:85
void SetMinTime(double t)
set cache for faster access
Definition: calendar.cc:88
virtual void clear(bool destroy_entities=false)=0
remove all scheduled entities
friend void SetCalendar(const char *name)
choose calendar implementation default is list
Definition: calendar.cc:1250
virtual ~Calendar()
clear is called in derived class dtr
Definition: calendar.cc:106
abstract base class for active entities (Process, Event) instances of derived classes provide Behavio...
Definition: simlib.h:375
EventNotice * GetEventNotice()
Definition: simlib.h:427
EntityPriority_t Priority_t
Definition: simlib.h:397
bool Idle()
entity activation is not scheduled in calendar
Definition: simlib.h:412
EventNotice * _evn
points to calendar item, iff scheduled
Definition: simlib.h:425
Priority_t Priority
priority of the entity (scheduling,queues)
Definition: simlib.h:399
double ActivationTime()
get activation time of entity - iff scheduled it is here, because Entity has no knowledge of calend...
Definition: calendar.cc:1358
allocate activation records fast
Definition: calendar.cc:199
static const unsigned MAXSIZELIMIT
Definition: calendar.cc:200
EventNotice * alloc(Entity *p, double t)
EventNotice allocation or reuse from freelist.
Definition: calendar.cc:225
void clear()
clear: delete all free-list items
Definition: calendar.cc:238
EventNoticeLinkBase * l
Definition: calendar.cc:201
void free(EventNotice *en)
free EventNotice, add to freelist for future allocation
Definition: calendar.cc:210
bool isAllocated() const
Definition: simlib.h:318
virtual std::string Name() const
get object name
Definition: object.cc:134
@ EntityIsNotScheduled
Definition: errors.h:39
@ EmptyCalendar
Definition: errors.h:30
@ SchedulingBeforeTime
Definition: errors.h:29
Internal header file for SIMLIB/C++.
#define Dprintf(f)
Definition: internal.h:100
#define _SetTime(t, x)
macro for simple assignement to internal time variables
Definition: internal.h:228
int debug_print()
Definition: calendar.cc:1351
void Get(Entity *e)
remove selected entity activation record from calendar
Definition: calendar.cc:1308
Entity * GetFirst()
remove entity with minimum activation time
Definition: calendar.cc:1327
void ScheduleAt(Entity *e, double t)
schedule entity e at given time t using scheduling priority from e
Definition: calendar.cc:1287
void Clear()
remove all scheduled entities
Definition: calendar.cc:1346
bool Empty()
empty calendar predicate
Definition: calendar.cc:1280
Implementation of class CalendarList interface is static - using global functions in SQS namespace.
Definition: algloop.cc:32
const double & Time
model time (is NOT the block)
Definition: run.cc:48
const double & NextTime
next-event time
Definition: run.cc:49
const unsigned LIST_MIN
Definition: calendar.cc:537
const unsigned LIST_MAX
Definition: calendar.cc:535
const double COEF_PAR
Definition: calendar.cc:540
void SIMLIB_error(const enum _ErrEnum N)
print error message and abort program
Definition: error.cc:38
class simlib3::EventNoticeAllocator allocator
@ INITIALIZATION
Definition: internal.h:78
@ SIMULATION
Definition: internal.h:79
SIMLIB_IMPLEMENTATION
Definition: algloop.cc:34
void SetCalendar(const char *name)
choose calendar implementation default is list
Definition: calendar.cc:1250
void SIMLIB_atexit(SIMLIB_atexit_function_t p)
Definition: atexit.cc:26
const unsigned MINBUCKETS
Definition: calendar.cc:536
void SIMLIB_warning(const enum _ErrEnum N)
print warning message and continue
Definition: error.cc:74
int Print(const char *fmt,...)
for Output methods, can be redirected
Definition: print.cc:92
SIMLIB_Phase_t SIMLIB_Phase
Definition: run.cc:57
const double MUL_PAR
Definition: calendar.cc:543
const double SIMLIB_MAXTIME
maximum time (1e30 works for float, too)
Definition: simlib.h:148
static __inline__ unsigned long long rdtsc(void)
Definition: rdtsc.h:13
Main SIMLIB/C++ interface.
calendar item - PRIVATE for any implementation we use double-linked circular list
Definition: calendar.cc:117
void insert(EventNoticeLinkBase *p)
insert at position p
Definition: calendar.cc:132
EventNoticeLinkBase * pred
previous object in list
Definition: calendar.cc:119
void remove()
remove from calendar list
Definition: calendar.cc:125
EventNoticeLinkBase * succ
next object in list
Definition: calendar.cc:120
activation record
Definition: calendar.cc:145
static void Destroy(EventNotice *en)
delete EventNotice, if inserted in calendar remove it first
Definition: calendar.cc:435
Entity::Priority_t priority
priority at the time of scheduling
Definition: calendar.cc:151
void delete_reverse_link()
delete link from entity to activation record
Definition: calendar.cc:167
double time
activation time of entity
Definition: calendar.cc:149
void create_reverse_link()
create link from entity to activation record
Definition: calendar.cc:163
EventNotice & operator=(const EventNotice &)
void Set(Entity *e, double t)
set new values to existing (unlinked) record
Definition: calendar.cc:179
Entity * entity
which entity is scheduled
Definition: calendar.cc:147
EventNotice(const EventNotice &)
EventNotice(Entity *p, double t)
Definition: calendar.cc:153
static EventNotice * Create(Entity *p, double t)
creates new EventNotice,
Definition: calendar.cc:413