libroot_debug: Merge guarded heap into libroot_debug.
[haiku.git] / src / system / kernel / DPC.cpp
blob4d7261663c33772cf2201505b7df5187c6ddb54e
1 /*
2 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include <DPC.h>
9 #include <util/AutoLock.h>
12 #define NORMAL_PRIORITY B_NORMAL_PRIORITY
13 #define HIGH_PRIORITY B_URGENT_DISPLAY_PRIORITY
14 #define REAL_TIME_PRIORITY B_FIRST_REAL_TIME_PRIORITY
16 #define DEFAULT_QUEUE_SLOT_COUNT 64
19 static DPCQueue sNormalPriorityQueue;
20 static DPCQueue sHighPriorityQueue;
21 static DPCQueue sRealTimePriorityQueue;
24 // #pragma mark - FunctionDPCCallback
27 FunctionDPCCallback::FunctionDPCCallback(DPCQueue* owner)
29 fOwner(owner)
34 void
35 FunctionDPCCallback::SetTo(void (*function)(void*), void* argument)
37 fFunction = function;
38 fArgument = argument;
42 void
43 FunctionDPCCallback::DoDPC(DPCQueue* queue)
45 fFunction(fArgument);
47 if (fOwner != NULL)
48 fOwner->Recycle(this);
52 // #pragma mark - DPCCallback
55 DPCCallback::DPCCallback()
57 fInQueue(NULL)
62 DPCCallback::~DPCCallback()
67 // #pragma mark - DPCQueue
70 DPCQueue::DPCQueue()
72 fThreadID(-1),
73 fCallbackInProgress(NULL),
74 fCallbackDoneCondition(NULL)
76 B_INITIALIZE_SPINLOCK(&fLock);
78 fPendingCallbacksCondition.Init(this, "dpc queue");
82 DPCQueue::~DPCQueue()
84 // close, if not closed yet
86 InterruptsSpinLocker locker(fLock);
87 if (!_IsClosed()) {
88 locker.Unlock();
89 Close(false);
93 // delete function callbacks
94 while (DPCCallback* callback = fUnusedFunctionCallbacks.RemoveHead())
95 delete callback;
99 /*static*/ DPCQueue*
100 DPCQueue::DefaultQueue(int priority)
102 if (priority <= NORMAL_PRIORITY)
103 return &sNormalPriorityQueue;
105 if (priority <= HIGH_PRIORITY)
106 return &sHighPriorityQueue;
108 return &sRealTimePriorityQueue;
112 status_t
113 DPCQueue::Init(const char* name, int32 priority, uint32 reservedSlots)
115 // create function callbacks
116 for (uint32 i = 0; i < reservedSlots; i++) {
117 FunctionDPCCallback* callback
118 = new(std::nothrow) FunctionDPCCallback(this);
119 if (callback == NULL)
120 return B_NO_MEMORY;
122 fUnusedFunctionCallbacks.Add(callback);
125 // spawn the thread
126 fThreadID = spawn_kernel_thread(&_ThreadEntry, name, priority, this);
127 if (fThreadID < 0)
128 return fThreadID;
130 resume_thread(fThreadID);
132 return B_OK;
136 void
137 DPCQueue::Close(bool cancelPending)
139 InterruptsSpinLocker locker(fLock);
141 if (_IsClosed())
142 return;
144 // If requested, dequeue all pending callbacks
145 if (cancelPending)
146 fCallbacks.MakeEmpty();
148 // mark the queue closed
149 thread_id thread = fThreadID;
150 fThreadID = -1;
152 locker.Unlock();
154 // wake up the thread and wait for it
155 fPendingCallbacksCondition.NotifyAll();
156 wait_for_thread(thread, NULL);
160 status_t
161 DPCQueue::Add(DPCCallback* callback)
163 // queue the callback, if the queue isn't closed already
164 InterruptsSpinLocker locker(fLock);
166 if (_IsClosed())
167 return B_NOT_INITIALIZED;
169 bool wasEmpty = fCallbacks.IsEmpty();
170 fCallbacks.Add(callback);
171 callback->fInQueue = this;
173 locker.Unlock();
175 // notify the condition variable, if necessary
176 if (wasEmpty)
177 fPendingCallbacksCondition.NotifyAll();
179 return B_OK;
183 status_t
184 DPCQueue::Add(void (*function)(void*), void* argument)
186 if (function == NULL)
187 return B_BAD_VALUE;
189 // get a free callback
190 InterruptsSpinLocker locker(fLock);
192 DPCCallback* callback = fUnusedFunctionCallbacks.RemoveHead();
193 if (callback == NULL)
194 return B_NO_MEMORY;
196 locker.Unlock();
198 // init the callback
199 FunctionDPCCallback* functionCallback
200 = static_cast<FunctionDPCCallback*>(callback);
201 functionCallback->SetTo(function, argument);
203 // add it
204 status_t error = Add(functionCallback);
205 if (error != B_OK)
206 Recycle(functionCallback);
208 return error;
212 bool
213 DPCQueue::Cancel(DPCCallback* callback)
215 InterruptsSpinLocker locker(fLock);
217 // If the callback is queued, remove it.
218 if (callback->fInQueue == this) {
219 fCallbacks.Remove(callback);
220 return true;
223 // The callback is not queued. If it isn't in progress, we're done, too.
224 if (callback != fCallbackInProgress)
225 return false;
227 // The callback is currently being executed. We need to wait for it to be
228 // done.
230 // Set the respective condition, if not set yet. For the unlikely case that
231 // there are multiple threads trying to cancel the callback at the same
232 // time, the condition variable of the first thread will be used.
233 ConditionVariable condition;
234 if (fCallbackDoneCondition == NULL)
235 fCallbackDoneCondition = &condition;
237 // add our wait entry
238 ConditionVariableEntry waitEntry;
239 fCallbackDoneCondition->Add(&waitEntry);
241 // wait
242 locker.Unlock();
243 waitEntry.Wait();
245 return false;
249 void
250 DPCQueue::Recycle(FunctionDPCCallback* callback)
252 InterruptsSpinLocker locker(fLock);
253 fUnusedFunctionCallbacks.Insert(callback, false);
257 /*static*/ status_t
258 DPCQueue::_ThreadEntry(void* data)
260 return ((DPCQueue*)data)->_Thread();
264 status_t
265 DPCQueue::_Thread()
267 while (true) {
268 InterruptsSpinLocker locker(fLock);
270 // get the next pending callback
271 DPCCallback* callback = fCallbacks.RemoveHead();
272 if (callback == NULL) {
273 // nothing is pending -- wait unless the queue is already closed
274 if (_IsClosed())
275 break;
277 ConditionVariableEntry waitEntry;
278 fPendingCallbacksCondition.Add(&waitEntry);
280 locker.Unlock();
281 waitEntry.Wait();
283 continue;
286 callback->fInQueue = NULL;
287 fCallbackInProgress = callback;
289 // call the callback
290 locker.Unlock();
291 callback->DoDPC(this);
292 locker.Lock();
294 fCallbackInProgress = NULL;
296 // wake up threads waiting for the callback to be done
297 ConditionVariable* doneCondition = fCallbackDoneCondition;
298 fCallbackDoneCondition = NULL;
299 locker.Unlock();
300 if (doneCondition != NULL)
301 doneCondition->NotifyAll();
304 return B_OK;
308 // #pragma mark - kernel private
311 void
312 dpc_init()
314 // create the default queues
315 new(&sNormalPriorityQueue) DPCQueue;
316 new(&sHighPriorityQueue) DPCQueue;
317 new(&sRealTimePriorityQueue) DPCQueue;
319 if (sNormalPriorityQueue.Init("dpc: normal priority", NORMAL_PRIORITY,
320 DEFAULT_QUEUE_SLOT_COUNT) != B_OK
321 || sHighPriorityQueue.Init("dpc: high priority", HIGH_PRIORITY,
322 DEFAULT_QUEUE_SLOT_COUNT) != B_OK
323 || sRealTimePriorityQueue.Init("dpc: real-time priority",
324 REAL_TIME_PRIORITY, DEFAULT_QUEUE_SLOT_COUNT) != B_OK) {
325 panic("Failed to create default DPC queues!");