2 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
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
)
35 FunctionDPCCallback::SetTo(void (*function
)(void*), void* argument
)
43 FunctionDPCCallback::DoDPC(DPCQueue
* queue
)
48 fOwner
->Recycle(this);
52 // #pragma mark - DPCCallback
55 DPCCallback::DPCCallback()
62 DPCCallback::~DPCCallback()
67 // #pragma mark - DPCQueue
73 fCallbackInProgress(NULL
),
74 fCallbackDoneCondition(NULL
)
76 B_INITIALIZE_SPINLOCK(&fLock
);
78 fPendingCallbacksCondition
.Init(this, "dpc queue");
84 // close, if not closed yet
86 InterruptsSpinLocker
locker(fLock
);
93 // delete function callbacks
94 while (DPCCallback
* callback
= fUnusedFunctionCallbacks
.RemoveHead())
100 DPCQueue::DefaultQueue(int priority
)
102 if (priority
<= NORMAL_PRIORITY
)
103 return &sNormalPriorityQueue
;
105 if (priority
<= HIGH_PRIORITY
)
106 return &sHighPriorityQueue
;
108 return &sRealTimePriorityQueue
;
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
)
122 fUnusedFunctionCallbacks
.Add(callback
);
126 fThreadID
= spawn_kernel_thread(&_ThreadEntry
, name
, priority
, this);
130 resume_thread(fThreadID
);
137 DPCQueue::Close(bool cancelPending
)
139 InterruptsSpinLocker
locker(fLock
);
144 // If requested, dequeue all pending callbacks
146 fCallbacks
.MakeEmpty();
148 // mark the queue closed
149 thread_id thread
= fThreadID
;
154 // wake up the thread and wait for it
155 fPendingCallbacksCondition
.NotifyAll();
156 wait_for_thread(thread
, NULL
);
161 DPCQueue::Add(DPCCallback
* callback
)
163 // queue the callback, if the queue isn't closed already
164 InterruptsSpinLocker
locker(fLock
);
167 return B_NOT_INITIALIZED
;
169 bool wasEmpty
= fCallbacks
.IsEmpty();
170 fCallbacks
.Add(callback
);
171 callback
->fInQueue
= this;
175 // notify the condition variable, if necessary
177 fPendingCallbacksCondition
.NotifyAll();
184 DPCQueue::Add(void (*function
)(void*), void* argument
)
186 if (function
== NULL
)
189 // get a free callback
190 InterruptsSpinLocker
locker(fLock
);
192 DPCCallback
* callback
= fUnusedFunctionCallbacks
.RemoveHead();
193 if (callback
== NULL
)
199 FunctionDPCCallback
* functionCallback
200 = static_cast<FunctionDPCCallback
*>(callback
);
201 functionCallback
->SetTo(function
, argument
);
204 status_t error
= Add(functionCallback
);
206 Recycle(functionCallback
);
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
);
223 // The callback is not queued. If it isn't in progress, we're done, too.
224 if (callback
!= fCallbackInProgress
)
227 // The callback is currently being executed. We need to wait for it to be
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
);
250 DPCQueue::Recycle(FunctionDPCCallback
* callback
)
252 InterruptsSpinLocker
locker(fLock
);
253 fUnusedFunctionCallbacks
.Insert(callback
, false);
258 DPCQueue::_ThreadEntry(void* data
)
260 return ((DPCQueue
*)data
)->_Thread();
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
277 ConditionVariableEntry waitEntry
;
278 fPendingCallbacksCondition
.Add(&waitEntry
);
286 callback
->fInQueue
= NULL
;
287 fCallbackInProgress
= callback
;
291 callback
->DoDPC(this);
294 fCallbackInProgress
= NULL
;
296 // wake up threads waiting for the callback to be done
297 ConditionVariable
* doneCondition
= fCallbackDoneCondition
;
298 fCallbackDoneCondition
= NULL
;
300 if (doneCondition
!= NULL
)
301 doneCondition
->NotifyAll();
308 // #pragma mark - kernel private
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!");