Author: bonefish Date: 2011-06-06 06:38:27 +0200 (Mon, 06 Jun 2011) New Revision: 41958 Changeset: https://dev.haiku-os.org/changeset/41958 Added: haiku/branches/developer/bonefish/signals/headers/private/kernel/DPC.h haiku/branches/developer/bonefish/signals/src/system/kernel/DPC.cpp Modified: haiku/branches/developer/bonefish/signals/src/system/kernel/Jamfile haiku/branches/developer/bonefish/signals/src/system/kernel/main.cpp Log: Implemented an in-kernel DPC service that doesn't have the shortcomings of the module (no queuing limit, callable with scheduler lock held, cancelable DPCs). Not tested yet. Added: haiku/branches/developer/bonefish/signals/headers/private/kernel/DPC.h =================================================================== --- haiku/branches/developer/bonefish/signals/headers/private/kernel/DPC.h (rev 0) +++ haiku/branches/developer/bonefish/signals/headers/private/kernel/DPC.h 2011-06-06 04:38:27 UTC (rev 41958) @@ -0,0 +1,118 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_DPC_H +#define _KERNEL_DPC_H + + +#include <sys/cdefs.h> + +#include <KernelExport.h> + +#include <util/DoublyLinkedList.h> + +#include <condition_variable.h> + + +namespace BKernel { + + +class DPCQueue; + + +class DPCCallback : public DoublyLinkedListLinkImpl<DPCCallback> { +public: + DPCCallback(); + virtual ~DPCCallback(); + + virtual void DoDPC(DPCQueue* queue) = 0; + + bool IsQueued() const + { return fInQueue != NULL; } + +private: + friend class DPCQueue; + +private: + DPCQueue* fInQueue; +}; + + +class FunctionDPCCallback : public DPCCallback { +public: + FunctionDPCCallback(DPCQueue* owner); + + void SetTo(void (*function)(void*), void* argument); + + virtual void DoDPC(DPCQueue* queue); + +private: + DPCQueue* fOwner; + void (*fFunction)(void*); + void* fArgument; +}; + + +class DPCQueue { +public: + DPCQueue(); + ~DPCQueue(); + + static DPCQueue* DefaultQueue(int priority); + + status_t Init(const char* name, int32 priority, + uint32 reservedSlots); + void Close(bool cancelPending); + + status_t Add(DPCCallback* callback, + bool schedulerLocked); + status_t Add(void (*function)(void*), void* argument, + bool schedulerLocked); + bool Cancel(DPCCallback* callback); + + thread_id Thread() const + { return fThreadID; } + +public: + // conceptually package private + void Recycle(FunctionDPCCallback* callback); + +private: + typedef DoublyLinkedList<DPCCallback> CallbackList; + +private: + static status_t _ThreadEntry(void* data); + status_t _Thread(); + + bool _IsClosed() const + { return fThreadID < 0; } + +private: + spinlock fLock; + thread_id fThreadID; + CallbackList fCallbacks; + CallbackList fUnusedFunctionCallbacks; + ConditionVariable fPendingCallbacksCondition; + DPCCallback* fCallbackInProgress; + ConditionVariable* fCallbackDoneCondition; +}; + + +} // namespace BKernel + + +using BKernel::DPCCallback; +using BKernel::DPCQueue; +using BKernel::FunctionDPCCallback; + + +__BEGIN_DECLS + +void dpc_init(); +void dpc_init_post_scheduler(); + +__END_DECLS + + +#endif // _KERNEL_DPC_H Added: haiku/branches/developer/bonefish/signals/src/system/kernel/DPC.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/kernel/DPC.cpp (rev 0) +++ haiku/branches/developer/bonefish/signals/src/system/kernel/DPC.cpp 2011-06-06 04:38:27 UTC (rev 41958) @@ -0,0 +1,339 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ + + +#include <DPC.h> + +#include <util/AutoLock.h> + +#include <kernel.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; + + if (!gKernelStartup) + 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 (callback != fCallbackInProgress) { + // The callback is not currently being executed. Remove it, if queued. + if (callback->fInQueue != this) + return false; + + fCallbacks.Remove(callback); + return true; + } + + // 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!"); + } +} + + +void +dpc_init_post_scheduler() +{ + // start the threads of the default queues + resume_thread(sNormalPriorityQueue.Thread()); + resume_thread(sHighPriorityQueue.Thread()); + resume_thread(sRealTimePriorityQueue.Thread()); +} Modified: haiku/branches/developer/bonefish/signals/src/system/kernel/Jamfile =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/kernel/Jamfile 2011-06-06 03:47:01 UTC (rev 41957) +++ haiku/branches/developer/bonefish/signals/src/system/kernel/Jamfile 2011-06-06 04:38:27 UTC (rev 41958) @@ -28,6 +28,7 @@ commpage.cpp condition_variable.cpp cpu.cpp + DPC.cpp elf.cpp heap.cpp image.cpp Modified: haiku/branches/developer/bonefish/signals/src/system/kernel/main.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/kernel/main.cpp 2011-06-06 03:47:01 UTC (rev 41957) +++ haiku/branches/developer/bonefish/signals/src/system/kernel/main.cpp 2011-06-06 04:38:27 UTC (rev 41958) @@ -23,6 +23,7 @@ #include <condition_variable.h> #include <cpu.h> #include <debug.h> +#include <DPC.h> #include <elf.h> #include <fs/devfs.h> #include <fs/KPath.h> @@ -180,6 +181,8 @@ TRACE("init VM threads\n"); vm_init_post_thread(&sKernelArgs); low_resource_manager_init_post_thread(); + TRACE("init DPC\n"); + dpc_init(); TRACE("init VFS\n"); vfs_init(&sKernelArgs); #if ENABLE_SWAP_SUPPORT @@ -262,6 +265,8 @@ boot_splash_init(sKernelArgs.boot_splash); + dpc_init_post_scheduler(); + commpage_init_post_cpus(); TRACE("init ports\n");