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:
parent
314f04b896
commit
20cccda731
7 changed files with 182 additions and 1 deletions
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
79
Kernel/WorkQueue.cpp
Normal 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
94
Kernel/WorkQueue.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue