The Pedigree Project 0.1
RoundRobin.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <config.h>
21
22#if THREADS
23#include "pedigree/kernel/ActivityDiagnostics.h"
24#include "pedigree/kernel/LockGuard.h"
25#include "pedigree/kernel/process/RoundRobin.h"
26#include "pedigree/kernel/process/Thread.h"
27#include "pedigree/kernel/processor/Processor.h"
28#include "pedigree/kernel/processor/types.h"
29#include "pedigree/kernel/utilities/assert.h"
30
31RoundRobin::RoundRobin() : m_Lock(false) {
32 for (size_t i = 0; i < MAX_PRIORITIES; ++i) {
33 m_pReadyQueueHeads[i] = nullptr;
34 m_pReadyQueueTails[i] = nullptr;
35#if PEDIGREE_READY_QUEUE_COUNTS
36 m_ReadyQueueCounts[i] = 0;
37#endif
38 }
39}
40
42 for (size_t i = 0; i < MAX_PRIORITIES; ++i) {
43 while (m_pReadyQueueHeads[i]) {
44 unlink(m_pReadyQueueHeads[i]);
45 }
46 assert(!m_pReadyQueueTails[i]);
47 }
48}
49
51
53 LockGuard<Spinlock> guard(m_Lock);
54 unlink(pThread);
55}
56
57void RoundRobin::enqueue(Thread* pThread) {
58 assert(pThread);
59 assert(isReady(pThread));
60 assert(!pThread->m_bReadyQueued);
61 assert(!pThread->m_pReadyPrevious);
62 assert(!pThread->m_pReadyNext);
63 assert(pThread->getPriority() < MAX_PRIORITIES);
64
65 const size_t priority = pThread->getPriority();
66 pThread->m_pReadyPrevious = m_pReadyQueueTails[priority];
67 pThread->m_ReadyQueuePriority = priority;
68 pThread->m_bReadyQueued = true;
69 if (m_pReadyQueueTails[priority]) {
70 m_pReadyQueueTails[priority]->m_pReadyNext = pThread;
71 } else {
72 m_pReadyQueueHeads[priority] = pThread;
73 }
74 m_pReadyQueueTails[priority] = pThread;
75#if PEDIGREE_READY_QUEUE_COUNTS
76 ++m_ReadyQueueCounts[priority];
77#endif
78}
79
80void RoundRobin::unlink(Thread* pThread) {
81 if (!pThread || !pThread->m_bReadyQueued) {
82 return;
83 }
84
85 const size_t priority = pThread->m_ReadyQueuePriority;
86 assert(priority < MAX_PRIORITIES);
87 if (pThread->m_pReadyPrevious) {
88 pThread->m_pReadyPrevious->m_pReadyNext = pThread->m_pReadyNext;
89 } else {
90 assert(m_pReadyQueueHeads[priority] == pThread);
91 m_pReadyQueueHeads[priority] = pThread->m_pReadyNext;
92 }
93 if (pThread->m_pReadyNext) {
94 pThread->m_pReadyNext->m_pReadyPrevious = pThread->m_pReadyPrevious;
95 } else {
96 assert(m_pReadyQueueTails[priority] == pThread);
97 m_pReadyQueueTails[priority] = pThread->m_pReadyPrevious;
98 }
99
100 pThread->m_pReadyPrevious = nullptr;
101 pThread->m_pReadyNext = nullptr;
102 pThread->m_ReadyQueuePriority = MAX_PRIORITIES;
103 pThread->m_bReadyQueued = false;
104#if PEDIGREE_READY_QUEUE_COUNTS
105 assert(m_ReadyQueueCounts[priority]);
106 --m_ReadyQueueCounts[priority];
107#endif
108}
109
112 LockGuard<Spinlock> guard(m_Lock);
113
114 Thread* pThread = 0;
115 for (size_t i = 0; i < MAX_PRIORITIES; i++) {
116 // Bound the scan so a stale entry whose priority changes cannot be
117#if PEDIGREE_READY_QUEUE_COUNTS
118 // requeued forever in the same selection pass. The maintained count
119 // avoids walking this list once merely to determine that bound.
120 size_t candidates = m_ReadyQueueCounts[i];
121#else
122 // Bound the scan so a stale entry whose priority changes cannot be
123 // requeued forever in the same selection pass.
124 size_t candidates = 0;
125 for (pThread = m_pReadyQueueHeads[i]; pThread; pThread = pThread->m_pReadyNext)
126 ++candidates;
127#endif
128 while (candidates--) {
129 pThread = m_pReadyQueueHeads[i];
130 assert(pThread);
131 ActivityDiagnostics::recordReadyQueueCandidateVisit();
132 unlink(pThread);
133
134 if (pThread == pCurrentThread || !isReady(pThread)) {
135 continue;
136 }
137
138 if (pThread->getPriority() != i) {
139 enqueue(pThread);
140 continue;
141 }
142
143 return pThread;
144 }
145 }
146 ActivityDiagnostics::recordSchedulerNoEligibleSelection();
147 return 0;
148}
149
151 LockGuard<Spinlock> guard(m_Lock);
152 for (size_t i = 0; i < MAX_PRIORITIES; ++i) {
153 if (m_pReadyQueueHeads[i]) {
154 return true;
155 }
156 }
157 return false;
158}
159
161 LockGuard<Spinlock> guard(m_Lock);
162
163 if (pThread->m_bReadyQueued) {
164 if (!RoundRobin::isReady(pThread) || pThread->m_ReadyQueuePriority != pThread->getPriority()) {
165 unlink(pThread);
166 } else {
167 return;
168 }
169 }
170
171 if (RoundRobin::isReady(pThread)) {
172 enqueue(pThread);
173 }
174}
175
176bool RoundRobin::isReady(Thread* pThread) {
177 return pThread->getStatus() == Thread::Ready &&
178 !__atomic_load_n(&pThread->m_ReadyPublicationPending, __ATOMIC_ACQUIRE);
179}
180
181#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
182bool RoundRobin::runHostedIntrusiveQueueRegressions(Thread* pThread) {
183 if (!pThread || pThread->m_bReadyQueued || pThread->m_pReadyPrevious || pThread->m_pReadyNext) {
184 return false;
185 }
186
187 const bool interrupts = Processor::getInterrupts();
189 const Thread::Status status = pThread->m_Status;
190 const size_t priority = pThread->m_Priority;
191 bool passed = true;
192
193 {
194 RoundRobin queue;
195 pThread->m_Status = Thread::Ready;
196 pThread->m_Priority = 1;
197 queue.threadStatusChanged(pThread);
198 queue.threadStatusChanged(pThread);
199 passed &= pThread->m_bReadyQueued && pThread->m_ReadyQueuePriority == 1 &&
200 !pThread->m_pReadyPrevious && !pThread->m_pReadyNext;
201
202 pThread->m_Priority = 2;
203 queue.threadStatusChanged(pThread);
204 passed &= pThread->m_bReadyQueued && pThread->m_ReadyQueuePriority == 2 &&
205 !pThread->m_pReadyPrevious && !pThread->m_pReadyNext;
206
207 pThread->m_Status = Thread::AwaitingJoin;
208 passed &= !queue.getNext(nullptr) && !pThread->m_bReadyQueued;
209
210 pThread->m_Status = Thread::Ready;
211 queue.threadStatusChanged(pThread);
212 passed &= !queue.getNext(pThread) && !pThread->m_bReadyQueued;
213
214 pThread->m_Status = Thread::Ready;
215 queue.threadStatusChanged(pThread);
216 passed &= pThread->m_bReadyQueued;
217 }
218
219 passed &= !pThread->m_bReadyQueued && !pThread->m_pReadyPrevious && !pThread->m_pReadyNext &&
220 pThread->m_ReadyQueuePriority == MAX_PRIORITIES;
221 {
222 RoundRobin reused;
223 pThread->m_Status = Thread::Ready;
224 pThread->m_Priority = 0;
225 reused.threadStatusChanged(pThread);
226 passed &= reused.getNext(nullptr) == pThread && !pThread->m_bReadyQueued;
227 }
228
229 pThread->m_Status = status;
230 pThread->m_Priority = priority;
231 Processor::setInterrupts(interrupts);
232 return passed;
233}
234#endif
235
236#endif
static bool getInterrupts()
static void setInterrupts(bool bEnable)
virtual bool hasReady()
virtual ~RoundRobin()
Definition RoundRobin.cc:41
virtual void threadStatusChanged(Thread *pThread)
virtual void addThread(Thread *pThread)
Definition RoundRobin.cc:50
virtual Thread * getNext(Thread *pCurrentThread)
virtual void removeThread(Thread *pThread)
Definition RoundRobin.cc:52
volatile Status m_Status
Definition Thread.h:1310
Thread * m_pReadyPrevious
Definition Thread.h:1243
Status getStatus() const
Definition Thread.h:433
size_t m_Priority
Definition Thread.h:1240