[haiku-commits] r41958 - in haiku/branches/developer/bonefish/signals: headers/private/kernel src/system/kernel

  • From: ingo_weinhold@xxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Mon, 6 Jun 2011 06:38:27 +0200 (CEST)

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");


Other related posts:

  • » [haiku-commits] r41958 - in haiku/branches/developer/bonefish/signals: headers/private/kernel src/system/kernel - ingo_weinhold