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
|
/*
ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio.
This file is part of ChibiOS.
ChibiOS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file chpreempt.c
* @brief Preemption enforcement code.
*
* @addtogroup preemption_enforcement
* @details This modules export hook macros required for implementing
* a preemptive round robin mode for threads at the same priority
* level.<br>
* This method is alternative to the ChibiOS/RT native implementation
* which is not compatible with the tick-less mode, however, this
* timers-based solution can decrease threads context-switch
* performance because the added overhead.
* @note This file is not included automatically by @p ch.h, you need
* to:
* - Define @p CH_CFG_ROUND_ROBIN_QUANTUM in chconf.h. It is the
* time quantum in ticks.
* - Include @p chpreempt.h from @p chconf.h.
* - Add all the macros and functions to the appropriate hooks in
* chconf.h.
* - Explicitely add @p chpreempt.c to your makefile.
* .
*/
#include "ch.h"
/*===========================================================================*/
/* Module exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Module local types. */
/*===========================================================================*/
/*===========================================================================*/
/* Module local variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Module local functions. */
/*===========================================================================*/
static void preempt_cb(void *p) {
(void)p;
}
/*===========================================================================*/
/* Module exported functions. */
/*===========================================================================*/
/**
* @brief Hook code for system initialization.
*
* @notapi
*/
void ch_preempt_system_init(void) {
chVTObjectInit(&ch.preempt_vt);
}
/**
* @brief Hook code for context switch.
*
* @notapi
*/
void ch_preempt_thread_switch(void) {
chVTSetI(&ch.preempt_vt, CH_CFG_ROUND_ROBIN_QUANTUM, preempt_cb, NULL);
}
/**
* @brief Evaluates if preemption is required.
* @details The decision is taken by comparing the relative priorities and
* depending on the state of the round robin timeout counter.
* @note Not a user function, it is meant to be invoked by the scheduler
* itself or from within the port layer.
*
* @retval true if there is a thread that must go in running state
* immediately.
* @retval false if preemption is not required.
*
* @special
*/
bool chSchIsPreemptionRequired(void) {
tprio_t p1 = firstprio(&ch.rlist.queue);
tprio_t p2 = currp->prio;
/* If the running thread has not reached its time quantum, reschedule only
if the first thread on the ready queue has a higher priority.
Otherwise, if the running thread has used up its time quantum, reschedule
if the first thread on the ready queue has equal or higher priority.*/
return chVTIsArmed(&ch.preempt_vt) ? (p1 > p2) : (p1 >= p2);
}
/**
* @brief Switches to the first thread on the runnable queue.
* @details The current thread is positioned in the ready list behind or
* ahead of all threads having the same priority depending on
* if it used its whole time slice.
* @note Not a user function, it is meant to be invoked by the scheduler
* itself or from within the port layer.
*
* @special
*/
void chSchDoReschedule(void) {
thread_t *otp = currp;
/* Picks the first thread from the ready queue and makes it current.*/
currp = queue_fifo_remove(&ch.rlist.queue);
currp->state = CH_STATE_CURRENT;
/* Handling idle-leave hook.*/
if (otp->prio == IDLEPRIO) {
CH_CFG_IDLE_LEAVE_HOOK();
}
/* There are two different scenarios to handle on preemption: time quantum
elapsed or not.*/
if (!chVTIsArmed(&ch.preempt_vt)) {
/* The thread consumed its time quantum so it is enqueued behind threads
with same priority level, however, it acquires a new time quantum.*/
otp = chSchReadyI(otp);
}
else {
/* The thread didn't consume all its time quantum so it is put ahead of
threads with equal priority and does not acquire a new time quantum.*/
otp = chSchReadyAheadI(otp);
}
/* Swap operation as tail call.*/
chSysSwitch(currp, otp);
}
/** @} */
|