mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-11 18:20:43 +09:00
Kernel: Allow passing a thread argument for new kernel threads
This adds the ability to pass a pointer to kernel thread/process. Also add the ability to use a closure as thread function, which allows passing information to a kernel thread more easily.
This commit is contained in:
parent
6cb640eeba
commit
6a620562cc
Notes:
sideshowbarker
2024-07-19 01:08:54 +09:00
Author: https://github.com/tomuta
Commit: 6a620562cc
Pull-request: https://github.com/SerenityOS/serenity/pull/4113
11 changed files with 68 additions and 24 deletions
|
@ -63,6 +63,7 @@ static GenericInterruptHandler* s_interrupt_handler[GENERIC_INTERRUPT_HANDLERS_C
|
||||||
extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread);
|
extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread);
|
||||||
extern "C" void context_first_init(Thread* from_thread, Thread* to_thread, TrapFrame* trap);
|
extern "C" void context_first_init(Thread* from_thread, Thread* to_thread, TrapFrame* trap);
|
||||||
extern "C" u32 do_init_context(Thread* thread, u32 flags);
|
extern "C" u32 do_init_context(Thread* thread, u32 flags);
|
||||||
|
extern "C" void exit_kernel_thread(void);
|
||||||
extern "C" void pre_init_finished(void);
|
extern "C" void pre_init_finished(void);
|
||||||
extern "C" void post_init_finished(void);
|
extern "C" void post_init_finished(void);
|
||||||
extern "C" void handle_interrupt(TrapFrame*);
|
extern "C" void handle_interrupt(TrapFrame*);
|
||||||
|
@ -1457,6 +1458,11 @@ asm(
|
||||||
" jmp common_trap_exit \n"
|
" jmp common_trap_exit \n"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void exit_kernel_thread(void)
|
||||||
|
{
|
||||||
|
Thread::current()->exit();
|
||||||
|
}
|
||||||
|
|
||||||
u32 Processor::init_context(Thread& thread, bool leave_crit)
|
u32 Processor::init_context(Thread& thread, bool leave_crit)
|
||||||
{
|
{
|
||||||
ASSERT(is_kernel_mode());
|
ASSERT(is_kernel_mode());
|
||||||
|
@ -1468,7 +1474,7 @@ u32 Processor::init_context(Thread& thread, bool leave_crit)
|
||||||
ASSERT(in_critical() == 1);
|
ASSERT(in_critical() == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32 kernel_stack_top = thread.kernel_stack_top();
|
u32 kernel_stack_top = thread.kernel_stack_top();
|
||||||
u32 stack_top = kernel_stack_top;
|
u32 stack_top = kernel_stack_top;
|
||||||
|
|
||||||
// TODO: handle NT?
|
// TODO: handle NT?
|
||||||
|
@ -1482,13 +1488,20 @@ u32 Processor::init_context(Thread& thread, bool leave_crit)
|
||||||
// userspace_esp and userspace_ss are not popped off by iret
|
// userspace_esp and userspace_ss are not popped off by iret
|
||||||
// unless we're switching back to user mode
|
// unless we're switching back to user mode
|
||||||
stack_top -= sizeof(RegisterState) - 2 * sizeof(u32);
|
stack_top -= sizeof(RegisterState) - 2 * sizeof(u32);
|
||||||
|
|
||||||
|
// For kernel threads we'll push the thread function argument
|
||||||
|
// which should be in tss.esp and exit_kernel_thread as return
|
||||||
|
// address.
|
||||||
|
stack_top -= 2 * sizeof(u32);
|
||||||
|
*reinterpret_cast<u32*>(kernel_stack_top - 2 * sizeof(u32)) = tss.esp;
|
||||||
|
*reinterpret_cast<u32*>(kernel_stack_top - 3 * sizeof(u32)) = FlatPtr(&exit_kernel_thread);
|
||||||
} else {
|
} else {
|
||||||
stack_top -= sizeof(RegisterState);
|
stack_top -= sizeof(RegisterState);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we want to end up 16-byte aligned, %esp + 4 should be aligned
|
// we want to end up 16-byte aligned, %esp + 4 should be aligned
|
||||||
stack_top -= sizeof(u32);
|
stack_top -= sizeof(u32);
|
||||||
*reinterpret_cast<u32*>(kernel_stack_top - 4) = 0;
|
*reinterpret_cast<u32*>(kernel_stack_top - sizeof(u32)) = 0;
|
||||||
|
|
||||||
// set up the stack so that after returning from thread_context_first_enter()
|
// set up the stack so that after returning from thread_context_first_enter()
|
||||||
// we will end up either in kernel mode or user mode, depending on how the thread is set up
|
// we will end up either in kernel mode or user mode, depending on how the thread is set up
|
||||||
|
|
|
@ -57,15 +57,15 @@ static void handle_icmp(const EthernetFrameHeader&, const IPv4Packet&, const tim
|
||||||
static void handle_udp(const IPv4Packet&, const timeval& packet_timestamp);
|
static void handle_udp(const IPv4Packet&, const timeval& packet_timestamp);
|
||||||
static void handle_tcp(const IPv4Packet&, const timeval& packet_timestamp);
|
static void handle_tcp(const IPv4Packet&, const timeval& packet_timestamp);
|
||||||
|
|
||||||
[[noreturn]] static void NetworkTask_main();
|
[[noreturn]] static void NetworkTask_main(void*);
|
||||||
|
|
||||||
void NetworkTask::spawn()
|
void NetworkTask::spawn()
|
||||||
{
|
{
|
||||||
RefPtr<Thread> thread;
|
RefPtr<Thread> thread;
|
||||||
Process::create_kernel_process(thread, "NetworkTask", NetworkTask_main);
|
Process::create_kernel_process(thread, "NetworkTask", NetworkTask_main, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkTask_main()
|
void NetworkTask_main(void*)
|
||||||
{
|
{
|
||||||
WaitQueue packet_wait_queue;
|
WaitQueue packet_wait_queue;
|
||||||
u8 octet = 15;
|
u8 octet = 15;
|
||||||
|
|
|
@ -311,10 +311,11 @@ RefPtr<Process> Process::create_user_process(RefPtr<Thread>& first_thread, const
|
||||||
return process;
|
return process;
|
||||||
}
|
}
|
||||||
|
|
||||||
NonnullRefPtr<Process> Process::create_kernel_process(RefPtr<Thread>& first_thread, String&& name, void (*e)(), u32 affinity)
|
NonnullRefPtr<Process> Process::create_kernel_process(RefPtr<Thread>& first_thread, String&& name, void (*entry)(void*), void *entry_data, u32 affinity)
|
||||||
{
|
{
|
||||||
auto process = adopt(*new Process(first_thread, move(name), (uid_t)0, (gid_t)0, ProcessID(0), true));
|
auto process = adopt(*new Process(first_thread, move(name), (uid_t)0, (gid_t)0, ProcessID(0), true));
|
||||||
first_thread->tss().eip = (FlatPtr)e;
|
first_thread->tss().eip = (FlatPtr)entry;
|
||||||
|
first_thread->tss().esp = FlatPtr(entry_data); // entry function argument is expected to be in tss.esp
|
||||||
|
|
||||||
if (process->pid() != 0) {
|
if (process->pid() != 0) {
|
||||||
ScopedSpinLock lock(g_processes_lock);
|
ScopedSpinLock lock(g_processes_lock);
|
||||||
|
@ -765,7 +766,7 @@ KResult Process::send_signal(u8 signal, Process* sender)
|
||||||
return KResult(-ESRCH);
|
return KResult(-ESRCH);
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<Thread> Process::create_kernel_thread(void (*entry)(), u32 priority, const String& name, u32 affinity, bool joinable)
|
RefPtr<Thread> Process::create_kernel_thread(void (*entry)(void*), void* entry_data, u32 priority, const String& name, u32 affinity, bool joinable)
|
||||||
{
|
{
|
||||||
ASSERT((priority >= THREAD_PRIORITY_MIN) && (priority <= THREAD_PRIORITY_MAX));
|
ASSERT((priority >= THREAD_PRIORITY_MIN) && (priority <= THREAD_PRIORITY_MAX));
|
||||||
|
|
||||||
|
@ -781,6 +782,7 @@ RefPtr<Thread> Process::create_kernel_thread(void (*entry)(), u32 priority, cons
|
||||||
|
|
||||||
auto& tss = thread->tss();
|
auto& tss = thread->tss();
|
||||||
tss.eip = (FlatPtr)entry;
|
tss.eip = (FlatPtr)entry;
|
||||||
|
tss.esp = FlatPtr(entry_data); // entry function argument is expected to be in tss.esp
|
||||||
|
|
||||||
ScopedSpinLock lock(g_scheduler_lock);
|
ScopedSpinLock lock(g_scheduler_lock);
|
||||||
thread->set_state(Thread::State::Runnable);
|
thread->set_state(Thread::State::Runnable);
|
||||||
|
|
|
@ -126,14 +126,35 @@ public:
|
||||||
return current_thread ? ¤t_thread->process() : nullptr;
|
return current_thread ? ¤t_thread->process() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static NonnullRefPtr<Process> create_kernel_process(RefPtr<Thread>& first_thread, String&& name, void (*entry)(), u32 affinity = THREAD_AFFINITY_DEFAULT);
|
template<typename EntryFunction>
|
||||||
|
static NonnullRefPtr<Process> create_kernel_process(RefPtr<Thread>& first_thread, String&& name, EntryFunction entry, u32 affinity = THREAD_AFFINITY_DEFAULT)
|
||||||
|
{
|
||||||
|
auto* entry_func = new EntryFunction(move(entry));
|
||||||
|
return create_kernel_process(first_thread, move(name), [](void* data) {
|
||||||
|
EntryFunction* func = reinterpret_cast<EntryFunction*>(data);
|
||||||
|
(*func)();
|
||||||
|
delete func;
|
||||||
|
}, entry_func, affinity);
|
||||||
|
}
|
||||||
|
|
||||||
|
static NonnullRefPtr<Process> create_kernel_process(RefPtr<Thread>& first_thread, String&& name, void (*entry)(void*), void* entry_data = nullptr, u32 affinity = THREAD_AFFINITY_DEFAULT);
|
||||||
static RefPtr<Process> create_user_process(RefPtr<Thread>& first_thread, const String& path, uid_t, gid_t, ProcessID ppid, int& error, Vector<String>&& arguments = Vector<String>(), Vector<String>&& environment = Vector<String>(), TTY* = nullptr);
|
static RefPtr<Process> create_user_process(RefPtr<Thread>& first_thread, const String& path, uid_t, gid_t, ProcessID ppid, int& error, Vector<String>&& arguments = Vector<String>(), Vector<String>&& environment = Vector<String>(), TTY* = nullptr);
|
||||||
~Process();
|
~Process();
|
||||||
|
|
||||||
static Vector<ProcessID> all_pids();
|
static Vector<ProcessID> all_pids();
|
||||||
static AK::NonnullRefPtrVector<Process> all_processes();
|
static AK::NonnullRefPtrVector<Process> all_processes();
|
||||||
|
|
||||||
RefPtr<Thread> create_kernel_thread(void (*entry)(), u32 priority, const String& name, u32 affinity = THREAD_AFFINITY_DEFAULT, bool joinable = true);
|
template<typename EntryFunction>
|
||||||
|
RefPtr<Thread> create_kernel_thread(EntryFunction entry, u32 priority, const String& name, u32 affinity = THREAD_AFFINITY_DEFAULT, bool joinable = true)
|
||||||
|
{
|
||||||
|
auto* entry_func = new EntryFunction(move(entry));
|
||||||
|
return create_kernel_thread([](void* data) {
|
||||||
|
EntryFunction* func = reinterpret_cast<EntryFunction*>(data);
|
||||||
|
(*func)();
|
||||||
|
delete func;
|
||||||
|
}, priority, name, affinity, joinable);
|
||||||
|
}
|
||||||
|
RefPtr<Thread> create_kernel_thread(void (*entry)(void*), void* entry_data, u32 priority, const String& name, u32 affinity = THREAD_AFFINITY_DEFAULT, bool joinable = true);
|
||||||
|
|
||||||
bool is_profiling() const { return m_profiling; }
|
bool is_profiling() const { return m_profiling; }
|
||||||
void set_profiling(bool profiling) { m_profiling = profiling; }
|
void set_profiling(bool profiling) { m_profiling = profiling; }
|
||||||
|
@ -689,6 +710,7 @@ inline bool InodeMetadata::may_write(const Process& process) const
|
||||||
return may_write(process.euid(), process.egid(), process.extra_gids());
|
return may_write(process.euid(), process.egid(), process.extra_gids());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline bool InodeMetadata::may_execute(const Process& process) const
|
inline bool InodeMetadata::may_execute(const Process& process) const
|
||||||
{
|
{
|
||||||
return may_execute(process.euid(), process.egid(), process.extra_gids());
|
return may_execute(process.euid(), process.egid(), process.extra_gids());
|
||||||
|
|
|
@ -754,7 +754,7 @@ void Scheduler::initialize()
|
||||||
g_finalizer_wait_queue = new WaitQueue;
|
g_finalizer_wait_queue = new WaitQueue;
|
||||||
|
|
||||||
g_finalizer_has_work.store(false, AK::MemoryOrder::memory_order_release);
|
g_finalizer_has_work.store(false, AK::MemoryOrder::memory_order_release);
|
||||||
s_colonel_process = &Process::create_kernel_process(idle_thread, "colonel", idle_loop, 1).leak_ref();
|
s_colonel_process = &Process::create_kernel_process(idle_thread, "colonel", idle_loop, nullptr, 1).leak_ref();
|
||||||
ASSERT(s_colonel_process);
|
ASSERT(s_colonel_process);
|
||||||
ASSERT(idle_thread);
|
ASSERT(idle_thread);
|
||||||
idle_thread->set_priority(THREAD_PRIORITY_MIN);
|
idle_thread->set_priority(THREAD_PRIORITY_MIN);
|
||||||
|
@ -776,7 +776,7 @@ Thread* Scheduler::create_ap_idle_thread(u32 cpu)
|
||||||
ASSERT(Processor::current().id() == 0);
|
ASSERT(Processor::current().id() == 0);
|
||||||
|
|
||||||
ASSERT(s_colonel_process);
|
ASSERT(s_colonel_process);
|
||||||
Thread* idle_thread = s_colonel_process->create_kernel_thread(idle_loop, THREAD_PRIORITY_MIN, String::format("idle thread #%u", cpu), 1 << cpu, false);
|
Thread* idle_thread = s_colonel_process->create_kernel_thread(idle_loop, nullptr, THREAD_PRIORITY_MIN, String::format("idle thread #%u", cpu), 1 << cpu, false);
|
||||||
ASSERT(idle_thread);
|
ASSERT(idle_thread);
|
||||||
return idle_thread;
|
return idle_thread;
|
||||||
}
|
}
|
||||||
|
@ -832,7 +832,7 @@ void Scheduler::notify_finalizer()
|
||||||
g_finalizer_wait_queue->wake_all();
|
g_finalizer_wait_queue->wake_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::idle_loop()
|
void Scheduler::idle_loop(void*)
|
||||||
{
|
{
|
||||||
dbg() << "Scheduler[" << Processor::current().id() << "]: idle loop running";
|
dbg() << "Scheduler[" << Processor::current().id() << "]: idle loop running";
|
||||||
ASSERT(are_interrupts_enabled());
|
ASSERT(are_interrupts_enabled());
|
||||||
|
|
|
@ -66,7 +66,7 @@ public:
|
||||||
static void prepare_for_idle_loop();
|
static void prepare_for_idle_loop();
|
||||||
static Process* colonel();
|
static Process* colonel();
|
||||||
static void beep();
|
static void beep();
|
||||||
static void idle_loop();
|
static void idle_loop(void*);
|
||||||
static void invoke_async();
|
static void invoke_async();
|
||||||
static void notify_finalizer();
|
static void notify_finalizer();
|
||||||
|
|
||||||
|
|
|
@ -93,11 +93,7 @@ void Process::sys$exit_thread(Userspace<void*> exit_value)
|
||||||
{
|
{
|
||||||
REQUIRE_PROMISE(thread);
|
REQUIRE_PROMISE(thread);
|
||||||
cli();
|
cli();
|
||||||
auto current_thread = Thread::current();
|
Thread::current()->exit(reinterpret_cast<void*>(exit_value.ptr()));
|
||||||
current_thread->m_exit_value = reinterpret_cast<void*>(exit_value.ptr());
|
|
||||||
current_thread->set_should_die();
|
|
||||||
big_lock().force_unlock_if_locked();
|
|
||||||
current_thread->die_if_needed();
|
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace Kernel {
|
||||||
void FinalizerTask::spawn()
|
void FinalizerTask::spawn()
|
||||||
{
|
{
|
||||||
RefPtr<Thread> finalizer_thread;
|
RefPtr<Thread> finalizer_thread;
|
||||||
Process::create_kernel_process(finalizer_thread, "FinalizerTask", [] {
|
Process::create_kernel_process(finalizer_thread, "FinalizerTask", [](void*) {
|
||||||
Thread::current()->set_priority(THREAD_PRIORITY_LOW);
|
Thread::current()->set_priority(THREAD_PRIORITY_LOW);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
Thread::current()->wait_on(*g_finalizer_wait_queue, "FinalizerTask");
|
Thread::current()->wait_on(*g_finalizer_wait_queue, "FinalizerTask");
|
||||||
|
@ -41,7 +41,7 @@ void FinalizerTask::spawn()
|
||||||
if (g_finalizer_has_work.compare_exchange_strong(expected, false, AK::MemoryOrder::memory_order_acq_rel))
|
if (g_finalizer_has_work.compare_exchange_strong(expected, false, AK::MemoryOrder::memory_order_acq_rel))
|
||||||
Thread::finalize_dying_threads();
|
Thread::finalize_dying_threads();
|
||||||
}
|
}
|
||||||
});
|
}, nullptr);
|
||||||
g_finalizer = finalizer_thread;
|
g_finalizer = finalizer_thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -215,6 +215,15 @@ void Thread::die_if_needed()
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Thread::exit(void* exit_value)
|
||||||
|
{
|
||||||
|
ASSERT(Thread::current() == this);
|
||||||
|
m_exit_value = exit_value;
|
||||||
|
set_should_die();
|
||||||
|
unlock_process_if_locked();
|
||||||
|
die_if_needed();
|
||||||
|
}
|
||||||
|
|
||||||
void Thread::yield_without_holding_big_lock()
|
void Thread::yield_without_holding_big_lock()
|
||||||
{
|
{
|
||||||
bool did_unlock = unlock_process_if_locked();
|
bool did_unlock = unlock_process_if_locked();
|
||||||
|
|
|
@ -572,6 +572,8 @@ public:
|
||||||
bool should_die() const { return m_should_die; }
|
bool should_die() const { return m_should_die; }
|
||||||
void die_if_needed();
|
void die_if_needed();
|
||||||
|
|
||||||
|
void exit(void* = nullptr);
|
||||||
|
|
||||||
bool tick();
|
bool tick();
|
||||||
void set_ticks_left(u32 t) { m_ticks_left = t; }
|
void set_ticks_left(u32 t) { m_ticks_left = t; }
|
||||||
u32 ticks_left() const { return m_ticks_left; }
|
u32 ticks_left() const { return m_ticks_left; }
|
||||||
|
|
|
@ -86,7 +86,7 @@ u32 __stack_chk_guard;
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
[[noreturn]] static void init_stage2();
|
[[noreturn]] static void init_stage2(void*);
|
||||||
static void setup_serial_debug();
|
static void setup_serial_debug();
|
||||||
|
|
||||||
// boot.S expects these functions precisely this this. We declare them here
|
// boot.S expects these functions precisely this this. We declare them here
|
||||||
|
@ -168,7 +168,7 @@ extern "C" [[noreturn]] void init()
|
||||||
|
|
||||||
{
|
{
|
||||||
RefPtr<Thread> init_stage2_thread;
|
RefPtr<Thread> init_stage2_thread;
|
||||||
Process::create_kernel_process(init_stage2_thread, "init_stage2", init_stage2);
|
Process::create_kernel_process(init_stage2_thread, "init_stage2", init_stage2, nullptr);
|
||||||
// We need to make sure we drop the reference for init_stage2_thread
|
// We need to make sure we drop the reference for init_stage2_thread
|
||||||
// before calling into Scheduler::start, otherwise we will have a
|
// before calling into Scheduler::start, otherwise we will have a
|
||||||
// dangling Thread that never gets cleaned up
|
// dangling Thread that never gets cleaned up
|
||||||
|
@ -210,7 +210,7 @@ extern "C" void init_finished(u32 cpu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_stage2()
|
void init_stage2(void*)
|
||||||
{
|
{
|
||||||
if (APIC::initialized() && APIC::the().enabled_processor_count() > 1) {
|
if (APIC::initialized() && APIC::the().enabled_processor_count() > 1) {
|
||||||
// We can't start the APs until we have a scheduler up and running.
|
// We can't start the APs until we have a scheduler up and running.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue