24df65921b
* Reorganized the kernel locking related to threads and teams. * We now discriminate correctly between process and thread signals. Signal handlers have been moved to teams. Fixes #5679. * Implemented real-time signal support, including signal queuing, SA_SIGINFO support, sigqueue(), sigwaitinfo(), sigtimedwait(), waitid(), and the addition of the real-time signal range. Closes #1935 and #2695. * Gave SIGBUS a separate signal number. Fixes #6704. * Implemented <time.h> clock and timer support, and fixed/completed alarm() and [set]itimer(). Closes #5682. * Implemented support for thread cancellation. Closes #5686. * Moved send_signal() from <signal.h> to <OS.h>. Fixes #7554. * Lots over smaller more or less related changes. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@42116 a95241bf-73f2-0310-859d-f6bbb57e9c96
328 lines
6.2 KiB
C++
328 lines
6.2 KiB
C++
/*
|
|
* Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
|
|
* Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
|
|
#include <DPC.h>
|
|
|
|
#include <util/AutoLock.h>
|
|
|
|
|
|
#define NORMAL_PRIORITY B_NORMAL_PRIORITY
|
|
#define HIGH_PRIORITY B_URGENT_DISPLAY_PRIORITY
|
|
#define REAL_TIME_PRIORITY B_FIRST_REAL_TIME_PRIORITY
|
|
|
|
#define DEFAULT_QUEUE_SLOT_COUNT 64
|
|
|
|
|
|
static DPCQueue sNormalPriorityQueue;
|
|
static DPCQueue sHighPriorityQueue;
|
|
static DPCQueue sRealTimePriorityQueue;
|
|
|
|
|
|
// #pragma mark - FunctionDPCCallback
|
|
|
|
|
|
FunctionDPCCallback::FunctionDPCCallback(DPCQueue* owner)
|
|
:
|
|
fOwner(owner)
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
FunctionDPCCallback::SetTo(void (*function)(void*), void* argument)
|
|
{
|
|
fFunction = function;
|
|
fArgument = argument;
|
|
}
|
|
|
|
|
|
void
|
|
FunctionDPCCallback::DoDPC(DPCQueue* queue)
|
|
{
|
|
fFunction(fArgument);
|
|
|
|
if (fOwner != NULL)
|
|
fOwner->Recycle(this);
|
|
}
|
|
|
|
|
|
// #pragma mark - DPCCallback
|
|
|
|
|
|
DPCCallback::DPCCallback()
|
|
:
|
|
fInQueue(NULL)
|
|
{
|
|
}
|
|
|
|
|
|
DPCCallback::~DPCCallback()
|
|
{
|
|
}
|
|
|
|
|
|
// #pragma mark - DPCQueue
|
|
|
|
|
|
DPCQueue::DPCQueue()
|
|
:
|
|
fThreadID(-1),
|
|
fCallbackInProgress(NULL),
|
|
fCallbackDoneCondition(NULL)
|
|
{
|
|
B_INITIALIZE_SPINLOCK(&fLock);
|
|
|
|
fPendingCallbacksCondition.Init(this, "dpc queue");
|
|
}
|
|
|
|
|
|
DPCQueue::~DPCQueue()
|
|
{
|
|
// close, if not closed yet
|
|
{
|
|
InterruptsSpinLocker locker(fLock);
|
|
if (!_IsClosed()) {
|
|
locker.Unlock();
|
|
Close(false);
|
|
}
|
|
}
|
|
|
|
// delete function callbacks
|
|
while (DPCCallback* callback = fUnusedFunctionCallbacks.RemoveHead())
|
|
delete callback;
|
|
}
|
|
|
|
|
|
/*static*/ DPCQueue*
|
|
DPCQueue::DefaultQueue(int priority)
|
|
{
|
|
if (priority <= NORMAL_PRIORITY)
|
|
return &sNormalPriorityQueue;
|
|
|
|
if (priority <= HIGH_PRIORITY)
|
|
return &sHighPriorityQueue;
|
|
|
|
return &sRealTimePriorityQueue;
|
|
}
|
|
|
|
|
|
status_t
|
|
DPCQueue::Init(const char* name, int32 priority, uint32 reservedSlots)
|
|
{
|
|
// create function callbacks
|
|
for (uint32 i = 0; i < reservedSlots; i++) {
|
|
FunctionDPCCallback* callback
|
|
= new(std::nothrow) FunctionDPCCallback(this);
|
|
if (callback == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
fUnusedFunctionCallbacks.Add(callback);
|
|
}
|
|
|
|
// spawn the thread
|
|
fThreadID = spawn_kernel_thread(&_ThreadEntry, name, priority, this);
|
|
if (fThreadID < 0)
|
|
return fThreadID;
|
|
|
|
resume_thread(fThreadID);
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
void
|
|
DPCQueue::Close(bool cancelPending)
|
|
{
|
|
InterruptsSpinLocker locker(fLock);
|
|
|
|
if (_IsClosed())
|
|
return;
|
|
|
|
// If requested, dequeue all pending callbacks
|
|
if (cancelPending)
|
|
fCallbacks.MakeEmpty();
|
|
|
|
// mark the queue closed
|
|
thread_id thread = fThreadID;
|
|
fThreadID = -1;
|
|
|
|
locker.Unlock();
|
|
|
|
// wake up the thread and wait for it
|
|
fPendingCallbacksCondition.NotifyAll();
|
|
wait_for_thread(thread, NULL);
|
|
}
|
|
|
|
|
|
status_t
|
|
DPCQueue::Add(DPCCallback* callback, bool schedulerLocked)
|
|
{
|
|
// queue the callback, if the queue isn't closed already
|
|
InterruptsSpinLocker locker(fLock);
|
|
|
|
if (_IsClosed())
|
|
return B_NOT_INITIALIZED;
|
|
|
|
bool wasEmpty = fCallbacks.IsEmpty();
|
|
fCallbacks.Add(callback);
|
|
callback->fInQueue = this;
|
|
|
|
locker.Unlock();
|
|
|
|
// notify the condition variable, if necessary
|
|
if (wasEmpty)
|
|
fPendingCallbacksCondition.NotifyAll(schedulerLocked);
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
DPCQueue::Add(void (*function)(void*), void* argument, bool schedulerLocked)
|
|
{
|
|
if (function == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
// get a free callback
|
|
InterruptsSpinLocker locker(fLock);
|
|
|
|
DPCCallback* callback = fUnusedFunctionCallbacks.RemoveHead();
|
|
if (callback == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
locker.Unlock();
|
|
|
|
// init the callback
|
|
FunctionDPCCallback* functionCallback
|
|
= static_cast<FunctionDPCCallback*>(callback);
|
|
functionCallback->SetTo(function, argument);
|
|
|
|
// add it
|
|
status_t error = Add(functionCallback, schedulerLocked);
|
|
if (error != B_OK)
|
|
Recycle(functionCallback);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
bool
|
|
DPCQueue::Cancel(DPCCallback* callback)
|
|
{
|
|
InterruptsSpinLocker locker(fLock);
|
|
|
|
// If the callback is queued, remove it.
|
|
if (callback->fInQueue == this) {
|
|
fCallbacks.Remove(callback);
|
|
return true;
|
|
}
|
|
|
|
// The callback is not queued. If it isn't in progress, we're done, too.
|
|
if (callback != fCallbackInProgress)
|
|
return false;
|
|
|
|
// The callback is currently being executed. We need to wait for it to be
|
|
// done.
|
|
|
|
// Set the respective condition, if not set yet. For the unlikely case that
|
|
// there are multiple threads trying to cancel the callback at the same
|
|
// time, the condition variable of the first thread will be used.
|
|
ConditionVariable condition;
|
|
if (fCallbackDoneCondition == NULL)
|
|
fCallbackDoneCondition = &condition;
|
|
|
|
// add our wait entry
|
|
ConditionVariableEntry waitEntry;
|
|
fCallbackDoneCondition->Add(&waitEntry);
|
|
|
|
// wait
|
|
locker.Unlock();
|
|
waitEntry.Wait();
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void
|
|
DPCQueue::Recycle(FunctionDPCCallback* callback)
|
|
{
|
|
InterruptsSpinLocker locker(fLock);
|
|
fUnusedFunctionCallbacks.Insert(callback, false);
|
|
}
|
|
|
|
|
|
/*static*/ status_t
|
|
DPCQueue::_ThreadEntry(void* data)
|
|
{
|
|
return ((DPCQueue*)data)->_Thread();
|
|
}
|
|
|
|
|
|
status_t
|
|
DPCQueue::_Thread()
|
|
{
|
|
while (true) {
|
|
InterruptsSpinLocker locker(fLock);
|
|
|
|
// get the next pending callback
|
|
DPCCallback* callback = fCallbacks.RemoveHead();
|
|
if (callback == NULL) {
|
|
// nothing is pending -- wait unless the queue is already closed
|
|
if (_IsClosed())
|
|
break;
|
|
|
|
ConditionVariableEntry waitEntry;
|
|
fPendingCallbacksCondition.Add(&waitEntry);
|
|
|
|
locker.Unlock();
|
|
waitEntry.Wait();
|
|
|
|
continue;
|
|
}
|
|
|
|
callback->fInQueue = NULL;
|
|
fCallbackInProgress = callback;
|
|
|
|
// call the callback
|
|
locker.Unlock();
|
|
callback->DoDPC(this);
|
|
locker.Lock();
|
|
|
|
fCallbackInProgress = NULL;
|
|
|
|
// wake up threads waiting for the callback to be done
|
|
ConditionVariable* doneCondition = fCallbackDoneCondition;
|
|
fCallbackDoneCondition = NULL;
|
|
locker.Unlock();
|
|
if (doneCondition != NULL)
|
|
doneCondition->NotifyAll();
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
// #pragma mark - kernel private
|
|
|
|
|
|
void
|
|
dpc_init()
|
|
{
|
|
// create the default queues
|
|
new(&sNormalPriorityQueue) DPCQueue;
|
|
new(&sHighPriorityQueue) DPCQueue;
|
|
new(&sRealTimePriorityQueue) DPCQueue;
|
|
|
|
if (sNormalPriorityQueue.Init("dpc: normal priority", NORMAL_PRIORITY,
|
|
DEFAULT_QUEUE_SLOT_COUNT) != B_OK
|
|
|| sHighPriorityQueue.Init("dpc: high priority", HIGH_PRIORITY,
|
|
DEFAULT_QUEUE_SLOT_COUNT) != B_OK
|
|
|| sRealTimePriorityQueue.Init("dpc: real-time priority",
|
|
REAL_TIME_PRIORITY, DEFAULT_QUEUE_SLOT_COUNT) != B_OK) {
|
|
panic("Failed to create default DPC queues!");
|
|
}
|
|
}
|