aboutsummaryrefslogtreecommitdiffstats
path: root/native/include/schedule_sim.h
blob: 05e5e67ca7d9ce3b1b1d9220fad9f426d305b3f6 (plain) (blame)
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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
#ifndef SCHED_H
#define SCHED_H

#include "tasks.h"
#include "event.h"
#include <vector>
#include <queue>
#include <algorithm>

typedef unsigned long simtime_t;

class Job {
  private:
    const Task  &task;
    simtime_t release;
    simtime_t cost;
    simtime_t allocation;
    simtime_t seqno;

  public:
    Job(const Task &tsk,
        simtime_t relt = 0,
        unsigned long sequence_no = 1,
        simtime_t cost = 0);

    const Task& get_task() const { return task; }
    simtime_t get_release()  const { return release; }
    simtime_t get_deadline() const { return release + task.get_deadline(); }
    simtime_t get_cost() const { return cost; }
    simtime_t get_allocation() const { return allocation; }
    unsigned long get_seqno() const { return seqno; }

    void increase_allocation(simtime_t service_time)
    {
        allocation += service_time;
    }

    bool is_complete() const
    {
        return allocation >= cost;
    }

    simtime_t remaining_demand() const
    {
        return cost - allocation;
    }

    void init_next(simtime_t cost = 0,
                   simtime_t inter_arrival_time = 0);

    // callbacks
    virtual void completed(simtime_t when, int proc) {};
};

class SimJob;

class ScheduleSimulation
{
  public:
    virtual void simulate_until(simtime_t end_of_simulation) = 0;

    virtual void add_release(SimJob *job) = 0;
    virtual void add_ready(Job *job) = 0;
};

class SimJob : public Job, public Event<simtime_t>
{
  private:
    ScheduleSimulation* sim;

  public:
    SimJob(Task& tsk, ScheduleSimulation* s = NULL) : Job(tsk), sim(s) {};

    void set_simulation(ScheduleSimulation* s) { sim = s; }
    ScheduleSimulation* get_sim() { return sim; }

    void fire(const simtime_t &time)
    {
        sim->add_ready(this);
    }
};

class PeriodicJobSequence : public SimJob
{
  public:
    PeriodicJobSequence(Task& tsk) : SimJob(tsk) {};
    virtual ~PeriodicJobSequence() {};

    // simulator callback
    virtual void completed(simtime_t when, int proc);
};


class EarliestDeadlineFirst {
  public:
    bool operator()(const Job* a, const Job* b)
    {
        if (a && b)
            return a->get_deadline() > b->get_deadline();
        else if (b && !a)
            return true;
        else
            return false;
    }
};

// periodic job sequence

class Processor
{
  private:
    Job*      scheduled;

  public:
    Processor() : scheduled(NULL) {}

    Job* get_scheduled() const { return scheduled; };
    void schedule(Job* new_job) { scheduled = new_job; }

    void idle() { scheduled = NULL; }

    bool advance_time(simtime_t delta)
    {
        if (scheduled)
        {
            scheduled->increase_allocation(delta);
            return scheduled->is_complete();
        }
        else
            return false;
    }
};

template <typename JobPriority>
class PreemptionOrder
{
  public:
    bool operator()(const Processor& a, const Processor& b)
    {
        JobPriority higher_prio;
        return higher_prio(a.get_scheduled(), b.get_scheduled());
    }
};


typedef std::priority_queue<Timeout<simtime_t>,
                            std::vector<Timeout<simtime_t> >,
                            std::greater<Timeout<simtime_t> >
                             > EventQueue;

template <typename JobPriority>
class GlobalScheduler : public ScheduleSimulation
{
    typedef std::priority_queue<Job*,
                                std::vector<Job*>,
                                JobPriority > ReadyQueue;

  private:
    EventQueue events;
    ReadyQueue pending;
    simtime_t  current_time;

    Processor* processors;
    int num_procs;

    JobPriority                   lower_prio;
    PreemptionOrder<JobPriority>  first_to_preempt;

    Event<simtime_t> dummy;

    bool aborted;

  private:

    void advance_time(simtime_t until)
    {
        simtime_t last = current_time;

        current_time = until;

        // 1) advance time until next event (job completion or event)
        for (int i = 0; i < num_procs; i++)
            if (processors[i].advance_time(current_time - last))
            {
                // process job completion
                Job* sched = processors[i].get_scheduled();
                processors[i].idle();
                // notify simulation callback
                job_completed(i, sched);
                // nofity job callback
                sched->completed(current_time, i);
            }

        // 2) process any pending events
        while (!events.empty())
        {
            const Timeout<simtime_t>& next_event = events.top();

            if (next_event.time() <= current_time)
            {
                next_event.event().fire(current_time);
                events.pop();
            }
            else
                // no more expired events
                break;
        }

        // 3) process any required preemptions
        bool all_checked = false;
        while (!pending.empty() && !all_checked)
        {
            Job* highest_prio = pending.top();
            Processor* lowest_prio_proc;

            lowest_prio_proc = std::min_element(processors,
                                                processors + num_procs,
                                                first_to_preempt);
            Job* scheduled = lowest_prio_proc->get_scheduled();

            if (lower_prio(scheduled, highest_prio))
            {
                // do a preemption
                pending.pop();


                // schedule
                lowest_prio_proc->schedule(highest_prio);

                // notify simulation callback
                job_scheduled(lowest_prio_proc - processors,
                              scheduled,
                              highest_prio);
                if (scheduled && !scheduled->is_complete())
                    // add back into the pending queue
                    pending.push(scheduled);

                // schedule job completion event
                Timeout<simtime_t> ev(highest_prio->remaining_demand() +
                                      current_time,
                                      &dummy);
                events.push(ev);
            }
            else
                all_checked = true;
        }
    }

  public:
    GlobalScheduler(int num_procs)
    {
        aborted = false;
        current_time = 0;
        this->num_procs = num_procs;
        processors = new Processor[num_procs];
    }

    virtual ~GlobalScheduler()
    {
        delete [] processors;
    }

    simtime_t get_current_time() { return current_time; }

    void abort() { aborted = true; }

    void simulate_until(simtime_t end_of_simulation)
    {
        while (current_time <= end_of_simulation &&
               !aborted &&
               !events.empty()) {
            simtime_t next = events.top().time();
            advance_time(next);
        }
    }

    // Simulation event callback interface
    virtual void job_released(Job *job) {};
    virtual void job_completed(int proc,
                               Job *job) {};
    virtual void job_scheduled(int proc,
                               Job *preempted,
                               Job *scheduled) {};

    // ScheduleSimulation interface
    void add_release(SimJob *job)
    {
        if (job->get_release() >= current_time)
        {
            // schedule future release
            Timeout<simtime_t> rel(job->get_release(), job);
            events.push(rel);
        }
        else
            add_ready(job);
    }

    // ScheduleSimulation interface
    void add_ready(Job *job)
    {
        // release immediately
        pending.push(job);
        // notify callback
        job_released(job);
    }

};



void run_periodic_simulation(ScheduleSimulation& sim,
                             TaskSet& ts,
                             simtime_t end_of_simulation);


#endif