124df6592SIngo Weinhold /*
224df6592SIngo Weinhold * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
324df6592SIngo Weinhold * Distributed under the terms of the MIT License.
424df6592SIngo Weinhold */
524df6592SIngo Weinhold
624df6592SIngo Weinhold
724df6592SIngo Weinhold #include <DPC.h>
824df6592SIngo Weinhold
924df6592SIngo Weinhold #include <util/AutoLock.h>
1024df6592SIngo Weinhold
1124df6592SIngo Weinhold
1224df6592SIngo Weinhold #define NORMAL_PRIORITY B_NORMAL_PRIORITY
1324df6592SIngo Weinhold #define HIGH_PRIORITY B_URGENT_DISPLAY_PRIORITY
1424df6592SIngo Weinhold #define REAL_TIME_PRIORITY B_FIRST_REAL_TIME_PRIORITY
1524df6592SIngo Weinhold
1624df6592SIngo Weinhold #define DEFAULT_QUEUE_SLOT_COUNT 64
1724df6592SIngo Weinhold
1824df6592SIngo Weinhold
1924df6592SIngo Weinhold static DPCQueue sNormalPriorityQueue;
2024df6592SIngo Weinhold static DPCQueue sHighPriorityQueue;
2124df6592SIngo Weinhold static DPCQueue sRealTimePriorityQueue;
2224df6592SIngo Weinhold
2324df6592SIngo Weinhold
2424df6592SIngo Weinhold // #pragma mark - FunctionDPCCallback
2524df6592SIngo Weinhold
2624df6592SIngo Weinhold
FunctionDPCCallback(DPCQueue * owner)2724df6592SIngo Weinhold FunctionDPCCallback::FunctionDPCCallback(DPCQueue* owner)
2824df6592SIngo Weinhold :
2924df6592SIngo Weinhold fOwner(owner)
3024df6592SIngo Weinhold {
3124df6592SIngo Weinhold }
3224df6592SIngo Weinhold
3324df6592SIngo Weinhold
3424df6592SIngo Weinhold void
SetTo(void (* function)(void *),void * argument)3524df6592SIngo Weinhold FunctionDPCCallback::SetTo(void (*function)(void*), void* argument)
3624df6592SIngo Weinhold {
3724df6592SIngo Weinhold fFunction = function;
3824df6592SIngo Weinhold fArgument = argument;
3924df6592SIngo Weinhold }
4024df6592SIngo Weinhold
4124df6592SIngo Weinhold
4224df6592SIngo Weinhold void
DoDPC(DPCQueue * queue)4324df6592SIngo Weinhold FunctionDPCCallback::DoDPC(DPCQueue* queue)
4424df6592SIngo Weinhold {
4524df6592SIngo Weinhold fFunction(fArgument);
4624df6592SIngo Weinhold
4724df6592SIngo Weinhold if (fOwner != NULL)
4824df6592SIngo Weinhold fOwner->Recycle(this);
4924df6592SIngo Weinhold }
5024df6592SIngo Weinhold
5124df6592SIngo Weinhold
5224df6592SIngo Weinhold // #pragma mark - DPCCallback
5324df6592SIngo Weinhold
5424df6592SIngo Weinhold
DPCCallback()5524df6592SIngo Weinhold DPCCallback::DPCCallback()
5624df6592SIngo Weinhold :
5724df6592SIngo Weinhold fInQueue(NULL)
5824df6592SIngo Weinhold {
5924df6592SIngo Weinhold }
6024df6592SIngo Weinhold
6124df6592SIngo Weinhold
~DPCCallback()6224df6592SIngo Weinhold DPCCallback::~DPCCallback()
6324df6592SIngo Weinhold {
6424df6592SIngo Weinhold }
6524df6592SIngo Weinhold
6624df6592SIngo Weinhold
6724df6592SIngo Weinhold // #pragma mark - DPCQueue
6824df6592SIngo Weinhold
6924df6592SIngo Weinhold
DPCQueue()7024df6592SIngo Weinhold DPCQueue::DPCQueue()
7124df6592SIngo Weinhold :
7224df6592SIngo Weinhold fThreadID(-1),
7324df6592SIngo Weinhold fCallbackInProgress(NULL),
7424df6592SIngo Weinhold fCallbackDoneCondition(NULL)
7524df6592SIngo Weinhold {
7624df6592SIngo Weinhold B_INITIALIZE_SPINLOCK(&fLock);
7724df6592SIngo Weinhold
7824df6592SIngo Weinhold fPendingCallbacksCondition.Init(this, "dpc queue");
7924df6592SIngo Weinhold }
8024df6592SIngo Weinhold
8124df6592SIngo Weinhold
~DPCQueue()8224df6592SIngo Weinhold DPCQueue::~DPCQueue()
8324df6592SIngo Weinhold {
8424df6592SIngo Weinhold // close, if not closed yet
8524df6592SIngo Weinhold {
8624df6592SIngo Weinhold InterruptsSpinLocker locker(fLock);
8724df6592SIngo Weinhold if (!_IsClosed()) {
8824df6592SIngo Weinhold locker.Unlock();
8924df6592SIngo Weinhold Close(false);
9024df6592SIngo Weinhold }
9124df6592SIngo Weinhold }
9224df6592SIngo Weinhold
9324df6592SIngo Weinhold // delete function callbacks
9424df6592SIngo Weinhold while (DPCCallback* callback = fUnusedFunctionCallbacks.RemoveHead())
9524df6592SIngo Weinhold delete callback;
9624df6592SIngo Weinhold }
9724df6592SIngo Weinhold
9824df6592SIngo Weinhold
9924df6592SIngo Weinhold /*static*/ DPCQueue*
DefaultQueue(int priority)10024df6592SIngo Weinhold DPCQueue::DefaultQueue(int priority)
10124df6592SIngo Weinhold {
10224df6592SIngo Weinhold if (priority <= NORMAL_PRIORITY)
10324df6592SIngo Weinhold return &sNormalPriorityQueue;
10424df6592SIngo Weinhold
10524df6592SIngo Weinhold if (priority <= HIGH_PRIORITY)
10624df6592SIngo Weinhold return &sHighPriorityQueue;
10724df6592SIngo Weinhold
10824df6592SIngo Weinhold return &sRealTimePriorityQueue;
10924df6592SIngo Weinhold }
11024df6592SIngo Weinhold
11124df6592SIngo Weinhold
11224df6592SIngo Weinhold status_t
Init(const char * name,int32 priority,uint32 reservedSlots)11324df6592SIngo Weinhold DPCQueue::Init(const char* name, int32 priority, uint32 reservedSlots)
11424df6592SIngo Weinhold {
11524df6592SIngo Weinhold // create function callbacks
11624df6592SIngo Weinhold for (uint32 i = 0; i < reservedSlots; i++) {
11724df6592SIngo Weinhold FunctionDPCCallback* callback
11824df6592SIngo Weinhold = new(std::nothrow) FunctionDPCCallback(this);
11924df6592SIngo Weinhold if (callback == NULL)
12024df6592SIngo Weinhold return B_NO_MEMORY;
12124df6592SIngo Weinhold
12224df6592SIngo Weinhold fUnusedFunctionCallbacks.Add(callback);
12324df6592SIngo Weinhold }
12424df6592SIngo Weinhold
12524df6592SIngo Weinhold // spawn the thread
12624df6592SIngo Weinhold fThreadID = spawn_kernel_thread(&_ThreadEntry, name, priority, this);
12724df6592SIngo Weinhold if (fThreadID < 0)
12824df6592SIngo Weinhold return fThreadID;
12924df6592SIngo Weinhold
13024df6592SIngo Weinhold resume_thread(fThreadID);
13124df6592SIngo Weinhold
13224df6592SIngo Weinhold return B_OK;
13324df6592SIngo Weinhold }
13424df6592SIngo Weinhold
13524df6592SIngo Weinhold
13624df6592SIngo Weinhold void
Close(bool cancelPending)13724df6592SIngo Weinhold DPCQueue::Close(bool cancelPending)
13824df6592SIngo Weinhold {
13924df6592SIngo Weinhold InterruptsSpinLocker locker(fLock);
14024df6592SIngo Weinhold
14124df6592SIngo Weinhold if (_IsClosed())
14224df6592SIngo Weinhold return;
14324df6592SIngo Weinhold
14424df6592SIngo Weinhold // If requested, dequeue all pending callbacks
14524df6592SIngo Weinhold if (cancelPending)
14624df6592SIngo Weinhold fCallbacks.MakeEmpty();
14724df6592SIngo Weinhold
14824df6592SIngo Weinhold // mark the queue closed
14924df6592SIngo Weinhold thread_id thread = fThreadID;
15024df6592SIngo Weinhold fThreadID = -1;
15124df6592SIngo Weinhold
15224df6592SIngo Weinhold locker.Unlock();
15324df6592SIngo Weinhold
15424df6592SIngo Weinhold // wake up the thread and wait for it
15524df6592SIngo Weinhold fPendingCallbacksCondition.NotifyAll();
15624df6592SIngo Weinhold wait_for_thread(thread, NULL);
15724df6592SIngo Weinhold }
15824df6592SIngo Weinhold
15924df6592SIngo Weinhold
16024df6592SIngo Weinhold status_t
Add(DPCCallback * callback)1613c819aaaSPawel Dziepak DPCQueue::Add(DPCCallback* callback)
16224df6592SIngo Weinhold {
16324df6592SIngo Weinhold // queue the callback, if the queue isn't closed already
16424df6592SIngo Weinhold InterruptsSpinLocker locker(fLock);
16524df6592SIngo Weinhold
16624df6592SIngo Weinhold if (_IsClosed())
16724df6592SIngo Weinhold return B_NOT_INITIALIZED;
16824df6592SIngo Weinhold
169*e925863dSX512 if (callback->fInQueue != NULL)
170*e925863dSX512 return EALREADY;
171*e925863dSX512
17224df6592SIngo Weinhold bool wasEmpty = fCallbacks.IsEmpty();
17324df6592SIngo Weinhold fCallbacks.Add(callback);
17424df6592SIngo Weinhold callback->fInQueue = this;
17524df6592SIngo Weinhold
17624df6592SIngo Weinhold locker.Unlock();
17724df6592SIngo Weinhold
17824df6592SIngo Weinhold // notify the condition variable, if necessary
17924df6592SIngo Weinhold if (wasEmpty)
1803c819aaaSPawel Dziepak fPendingCallbacksCondition.NotifyAll();
18124df6592SIngo Weinhold
18224df6592SIngo Weinhold return B_OK;
18324df6592SIngo Weinhold }
18424df6592SIngo Weinhold
18524df6592SIngo Weinhold
18624df6592SIngo Weinhold status_t
Add(void (* function)(void *),void * argument)1873c819aaaSPawel Dziepak DPCQueue::Add(void (*function)(void*), void* argument)
18824df6592SIngo Weinhold {
18924df6592SIngo Weinhold if (function == NULL)
19024df6592SIngo Weinhold return B_BAD_VALUE;
19124df6592SIngo Weinhold
19224df6592SIngo Weinhold // get a free callback
19324df6592SIngo Weinhold InterruptsSpinLocker locker(fLock);
19424df6592SIngo Weinhold
19524df6592SIngo Weinhold DPCCallback* callback = fUnusedFunctionCallbacks.RemoveHead();
19624df6592SIngo Weinhold if (callback == NULL)
19724df6592SIngo Weinhold return B_NO_MEMORY;
19824df6592SIngo Weinhold
19924df6592SIngo Weinhold locker.Unlock();
20024df6592SIngo Weinhold
20124df6592SIngo Weinhold // init the callback
20224df6592SIngo Weinhold FunctionDPCCallback* functionCallback
20324df6592SIngo Weinhold = static_cast<FunctionDPCCallback*>(callback);
20424df6592SIngo Weinhold functionCallback->SetTo(function, argument);
20524df6592SIngo Weinhold
20624df6592SIngo Weinhold // add it
2073c819aaaSPawel Dziepak status_t error = Add(functionCallback);
20824df6592SIngo Weinhold if (error != B_OK)
20924df6592SIngo Weinhold Recycle(functionCallback);
21024df6592SIngo Weinhold
21124df6592SIngo Weinhold return error;
21224df6592SIngo Weinhold }
21324df6592SIngo Weinhold
21424df6592SIngo Weinhold
21524df6592SIngo Weinhold bool
Cancel(DPCCallback * callback)21624df6592SIngo Weinhold DPCQueue::Cancel(DPCCallback* callback)
21724df6592SIngo Weinhold {
21824df6592SIngo Weinhold InterruptsSpinLocker locker(fLock);
21924df6592SIngo Weinhold
22024df6592SIngo Weinhold // If the callback is queued, remove it.
22124df6592SIngo Weinhold if (callback->fInQueue == this) {
22224df6592SIngo Weinhold fCallbacks.Remove(callback);
22324df6592SIngo Weinhold return true;
22424df6592SIngo Weinhold }
22524df6592SIngo Weinhold
22624df6592SIngo Weinhold // The callback is not queued. If it isn't in progress, we're done, too.
22724df6592SIngo Weinhold if (callback != fCallbackInProgress)
22824df6592SIngo Weinhold return false;
22924df6592SIngo Weinhold
23024df6592SIngo Weinhold // The callback is currently being executed. We need to wait for it to be
23124df6592SIngo Weinhold // done.
23224df6592SIngo Weinhold
23324df6592SIngo Weinhold // Set the respective condition, if not set yet. For the unlikely case that
23424df6592SIngo Weinhold // there are multiple threads trying to cancel the callback at the same
23524df6592SIngo Weinhold // time, the condition variable of the first thread will be used.
23624df6592SIngo Weinhold ConditionVariable condition;
23724df6592SIngo Weinhold if (fCallbackDoneCondition == NULL)
23824df6592SIngo Weinhold fCallbackDoneCondition = &condition;
23924df6592SIngo Weinhold
24024df6592SIngo Weinhold // add our wait entry
24124df6592SIngo Weinhold ConditionVariableEntry waitEntry;
24224df6592SIngo Weinhold fCallbackDoneCondition->Add(&waitEntry);
24324df6592SIngo Weinhold
24424df6592SIngo Weinhold // wait
24524df6592SIngo Weinhold locker.Unlock();
24624df6592SIngo Weinhold waitEntry.Wait();
24724df6592SIngo Weinhold
24824df6592SIngo Weinhold return false;
24924df6592SIngo Weinhold }
25024df6592SIngo Weinhold
25124df6592SIngo Weinhold
25224df6592SIngo Weinhold void
Recycle(FunctionDPCCallback * callback)25324df6592SIngo Weinhold DPCQueue::Recycle(FunctionDPCCallback* callback)
25424df6592SIngo Weinhold {
25524df6592SIngo Weinhold InterruptsSpinLocker locker(fLock);
25624df6592SIngo Weinhold fUnusedFunctionCallbacks.Insert(callback, false);
25724df6592SIngo Weinhold }
25824df6592SIngo Weinhold
25924df6592SIngo Weinhold
26024df6592SIngo Weinhold /*static*/ status_t
_ThreadEntry(void * data)26124df6592SIngo Weinhold DPCQueue::_ThreadEntry(void* data)
26224df6592SIngo Weinhold {
26324df6592SIngo Weinhold return ((DPCQueue*)data)->_Thread();
26424df6592SIngo Weinhold }
26524df6592SIngo Weinhold
26624df6592SIngo Weinhold
26724df6592SIngo Weinhold status_t
_Thread()26824df6592SIngo Weinhold DPCQueue::_Thread()
26924df6592SIngo Weinhold {
27024df6592SIngo Weinhold while (true) {
27124df6592SIngo Weinhold InterruptsSpinLocker locker(fLock);
27224df6592SIngo Weinhold
27324df6592SIngo Weinhold // get the next pending callback
27424df6592SIngo Weinhold DPCCallback* callback = fCallbacks.RemoveHead();
27524df6592SIngo Weinhold if (callback == NULL) {
27624df6592SIngo Weinhold // nothing is pending -- wait unless the queue is already closed
27724df6592SIngo Weinhold if (_IsClosed())
27824df6592SIngo Weinhold break;
27924df6592SIngo Weinhold
28024df6592SIngo Weinhold ConditionVariableEntry waitEntry;
28124df6592SIngo Weinhold fPendingCallbacksCondition.Add(&waitEntry);
28224df6592SIngo Weinhold
28324df6592SIngo Weinhold locker.Unlock();
28424df6592SIngo Weinhold waitEntry.Wait();
28524df6592SIngo Weinhold
28624df6592SIngo Weinhold continue;
28724df6592SIngo Weinhold }
28824df6592SIngo Weinhold
28924df6592SIngo Weinhold callback->fInQueue = NULL;
29024df6592SIngo Weinhold fCallbackInProgress = callback;
29124df6592SIngo Weinhold
29224df6592SIngo Weinhold // call the callback
29324df6592SIngo Weinhold locker.Unlock();
29424df6592SIngo Weinhold callback->DoDPC(this);
29524df6592SIngo Weinhold locker.Lock();
29624df6592SIngo Weinhold
29724df6592SIngo Weinhold fCallbackInProgress = NULL;
29824df6592SIngo Weinhold
29924df6592SIngo Weinhold // wake up threads waiting for the callback to be done
30024df6592SIngo Weinhold ConditionVariable* doneCondition = fCallbackDoneCondition;
30124df6592SIngo Weinhold fCallbackDoneCondition = NULL;
30224df6592SIngo Weinhold locker.Unlock();
30324df6592SIngo Weinhold if (doneCondition != NULL)
30424df6592SIngo Weinhold doneCondition->NotifyAll();
30524df6592SIngo Weinhold }
30624df6592SIngo Weinhold
30724df6592SIngo Weinhold return B_OK;
30824df6592SIngo Weinhold }
30924df6592SIngo Weinhold
31024df6592SIngo Weinhold
31124df6592SIngo Weinhold // #pragma mark - kernel private
31224df6592SIngo Weinhold
31324df6592SIngo Weinhold
31424df6592SIngo Weinhold void
dpc_init()31524df6592SIngo Weinhold dpc_init()
31624df6592SIngo Weinhold {
31724df6592SIngo Weinhold // create the default queues
31824df6592SIngo Weinhold new(&sNormalPriorityQueue) DPCQueue;
31924df6592SIngo Weinhold new(&sHighPriorityQueue) DPCQueue;
32024df6592SIngo Weinhold new(&sRealTimePriorityQueue) DPCQueue;
32124df6592SIngo Weinhold
32224df6592SIngo Weinhold if (sNormalPriorityQueue.Init("dpc: normal priority", NORMAL_PRIORITY,
32324df6592SIngo Weinhold DEFAULT_QUEUE_SLOT_COUNT) != B_OK
32424df6592SIngo Weinhold || sHighPriorityQueue.Init("dpc: high priority", HIGH_PRIORITY,
32524df6592SIngo Weinhold DEFAULT_QUEUE_SLOT_COUNT) != B_OK
32624df6592SIngo Weinhold || sRealTimePriorityQueue.Init("dpc: real-time priority",
32724df6592SIngo Weinhold REAL_TIME_PRIORITY, DEFAULT_QUEUE_SLOT_COUNT) != B_OK) {
32824df6592SIngo Weinhold panic("Failed to create default DPC queues!");
32924df6592SIngo Weinhold }
33024df6592SIngo Weinhold }
331