1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-24 21:17:42 +00:00

Kernel: Add simplistic work queues

We can't use deferred functions for anything that may require preemption,
such as copying from/to user or accessing the disk. For those purposes
we should use a work queue, which is essentially a kernel thread that
may be preempted or blocked.
This commit is contained in:
Tom 2021-02-05 23:36:38 -07:00 committed by Andreas Kling
parent 314f04b896
commit 20cccda731
7 changed files with 182 additions and 1 deletions

View file

@ -228,6 +228,7 @@ set(KERNEL_SOURCES
VM/Space.cpp VM/Space.cpp
VM/VMObject.cpp VM/VMObject.cpp
WaitQueue.cpp WaitQueue.cpp
WorkQueue.cpp
init.cpp init.cpp
kprintf.cpp kprintf.cpp
) )

View file

@ -37,6 +37,8 @@ namespace Kernel {
class Device; class Device;
extern WorkQueue* g_io_work;
class AsyncDeviceRequest : public RefCounted<AsyncDeviceRequest> { class AsyncDeviceRequest : public RefCounted<AsyncDeviceRequest> {
AK_MAKE_NONCOPYABLE(AsyncDeviceRequest); AK_MAKE_NONCOPYABLE(AsyncDeviceRequest);
AK_MAKE_NONMOVABLE(AsyncDeviceRequest); AK_MAKE_NONMOVABLE(AsyncDeviceRequest);

View file

@ -76,6 +76,7 @@ class UserOrKernelBuffer;
class VFS; class VFS;
class VMObject; class VMObject;
class WaitQueue; class WaitQueue;
class WorkQueue;
template<typename T> template<typename T>
class KResultOr; class KResultOr;

View file

@ -35,6 +35,7 @@
#include <Kernel/Storage/IDEController.h> #include <Kernel/Storage/IDEController.h>
#include <Kernel/Storage/PATADiskDevice.h> #include <Kernel/Storage/PATADiskDevice.h>
#include <Kernel/VM/MemoryManager.h> #include <Kernel/VM/MemoryManager.h>
#include <Kernel/WorkQueue.h>
namespace Kernel { namespace Kernel {
@ -122,7 +123,7 @@ void IDEChannel::complete_current_request(AsyncDeviceRequest::RequestResult resu
// This is important so that we can safely write the buffer back, // This is important so that we can safely write the buffer back,
// which could cause page faults. Note that this may be called immediately // which could cause page faults. Note that this may be called immediately
// before Processor::deferred_call_queue returns! // before Processor::deferred_call_queue returns!
Processor::deferred_call_queue([this, result]() { g_io_work->queue([this, result]() {
dbgln_if(PATA_DEBUG, "IDEChannel::complete_current_request result: {}", (int)result); dbgln_if(PATA_DEBUG, "IDEChannel::complete_current_request result: {}", (int)result);
ScopedSpinLock lock(m_request_lock); ScopedSpinLock lock(m_request_lock);
VERIFY(m_current_request); VERIFY(m_current_request);

79
Kernel/WorkQueue.cpp Normal file
View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2021, The SerenityOS developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <Kernel/Process.h>
#include <Kernel/SpinLock.h>
#include <Kernel/WaitQueue.h>
#include <Kernel/WorkQueue.h>
namespace Kernel {
WorkQueue* g_io_work;
void WorkQueue::initialize()
{
g_io_work = new WorkQueue("IO WorkQueue");
}
WorkQueue::WorkQueue(const char* name)
: m_name(name)
{
RefPtr<Thread> thread;
Process::create_kernel_process(thread, name, [this] {
for (;;) {
WorkItem* item;
bool have_more;
{
ScopedSpinLock lock(m_lock);
item = m_items.take_first();
have_more = !m_items.is_empty();
}
if (item) {
item->function(item->data);
if (item->free_data)
item->free_data(item->data);
delete item;
if (have_more)
continue;
}
[[maybe_unused]] auto result = m_wait_queue.wait_on({});
}
});
// If we can't create the thread we're in trouble...
m_thread = thread.release_nonnull();
}
void WorkQueue::do_queue(WorkItem* item)
{
{
ScopedSpinLock lock(m_lock);
m_items.append(*item);
}
m_wait_queue.wake_one();
}
}

94
Kernel/WorkQueue.h Normal file
View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2021, The SerenityOS developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <AK/IntrusiveList.h>
#include <Kernel/Forward.h>
namespace Kernel {
extern WorkQueue* g_io_work;
class WorkQueue {
AK_MAKE_NONCOPYABLE(WorkQueue);
AK_MAKE_NONMOVABLE(WorkQueue);
public:
static void initialize();
WorkQueue(const char*);
void queue(void (*function)(void*), void* data = nullptr, void (*free_data)(void*) = nullptr)
{
auto* item = new WorkItem; // TODO: use a pool
item->function = function;
item->data = data;
item->free_data = free_data;
do_queue(item);
}
template<typename Function>
void queue(Function function)
{
auto* item = new WorkItem; // TODO: use a pool
item->function = [](void* f) {
(*reinterpret_cast<Function*>(f))();
};
if constexpr (sizeof(Function) <= sizeof(item->inline_data)) {
item->data = new (item->inline_data) Function(move(function));
item->free_data = [](void* f) {
reinterpret_cast<Function*>(f)->~Function();
};
} else {
item->data = new Function(move(function));
item->free_data = [](void* f) {
delete reinterpret_cast<Function*>(f);
};
}
do_queue(item);
}
private:
struct WorkItem {
IntrusiveListNode m_node;
void (*function)(void*);
void* data;
void (*free_data)(void*);
u8 inline_data[4 * sizeof(void*)];
};
void do_queue(WorkItem*);
const char* const m_name;
RefPtr<Thread> m_thread;
WaitQueue m_wait_queue;
IntrusiveList<WorkItem, &WorkItem::m_node> m_items;
SpinLock<u8> m_lock;
};
}

View file

@ -72,6 +72,7 @@
#include <Kernel/Tasks/SyncTask.h> #include <Kernel/Tasks/SyncTask.h>
#include <Kernel/Time/TimeManagement.h> #include <Kernel/Time/TimeManagement.h>
#include <Kernel/VM/MemoryManager.h> #include <Kernel/VM/MemoryManager.h>
#include <Kernel/WorkQueue.h>
#include <Kernel/kstdio.h> #include <Kernel/kstdio.h>
// Defined in the linker script // Defined in the linker script
@ -189,6 +190,8 @@ extern "C" UNMAP_AFTER_INIT [[noreturn]] void init()
Process::initialize(); Process::initialize();
Scheduler::initialize(); Scheduler::initialize();
WorkQueue::initialize();
{ {
RefPtr<Thread> init_stage2_thread; RefPtr<Thread> init_stage2_thread;
Process::create_kernel_process(init_stage2_thread, "init_stage2", init_stage2, nullptr); Process::create_kernel_process(init_stage2_thread, "init_stage2", init_stage2, nullptr);