forked from luteberget/simcpp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
simcpp.cpp
226 lines (168 loc) · 4.5 KB
/
simcpp.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
// Copyright © 2021 Bjørnar Steinnes Luteberget, Felix Schütz.
// Licensed under the MIT license. See the LICENSE file for details.
#include "simcpp.h"
namespace simcpp {
/* Simulation */
SimulationPtr Simulation::create() { return std::make_shared<Simulation>(); }
void Simulation::run_process(const ProcessPtr& process, simtime delay /* = 0.0 */) {
auto event = this->event();
event->add_handler(process);
event->trigger(delay);
}
EventPtr Simulation::timeout(simtime delay) {
auto event = this->event();
event->trigger(delay);
return event;
}
EventPtr Simulation::any_of(std::initializer_list<EventPtr> events) {
int n = 1;
for (const auto &event : events) {
if (event->is_triggered()) {
n = 0;
break;
}
}
auto process = start_process<Condition>(n);
for (auto &event : events) {
event->add_handler(process);
}
return process;
}
EventPtr Simulation::all_of(std::initializer_list<EventPtr> events) {
int n = 0;
for (const auto &event : events) {
if (!event->is_triggered()) {
++n;
}
}
auto process = start_process<Condition>(n);
for (auto &event : events) {
event->add_handler(process);
}
return process;
}
void Simulation::schedule(const EventPtr& event, simtime delay /* = 0.0 */) {
queued_events.emplace(now + delay, next_id, event);
++next_id;
}
bool Simulation::step() {
if (queued_events.empty()) {
return false;
}
auto queued_event = queued_events.top();
queued_events.pop();
now = queued_event.time;
auto event = queued_event.event;
event->process();
return true;
}
void Simulation::advance_by(simtime duration) {
simtime target = now + duration;
while (has_next() && peek_next_time() <= target) {
step();
}
now = target;
}
bool Simulation::advance_to(const EventPtr& event) {
while (event->is_pending() && has_next()) {
step();
}
return event->is_triggered();
}
void Simulation::run() {
while (step());
}
simtime Simulation::get_now() const { return now; }
bool Simulation::has_next() const { return !queued_events.empty(); }
simtime Simulation::peek_next_time() const { return queued_events.top().time; }
/* Simulation::QueuedEvent */
Simulation::QueuedEvent::QueuedEvent(simtime time, size_t id, const EventPtr& event)
: time(time), id(id), event(event) {}
bool Simulation::QueuedEvent::operator<(const QueuedEvent &other) const {
if (time != other.time) {
return time > other.time;
}
return id > other.id;
}
/* Event */
Event::Event(const SimulationPtr& sim) : sim(sim) {}
bool Event::add_handler(const ProcessPtr& process) {
// Handler takes an additional EventPtr arg, but this is ignored by the
// bound function.
return add_handler(std::bind(&Process::resume, process));
}
bool Event::add_handler(const Handler& handler) {
if (is_triggered()) {
return false;
}
if (is_pending()) {
handlers.push_back(handler);
}
return true;
}
bool Event::trigger(simtime delay /* = 0.0 */) {
if (!is_pending()) {
return false;
}
auto sim = this->sim.lock();
sim->schedule(shared_from_this(), delay);
if (delay == 0.0) {
state = State::Triggered;
}
return true;
}
bool Event::abort() {
if (!is_pending()) {
return false;
}
state = State::Aborted;
handlers.clear();
Aborted();
return true;
}
void Event::process() {
if (is_aborted() || is_processed()) {
return;
}
state = State::Processed;
for (const auto& handler : handlers) {
handler(shared_from_this());
}
handlers.clear();
}
bool Event::is_pending() const { return state == State::Pending; }
bool Event::is_triggered() const {
return state == State::Triggered || state == State::Processed;
}
bool Event::is_processed() const { return state == State::Processed; }
bool Event::is_aborted() const { return state == State::Aborted; }
Event::State Event::get_state() const { return state; }
void Event::Aborted() {}
/* Process */
Process::Process(const SimulationPtr& sim) : Event(sim), Protothread() {}
void Process::resume() {
// Is the process already finished?
if (!is_pending()) {
return;
}
bool still_running = Run();
// Did the process finish now?
if (!still_running) {
// Process finished
trigger();
}
}
ProcessPtr Process::shared_from_this() {
return std::static_pointer_cast<Process>(Event::shared_from_this());
}
/* Condition */
Condition::Condition(const SimulationPtr& sim, int n) : Process(sim), n(n) {}
bool Condition::Run() {
PT_BEGIN();
while (n > 0) {
PT_YIELD();
--n;
}
PT_END();
}
} // namespace simcpp