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
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
|
/**
* \file
* \brief Scheduler class header
*
* \author Copyright (C) 2014-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
*
* \par License
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
* distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_SCHEDULER_HPP_
#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_SCHEDULER_HPP_
#include "distortos/internal/scheduler/ThreadControlBlock.hpp"
#include "distortos/internal/scheduler/ThreadList.hpp"
#include "distortos/internal/scheduler/SoftwareTimerSupervisor.hpp"
namespace distortos
{
namespace internal
{
class MainThread;
/// Scheduler class is a system's scheduler
class Scheduler
{
public:
/**
* \brief Scheduler's constructor
*/
constexpr Scheduler() :
currentThreadControlBlock_{},
runnableList_{},
suspendedList_{},
softwareTimerSupervisor_{},
contextSwitchCount_{},
tickCount_{}
{
}
/**
* \brief Adds new ThreadControlBlock to scheduler.
*
* ThreadControlBlock's state is changed to "runnable".
*
* \param [in] threadControlBlock is a reference to added ThreadControlBlock object
*
* \return 0 on success, error code otherwise:
* - EINVAL - thread is already started;
* - error codes returned by Scheduler::addInternal();
*/
int add(ThreadControlBlock& threadControlBlock);
/**
* \brief Blocks current thread, transferring it to provided container.
*
* \param [in] container is a reference to destination container to which the thread will be transferred
* \param [in] state is the new state of thread that will be blocked
* \param [in] unblockFunctor is a pointer to ThreadControlBlock::UnblockFunctor which will be executed in
* ThreadControlBlock::unblockHook(), default - nullptr (no functor will be executed)
*
* \return 0 on success, error code otherwise:
* - EINTR - thread was unblocked with ThreadControlBlock::UnblockReason::signal;
* - ETIMEDOUT - thread was unblocked with ThreadControlBlock::UnblockReason::timeout;
*/
int block(ThreadList& container, ThreadState state, const ThreadControlBlock::UnblockFunctor* unblockFunctor = {});
/**
* \brief Blocks thread, transferring it to provided container.
*
* The thread must be on "runnable" list - trying to block thread in other state is an error.
*
* \param [in] container is a reference to destination container to which the thread will be transferred
* \param [in] iterator is the iterator to the thread that will be blocked
* \param [in] state is the new state of thread that will be blocked
* \param [in] unblockFunctor is a pointer to ThreadControlBlock::UnblockFunctor which will be executed in
* ThreadControlBlock::unblockHook(), default - nullptr (no functor will be executed)
*
* \return 0 on success, error code otherwise:
* - EINTR - thread was unblocked with ThreadControlBlock::UnblockReason::signal (possible only when blocking
* current thread);
* - EINVAL - provided thread is not on "runnable" list;
* - ETIMEDOUT - thread was unblocked with ThreadControlBlock::UnblockReason::timeout (possible only when blocking
* current thread);
*/
int block(ThreadList& container, ThreadList::iterator iterator, ThreadState state,
const ThreadControlBlock::UnblockFunctor* unblockFunctor = {});
/**
* \brief Blocks current thread with timeout, transferring it to provided container.
*
* \param [in] container is a reference to destination container to which the thread will be transferred
* \param [in] state is the new state of thread that will be blocked
* \param [in] timePoint is the time point at which the thread will be unblocked (if not already unblocked)
* \param [in] unblockFunctor is a pointer to ThreadControlBlock::UnblockFunctor which will be executed in
* ThreadControlBlock::unblockHook(), default - nullptr (no functor will be executed)
*
* \return 0 on success, error code otherwise:
* - EINTR - thread was unblocked with ThreadControlBlock::UnblockReason::signal;
* - ETIMEDOUT - thread was unblocked because timePoint was reached;
*/
int blockUntil(ThreadList& container, ThreadState state, TickClock::time_point timePoint,
const ThreadControlBlock::UnblockFunctor* unblockFunctor = {});
/**
* \return number of context switches
*/
uint64_t getContextSwitchCount() const;
/**
* \return reference to currently active ThreadControlBlock
*/
ThreadControlBlock& getCurrentThreadControlBlock() const
{
return *currentThreadControlBlock_;
}
/**
* \return reference to internal SoftwareTimerSupervisor object
*/
SoftwareTimerSupervisor& getSoftwareTimerSupervisor()
{
return softwareTimerSupervisor_;
}
/**
* \return const reference to internal SoftwareTimerSupervisor object
*/
const SoftwareTimerSupervisor& getSoftwareTimerSupervisor() const
{
return softwareTimerSupervisor_;
}
/**
* \return current value of tick count
*/
uint64_t getTickCount() const;
/**
* \brief Scheduler's initialization
*
* \attention This must be called after constructor, before enabling any scheduling. Priority of main thread must
* be higher than priority of idle thread
*
* \param [in] mainThread is a reference to main thread
*
* \return 0 on success, error code otherwise:
* - error codes returned by Scheduler::addInternal();
*/
int initialize(MainThread& mainThread);
/**
* \brief Requests context switch if it is needed.
*
* \attention This function must be called with interrupt masking enabled.
*/
void maybeRequestContextSwitch() const;
/**
* \brief Removes current thread from Scheduler's control.
*
* Thread's state is changed to "terminated".
*
* \note This function must be called with masked interrupts.
*
* \note This function can be used only after thread's function returns an all cleanup is done.
*
* \return 0 on success, error code otherwise:
* - EINVAL - provided thread is not on "runnable" list and cannot be removed/terminated;
*/
int remove();
/**
* \brief Resumes suspended thread.
*
* The thread must be on the "suspended" list - trying to resume thread that is not suspended is an error.
*
* \param [in] iterator is the iterator to the thread that will be resumed
*
* \return 0 on success, error code otherwise:
* - EINVAL - provided thread is not on "suspended" list;
*/
int resume(ThreadList::iterator iterator);
/**
* \brief Suspends current thread.
*
* \return 0 on success, error code otherwise:
* - EINTR - thread was unblocked with ThreadControlBlock::UnblockReason::signal;
*/
int suspend();
/**
* \brief Suspends thread.
*
* The thread must be on "runnable" list - trying to suspend thread in other state is an error.
*
* \param [in] iterator is the iterator to the thread that will be suspended
*
* \return 0 on success, error code otherwise:
* - EINTR - thread was unblocked with ThreadControlBlock::UnblockReason::signal;
* - EINVAL - provided thread is not on "runnable" list;
*/
int suspend(ThreadList::iterator iterator);
/**
* \brief Called by architecture-specific code to do final context switch.
*
* Current task is suspended and the next available task is started.
*
* \param [in] stackPointer is the current value of current thread's stack pointer
*
* \return new thread's stack pointer
*/
void* switchContext(void* stackPointer);
/**
* \brief Handler of "tick" interrupt.
*
* \note this must not be called by user code
*
* \return true if context switch is required, false otherwise
*/
bool tickInterruptHandler();
/**
* \brief Unblocks provided thread, transferring it from it's current container to "runnable" container.
*
* Current container of the thread is obtained with ThreadControlBlock::getList().
*
* \param [in] iterator is the iterator which points to unblocked thread
* \param [in] unblockReason is the reason of unblocking of the thread, default -
* ThreadControlBlock::UnblockReason::unblockRequest
*/
void unblock(ThreadList::iterator iterator,
ThreadControlBlock::UnblockReason unblockReason = ThreadControlBlock::UnblockReason::unblockRequest);
/**
* \brief Yields time slot of the scheduler to next thread.
*/
void yield();
private:
/**
* \brief Adds new ThreadControlBlock to scheduler.
*
* Internal version - without interrupt masking and call to Scheduler::maybeRequestContextSwitch()
*
* \param [in] threadControlBlock is a reference to added ThreadControlBlock object
*
* \return 0 on success, error code otherwise:
* - error codes returned by ThreadControlBlock::addHook();
*/
int addInternal(ThreadControlBlock& threadControlBlock);
/**
* \brief Blocks thread, transferring it to provided container.
*
* Internal version - without interrupt masking and forced context switch.
*
* \param [in] container is a reference to destination container to which the thread will be transferred
* \param [in] iterator is the iterator to the thread that will be blocked
* \param [in] state is the new state of thread that will be blocked
* \param [in] unblockFunctor is a pointer to ThreadControlBlock::UnblockFunctor which will be executed in
* ThreadControlBlock::unblockHook()
*
* \return 0 on success, error code otherwise:
* - EINVAL - provided thread is not on "runnable" list;
*/
int blockInternal(ThreadList& container, ThreadList::iterator iterator, ThreadState state,
const ThreadControlBlock::UnblockFunctor* unblockFunctor);
/**
* \brief Tests whether context switch is required or not.
*
* Context switch is required in following situations:
* - current thread is no longer on "runnable" list,
* - current thread is no longer on the beginning of the "runnable" list (because higher-priority thread is
* available or current thread was "rotated" due to round-robin scheduling policy).
*
* \return true if context switch is required
*/
bool isContextSwitchRequired() const;
/**
* \brief Unblocks provided thread, transferring it from it's current container to "runnable" container.
*
* Current container of the thread is obtained with ThreadControlBlock::getList(). Round-robin quantum of thread is
* reset.
*
* \note Internal version - without interrupt masking and yield()
*
* \param [in] iterator is the iterator which points to unblocked thread
* \param [in] unblockReason is the reason of unblocking of the thread
*/
void unblockInternal(ThreadList::iterator iterator, ThreadControlBlock::UnblockReason unblockReason);
/// iterator to the currently active ThreadControlBlock
ThreadList::iterator currentThreadControlBlock_;
/// list of ThreadControlBlock elements in "runnable" state, sorted by priority in descending order
ThreadList runnableList_;
/// list of ThreadControlBlock elements in "suspended" state, sorted by priority in descending order
ThreadList suspendedList_;
/// internal SoftwareTimerSupervisor object
SoftwareTimerSupervisor softwareTimerSupervisor_;
/// number of context switches
uint64_t contextSwitchCount_;
/// tick count
uint64_t tickCount_;
};
} // namespace internal
} // namespace distortos
#endif // INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_SCHEDULER_HPP_
|