mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-29 16:21:29 +00: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: https://github.com/SerenityOS/serenity/commit/6a620562cc7 Pull-request: https://github.com/SerenityOS/serenity/pull/4113
|
@ -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 context_first_init(Thread* from_thread, Thread* to_thread, TrapFrame* trap);
|
||||
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 post_init_finished(void);
|
||||
extern "C" void handle_interrupt(TrapFrame*);
|
||||
|
@ -1457,6 +1458,11 @@ asm(
|
|||
" jmp common_trap_exit \n"
|
||||
);
|
||||
|
||||
void exit_kernel_thread(void)
|
||||
{
|
||||
Thread::current()->exit();
|
||||
}
|
||||
|
||||
u32 Processor::init_context(Thread& thread, bool leave_crit)
|
||||
{
|
||||
ASSERT(is_kernel_mode());
|
||||
|
@ -1468,7 +1474,7 @@ u32 Processor::init_context(Thread& thread, bool leave_crit)
|
|||
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;
|
||||
|
||||
// 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
|
||||
// unless we're switching back to user mode
|
||||
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 {
|
||||
stack_top -= sizeof(RegisterState);
|
||||
}
|
||||
|
||||
// we want to end up 16-byte aligned, %esp + 4 should be aligned
|
||||
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()
|
||||
// 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_tcp(const IPv4Packet&, const timeval& packet_timestamp);
|
||||
|
||||
[[noreturn]] static void NetworkTask_main();
|
||||
[[noreturn]] static void NetworkTask_main(void*);
|
||||
|
||||
void NetworkTask::spawn()
|
||||
{
|
||||
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;
|
||||
u8 octet = 15;
|
||||
|
|
|
@ -311,10 +311,11 @@ RefPtr<Process> Process::create_user_process(RefPtr<Thread>& first_thread, const
|
|||
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));
|
||||
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) {
|
||||
ScopedSpinLock lock(g_processes_lock);
|
||||
|
@ -765,7 +766,7 @@ KResult Process::send_signal(u8 signal, Process* sender)
|
|||
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));
|
||||
|
||||
|
@ -781,6 +782,7 @@ RefPtr<Thread> Process::create_kernel_thread(void (*entry)(), u32 priority, cons
|
|||
|
||||
auto& tss = thread->tss();
|
||||
tss.eip = (FlatPtr)entry;
|
||||
tss.esp = FlatPtr(entry_data); // entry function argument is expected to be in tss.esp
|
||||
|
||||
ScopedSpinLock lock(g_scheduler_lock);
|
||||
thread->set_state(Thread::State::Runnable);
|
||||
|
|
|
@ -126,14 +126,35 @@ public:
|
|||
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);
|
||||
~Process();
|
||||
|
||||
static Vector<ProcessID> all_pids();
|
||||
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; }
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
inline bool InodeMetadata::may_execute(const Process& process) const
|
||||
{
|
||||
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_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(idle_thread);
|
||||
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(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);
|
||||
return idle_thread;
|
||||
}
|
||||
|
@ -832,7 +832,7 @@ void Scheduler::notify_finalizer()
|
|||
g_finalizer_wait_queue->wake_all();
|
||||
}
|
||||
|
||||
void Scheduler::idle_loop()
|
||||
void Scheduler::idle_loop(void*)
|
||||
{
|
||||
dbg() << "Scheduler[" << Processor::current().id() << "]: idle loop running";
|
||||
ASSERT(are_interrupts_enabled());
|
||||
|
|
|
@ -66,7 +66,7 @@ public:
|
|||
static void prepare_for_idle_loop();
|
||||
static Process* colonel();
|
||||
static void beep();
|
||||
static void idle_loop();
|
||||
static void idle_loop(void*);
|
||||
static void invoke_async();
|
||||
static void notify_finalizer();
|
||||
|
||||
|
|
|
@ -93,11 +93,7 @@ void Process::sys$exit_thread(Userspace<void*> exit_value)
|
|||
{
|
||||
REQUIRE_PROMISE(thread);
|
||||
cli();
|
||||
auto current_thread = Thread::current();
|
||||
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();
|
||||
Thread::current()->exit(reinterpret_cast<void*>(exit_value.ptr()));
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace Kernel {
|
|||
void FinalizerTask::spawn()
|
||||
{
|
||||
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);
|
||||
for (;;) {
|
||||
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))
|
||||
Thread::finalize_dying_threads();
|
||||
}
|
||||
});
|
||||
}, nullptr);
|
||||
g_finalizer = finalizer_thread;
|
||||
}
|
||||
|
||||
|
|
|
@ -215,6 +215,15 @@ void Thread::die_if_needed()
|
|||
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()
|
||||
{
|
||||
bool did_unlock = unlock_process_if_locked();
|
||||
|
|
|
@ -572,6 +572,8 @@ public:
|
|||
bool should_die() const { return m_should_die; }
|
||||
void die_if_needed();
|
||||
|
||||
void exit(void* = nullptr);
|
||||
|
||||
bool tick();
|
||||
void set_ticks_left(u32 t) { m_ticks_left = t; }
|
||||
u32 ticks_left() const { return m_ticks_left; }
|
||||
|
|
|
@ -86,7 +86,7 @@ u32 __stack_chk_guard;
|
|||
|
||||
namespace Kernel {
|
||||
|
||||
[[noreturn]] static void init_stage2();
|
||||
[[noreturn]] static void init_stage2(void*);
|
||||
static void setup_serial_debug();
|
||||
|
||||
// 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;
|
||||
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
|
||||
// before calling into Scheduler::start, otherwise we will have a
|
||||
// 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) {
|
||||
// We can't start the APs until we have a scheduler up and running.
|
||||
|
|
Loading…
Reference in a new issue