xref: /haiku/src/system/kernel/DPC.cpp (revision e925863d958656d34cca5663b27190046dc26eac)
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