diff --git a/AK/DoublyLinkedList.h b/AK/DoublyLinkedList.h index 3bd8899a3bd..14661acf0ec 100644 --- a/AK/DoublyLinkedList.h +++ b/AK/DoublyLinkedList.h @@ -1,6 +1,7 @@ #pragma once #include "StdLibExtras.h" +#include namespace AK { diff --git a/Kernel/DiskBackedFileSystem.cpp b/Kernel/DiskBackedFileSystem.cpp index 5b799552306..f6214bb8b33 100644 --- a/Kernel/DiskBackedFileSystem.cpp +++ b/Kernel/DiskBackedFileSystem.cpp @@ -1,6 +1,7 @@ #include "DiskBackedFileSystem.h" #include "i386.h" #include +#include //#define DBFS_DEBUG diff --git a/Kernel/DiskDevice.cpp b/Kernel/DiskDevice.cpp index a0c655e6d9a..d7157485add 100644 --- a/Kernel/DiskDevice.cpp +++ b/Kernel/DiskDevice.cpp @@ -10,7 +10,6 @@ DiskDevice::~DiskDevice() bool DiskDevice::read(DiskOffset offset, unsigned length, byte* out) const { - //kprintf("DD::read %u x%u\n", offset, length); ASSERT((offset % block_size()) == 0); ASSERT((length % block_size()) == 0); dword first_block = offset / block_size(); diff --git a/Kernel/Ext2FileSystem.cpp b/Kernel/Ext2FileSystem.cpp index a5cfcd2123f..7ae1bb65a6b 100644 --- a/Kernel/Ext2FileSystem.cpp +++ b/Kernel/Ext2FileSystem.cpp @@ -1182,8 +1182,8 @@ RetainPtr Ext2FS::create_inode(InodeIdentifier parent_id, const String& n ext2_inode e2inode; memset(&e2inode, 0, sizeof(ext2_inode)); e2inode.i_mode = mode; - e2inode.i_uid = current->euid(); - e2inode.i_gid = current->egid(); + e2inode.i_uid = current->process().euid(); + e2inode.i_gid = current->process().egid(); e2inode.i_size = size; e2inode.i_atime = timestamp; e2inode.i_ctime = timestamp; diff --git a/Kernel/IDEDiskDevice.cpp b/Kernel/IDEDiskDevice.cpp index ba70225cd79..1b0eb27061e 100644 --- a/Kernel/IDEDiskDevice.cpp +++ b/Kernel/IDEDiskDevice.cpp @@ -178,8 +178,8 @@ bool IDEDiskDevice::read_sectors(dword start_sector, word count, byte* outbuf) { LOCKER(m_lock); #ifdef DISK_DEBUG - kprintf("%s: Disk::read_sectors request (%u sector(s) @ %u)\n", - current->name().characters(), + dbgprintf("%s: Disk::read_sectors request (%u sector(s) @ %u)\n", + current->process().name().characters(), count, start_sector); #endif @@ -231,7 +231,7 @@ bool IDEDiskDevice::write_sectors(dword start_sector, word count, const byte* da LOCKER(m_lock); #ifdef DISK_DEBUG dbgprintf("%s(%u): IDEDiskDevice::write_sectors request (%u sector(s) @ %u)\n", - current->name().characters(), + current->process().name().characters(), current->pid(), count, start_sector); diff --git a/Kernel/IPv4Socket.cpp b/Kernel/IPv4Socket.cpp index 63ad0ae80a6..3d3ea4664b7 100644 --- a/Kernel/IPv4Socket.cpp +++ b/Kernel/IPv4Socket.cpp @@ -33,7 +33,7 @@ Retained IPv4Socket::create(int type, int protocol) IPv4Socket::IPv4Socket(int type, int protocol) : Socket(AF_INET, type, protocol) { - kprintf("%s(%u) IPv4Socket{%p} created with type=%u, protocol=%d\n", current->name().characters(), current->pid(), this, type, protocol); + kprintf("%s(%u) IPv4Socket{%p} created with type=%u, protocol=%d\n", current->process().name().characters(), current->pid(), this, type, protocol); LOCKER(all_sockets().lock()); all_sockets().resource().set(this); } @@ -189,7 +189,7 @@ ssize_t IPv4Socket::recvfrom(void* buffer, size_t buffer_length, int flags, sock current->set_blocked_socket(this); load_receive_deadline(); - block(Process::BlockedReceive); + block(Thread::BlockedReceive); Scheduler::yield(); LOCKER(lock()); diff --git a/Kernel/KSyms.cpp b/Kernel/KSyms.cpp index a9adf83a0b4..875a6991ede 100644 --- a/Kernel/KSyms.cpp +++ b/Kernel/KSyms.cpp @@ -74,7 +74,7 @@ static void load_ksyms_from_data(const ByteBuffer& buffer) [[gnu::noinline]] void dump_backtrace_impl(dword ebp, bool use_ksyms) { if (!current) { - hang(); + //hang(); return; } if (use_ksyms && !ksyms_ready) { @@ -87,13 +87,13 @@ static void load_ksyms_from_data(const ByteBuffer& buffer) }; Vector recognized_symbols; if (use_ksyms) { - for (dword* stack_ptr = (dword*)ebp; current->validate_read_from_kernel(LinearAddress((dword)stack_ptr)); stack_ptr = (dword*)*stack_ptr) { + for (dword* stack_ptr = (dword*)ebp; current->process().validate_read_from_kernel(LinearAddress((dword)stack_ptr)); stack_ptr = (dword*)*stack_ptr) { dword retaddr = stack_ptr[1]; if (auto* ksym = ksymbolicate(retaddr)) recognized_symbols.append({ retaddr, ksym }); } } else{ - for (dword* stack_ptr = (dword*)ebp; current->validate_read_from_kernel(LinearAddress((dword)stack_ptr)); stack_ptr = (dword*)*stack_ptr) { + for (dword* stack_ptr = (dword*)ebp; current->process().validate_read_from_kernel(LinearAddress((dword)stack_ptr)); stack_ptr = (dword*)*stack_ptr) { dword retaddr = stack_ptr[1]; kprintf("%x (next: %x)\n", retaddr, stack_ptr ? (dword*)*stack_ptr : 0); } @@ -129,7 +129,7 @@ void load_ksyms() auto result = VFS::the().open("/kernel.map", 0, 0, *VFS::the().root_inode()); ASSERT(!result.is_error()); auto descriptor = result.value(); - auto buffer = descriptor->read_entire_file(*current); + auto buffer = descriptor->read_entire_file(current->process()); ASSERT(buffer); load_ksyms_from_data(buffer); } diff --git a/Kernel/LocalSocket.cpp b/Kernel/LocalSocket.cpp index 1f7209bdd51..d14cf3ca13e 100644 --- a/Kernel/LocalSocket.cpp +++ b/Kernel/LocalSocket.cpp @@ -15,7 +15,7 @@ LocalSocket::LocalSocket(int type) : Socket(AF_LOCAL, type, 0) { #ifdef DEBUG_LOCAL_SOCKET - kprintf("%s(%u) LocalSocket{%p} created with type=%u\n", current->name().characters(), current->pid(), this, type); + kprintf("%s(%u) LocalSocket{%p} created with type=%u\n", current->process().name().characters(), current->pid(), this, type); #endif } @@ -46,10 +46,10 @@ KResult LocalSocket::bind(const sockaddr* address, socklen_t address_size) memcpy(safe_address, local_address.sun_path, sizeof(local_address.sun_path)); #ifdef DEBUG_LOCAL_SOCKET - kprintf("%s(%u) LocalSocket{%p} bind(%s)\n", current->name().characters(), current->pid(), this, safe_address); + kprintf("%s(%u) LocalSocket{%p} bind(%s)\n", current->process().name().characters(), current->pid(), this, safe_address); #endif - auto result = VFS::the().open(safe_address, O_CREAT | O_EXCL, S_IFSOCK | 0666, current->cwd_inode()); + auto result = VFS::the().open(safe_address, O_CREAT | O_EXCL, S_IFSOCK | 0666, current->process().cwd_inode()); if (result.is_error()) { if (result.error() == -EEXIST) return KResult(-EADDRINUSE); @@ -78,10 +78,10 @@ KResult LocalSocket::connect(const sockaddr* address, socklen_t address_size) memcpy(safe_address, local_address.sun_path, sizeof(local_address.sun_path)); #ifdef DEBUG_LOCAL_SOCKET - kprintf("%s(%u) LocalSocket{%p} connect(%s)\n", current->name().characters(), current->pid(), this, safe_address); + kprintf("%s(%u) LocalSocket{%p} connect(%s)\n", current->process().name().characters(), current->pid(), this, safe_address); #endif - auto descriptor_or_error = VFS::the().open(safe_address, 0, 0, current->cwd_inode()); + auto descriptor_or_error = VFS::the().open(safe_address, 0, 0, current->process().cwd_inode()); if (descriptor_or_error.is_error()) return KResult(-ECONNREFUSED); m_file = move(descriptor_or_error.value()); diff --git a/Kernel/Lock.h b/Kernel/Lock.h index 4854ce7ccb0..c91c13faebd 100644 --- a/Kernel/Lock.h +++ b/Kernel/Lock.h @@ -5,8 +5,8 @@ #include #include -class Process; -extern Process* current; +class Thread; +extern Thread* current; static inline dword CAS(volatile dword* mem, dword newval, dword oldval) { @@ -32,7 +32,7 @@ public: private: volatile dword m_lock { 0 }; dword m_level { 0 }; - Process* m_holder { nullptr }; + Thread* m_holder { nullptr }; const char* m_name { nullptr }; }; diff --git a/Kernel/Makefile b/Kernel/Makefile index 5d42acab6d9..c57ac37d9c0 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -5,6 +5,7 @@ KERNEL_OBJS = \ StdLib.o \ i386.o \ Process.o \ + Thread.o \ i8253.o \ KeyboardDevice.o \ CMOS.o \ diff --git a/Kernel/MasterPTY.cpp b/Kernel/MasterPTY.cpp index 819277edd66..b727830612d 100644 --- a/Kernel/MasterPTY.cpp +++ b/Kernel/MasterPTY.cpp @@ -11,8 +11,8 @@ MasterPTY::MasterPTY(unsigned index) , m_slave(adopt(*new SlavePTY(*this, index))) , m_index(index) { - set_uid(current->uid()); - set_gid(current->gid()); + set_uid(current->process().uid()); + set_gid(current->process().gid()); } MasterPTY::~MasterPTY() diff --git a/Kernel/MemoryManager.cpp b/Kernel/MemoryManager.cpp index 1ce601bcc87..e5720473e79 100644 --- a/Kernel/MemoryManager.cpp +++ b/Kernel/MemoryManager.cpp @@ -36,6 +36,8 @@ MemoryManager::MemoryManager() m_page_table_zero = (dword*)0x6000; initialize_paging(); + + kprintf("MM initialized.\n"); } MemoryManager::~MemoryManager() @@ -109,6 +111,10 @@ void MemoryManager::initialize_paging() "orl $0x80000001, %%eax\n" "movl %%eax, %%cr0\n" :::"%eax", "memory"); + +#ifdef MM_DEBUG + dbgprintf("MM: Paging initialized.\n"); +#endif } RetainPtr MemoryManager::allocate_page_table(PageDirectory& page_directory, unsigned index) @@ -362,11 +368,12 @@ bool MemoryManager::page_in_from_inode(Region& region, unsigned page_index_in_re PageFaultResponse MemoryManager::handle_page_fault(const PageFault& fault) { ASSERT_INTERRUPTS_DISABLED(); + ASSERT(current); #ifdef PAGE_FAULT_DEBUG dbgprintf("MM: handle_page_fault(%w) at L%x\n", fault.code(), fault.laddr().get()); #endif ASSERT(fault.laddr() != m_quickmap_addr); - auto* region = region_from_laddr(*current, fault.laddr()); + auto* region = region_from_laddr(current->process(), fault.laddr()); if (!region) { kprintf("NP(error) fault at invalid address L%x\n", fault.laddr().get()); return PageFaultResponse::ShouldCrash; @@ -441,8 +448,9 @@ RetainPtr MemoryManager::allocate_supervisor_physical_page() void MemoryManager::enter_process_paging_scope(Process& process) { + ASSERT(current); InterruptDisabler disabler; - current->m_tss.cr3 = process.page_directory().cr3(); + current->tss().cr3 = process.page_directory().cr3(); asm volatile("movl %%eax, %%cr3"::"a"(process.page_directory().cr3()):"memory"); } @@ -620,9 +628,10 @@ bool MemoryManager::validate_user_write(const Process& process, LinearAddress la Retained Region::clone() { + ASSERT(current); if (m_shared || (m_readable && !m_writable)) { dbgprintf("%s<%u> Region::clone(): sharing %s (L%x)\n", - current->name().characters(), + current->process().name().characters(), current->pid(), m_name.characters(), laddr().get()); @@ -631,14 +640,14 @@ Retained Region::clone() } dbgprintf("%s<%u> Region::clone(): cowing %s (L%x)\n", - current->name().characters(), + current->process().name().characters(), current->pid(), m_name.characters(), laddr().get()); // Set up a COW region. The parent (this) region becomes COW as well! for (size_t i = 0; i < page_count(); ++i) m_cow_map.set(i, true); - MM.remap_region(current->page_directory(), *this); + MM.remap_region(current->process().page_directory(), *this); return adopt(*new Region(laddr(), size(), m_vmo->clone(), m_offset_in_vmo, String(m_name), m_readable, m_writable, true)); } @@ -966,6 +975,33 @@ PageDirectory::~PageDirectory() void PageDirectory::flush(LinearAddress laddr) { - if (¤t->page_directory() == this) +#ifdef MM_DEBUG + dbgprintf("MM: Flush page L%x\n", laddr.get()); +#endif + if (!current) + return; + if (¤t->process().page_directory() == this) MM.flush_tlb(laddr); } + +ProcessPagingScope::ProcessPagingScope(Process& process) +{ + ASSERT(current); + MM.enter_process_paging_scope(process); +} + +ProcessPagingScope::~ProcessPagingScope() +{ + MM.enter_process_paging_scope(current->process()); +} + +KernelPagingScope::KernelPagingScope() +{ + ASSERT(current); + MM.enter_kernel_paging_scope(); +} + +KernelPagingScope::~KernelPagingScope() +{ + MM.enter_process_paging_scope(current->process()); +} diff --git a/Kernel/MemoryManager.h b/Kernel/MemoryManager.h index a045ff1270e..60e7fc8c9bf 100644 --- a/Kernel/MemoryManager.h +++ b/Kernel/MemoryManager.h @@ -15,8 +15,6 @@ #define PAGE_ROUND_UP(x) ((((dword)(x)) + PAGE_SIZE-1) & (~(PAGE_SIZE-1))) -class Process; -extern Process* current; class SynthFSInode; enum class PageFaultResponse { @@ -411,11 +409,11 @@ private: }; struct ProcessPagingScope { - ProcessPagingScope(Process& process) { MM.enter_process_paging_scope(process); } - ~ProcessPagingScope() { MM.enter_process_paging_scope(*current); } + ProcessPagingScope(Process&); + ~ProcessPagingScope(); }; struct KernelPagingScope { - KernelPagingScope() { MM.enter_kernel_paging_scope(); } - ~KernelPagingScope() { MM.enter_process_paging_scope(*current); } + KernelPagingScope(); + ~KernelPagingScope(); }; diff --git a/Kernel/ProcFS.cpp b/Kernel/ProcFS.cpp index e59929ef3de..bfa183a49f4 100644 --- a/Kernel/ProcFS.cpp +++ b/Kernel/ProcFS.cpp @@ -292,19 +292,24 @@ ByteBuffer procfs$pid_stack(InodeIdentifier identifier) dword address; const KSym* ksym; }; - Vector recognized_symbols; - if (auto* eip_ksym = ksymbolicate(process.tss().eip)) - recognized_symbols.append({ process.tss().eip, eip_ksym }); - for (dword* stack_ptr = (dword*)process.frame_ptr(); process.validate_read_from_kernel(LinearAddress((dword)stack_ptr)); stack_ptr = (dword*)*stack_ptr) { - dword retaddr = stack_ptr[1]; - if (auto* ksym = ksymbolicate(retaddr)) - recognized_symbols.append({ retaddr, ksym }); - } StringBuilder builder; - for (auto& symbol : recognized_symbols) { - unsigned offset = symbol.address - symbol.ksym->address; - builder.appendf("%p %s +%u\n", symbol.address, symbol.ksym->name, offset); - } + process.for_each_thread([&] (Thread& thread) { + builder.appendf("Thread %d:\n", thread.tid()); + Vector recognized_symbols; + if (auto* eip_ksym = ksymbolicate(thread.tss().eip)) + recognized_symbols.append({ thread.tss().eip, eip_ksym }); + for (dword* stack_ptr = (dword*)thread.frame_ptr(); process.validate_read_from_kernel(LinearAddress((dword)stack_ptr)); stack_ptr = (dword*)*stack_ptr) { + dword retaddr = stack_ptr[1]; + if (auto* ksym = ksymbolicate(retaddr)) + recognized_symbols.append({ retaddr, ksym }); + } + + for (auto& symbol : recognized_symbols) { + unsigned offset = symbol.address - symbol.ksym->address; + builder.appendf("%p %s +%u\n", symbol.address, symbol.ksym->name, offset); + } + return IterationDecision::Continue; + }); return builder.to_byte_buffer(); } @@ -314,19 +319,23 @@ ByteBuffer procfs$pid_regs(InodeIdentifier identifier) if (!handle) return { }; auto& process = handle->process(); - auto& tss = process.tss(); StringBuilder builder; - builder.appendf("eax: %x\n", tss.eax); - builder.appendf("ebx: %x\n", tss.ebx); - builder.appendf("ecx: %x\n", tss.ecx); - builder.appendf("edx: %x\n", tss.edx); - builder.appendf("esi: %x\n", tss.esi); - builder.appendf("edi: %x\n", tss.edi); - builder.appendf("ebp: %x\n", tss.ebp); - builder.appendf("cr3: %x\n", tss.cr3); - builder.appendf("flg: %x\n", tss.eflags); - builder.appendf("sp: %w:%x\n", tss.ss, tss.esp); - builder.appendf("pc: %w:%x\n", tss.cs, tss.eip); + process.for_each_thread([&] (Thread& thread) { + builder.appendf("Thread %d:\n", thread.tid()); + auto& tss = thread.tss(); + builder.appendf("eax: %x\n", tss.eax); + builder.appendf("ebx: %x\n", tss.ebx); + builder.appendf("ecx: %x\n", tss.ecx); + builder.appendf("edx: %x\n", tss.edx); + builder.appendf("esi: %x\n", tss.esi); + builder.appendf("edi: %x\n", tss.edi); + builder.appendf("ebp: %x\n", tss.ebp); + builder.appendf("cr3: %x\n", tss.cr3); + builder.appendf("flg: %x\n", tss.eflags); + builder.appendf("sp: %w:%x\n", tss.ss, tss.esp); + builder.appendf("pc: %w:%x\n", tss.cs, tss.eip); + return IterationDecision::Continue; + }); return builder.to_byte_buffer(); } @@ -530,7 +539,7 @@ ByteBuffer procfs$summary(InodeIdentifier) process->uid(), to_string(process->state()), process->ppid(), - process->times_scheduled(), + process->main_thread().times_scheduled(), // FIXME(Thread): Bill all scheds to the process process->number_of_open_file_descriptors(), process->tty() ? strrchr(process->tty()->tty_name().characters(), '/') + 1 : "n/a", process->name().characters()); @@ -562,7 +571,7 @@ ByteBuffer procfs$all(InodeIdentifier) auto build_process_line = [&builder] (Process* process) { builder.appendf("%u,%u,%u,%u,%u,%u,%u,%s,%u,%u,%s,%s,%u,%u,%u,%u,%s\n", process->pid(), - process->times_scheduled(), + process->main_thread().times_scheduled(), // FIXME(Thread): Bill all scheds to the process process->tty() ? process->tty()->pgid() : 0, process->pgid(), process->sid(), @@ -576,7 +585,7 @@ ByteBuffer procfs$all(InodeIdentifier) process->amount_virtual(), process->amount_resident(), process->amount_shared(), - process->ticks(), + process->main_thread().ticks(), // FIXME(Thread): Bill all ticks to the process to_string(process->priority()) ); }; diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 60f67afba22..4ac23417adc 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -27,15 +27,12 @@ #include //#define DEBUG_IO -//#define TASK_DEBUG -//#define FORK_DEBUG -//#define SIGNAL_DEBUG +#define TASK_DEBUG +#define FORK_DEBUG +#define SIGNAL_DEBUG #define MAX_PROCESS_GIDS 32 //#define SHARED_BUFFER_DEBUG -static const dword default_kernel_stack_size = 16384; -static const dword default_userspace_stack_size = 65536; - static pid_t next_pid; InlineLinkedList* g_processes; static String* s_hostname; @@ -52,7 +49,6 @@ void Process::initialize() g_processes = new InlineLinkedList; s_hostname = new String("courage"); s_hostname_lock = new Lock; - Scheduler::initialize(); } Vector Process::all_pids() @@ -226,8 +222,6 @@ Process* Process::fork(RegisterDump& regs) if (!child) return nullptr; - memcpy(child->m_signal_action_data, m_signal_action_data, sizeof(m_signal_action_data)); - child->m_signal_mask = m_signal_mask; #ifdef FORK_DEBUG dbgprintf("fork: child=%p\n", child); #endif @@ -239,35 +233,31 @@ Process* Process::fork(RegisterDump& regs) auto cloned_region = region->clone(); child->m_regions.append(move(cloned_region)); MM.map_region(*child, *child->m_regions.last()); - if (region.ptr() == m_display_framebuffer_region.ptr()) - child->m_display_framebuffer_region = child->m_regions.last().copy_ref(); } for (auto gid : m_gids) child->m_gids.set(gid); - child->m_tss.eax = 0; // fork() returns 0 in the child :^) - child->m_tss.ebx = regs.ebx; - child->m_tss.ecx = regs.ecx; - child->m_tss.edx = regs.edx; - child->m_tss.ebp = regs.ebp; - child->m_tss.esp = regs.esp_if_crossRing; - child->m_tss.esi = regs.esi; - child->m_tss.edi = regs.edi; - child->m_tss.eflags = regs.eflags; - child->m_tss.eip = regs.eip; - child->m_tss.cs = regs.cs; - child->m_tss.ds = regs.ds; - child->m_tss.es = regs.es; - child->m_tss.fs = regs.fs; - child->m_tss.gs = regs.gs; - child->m_tss.ss = regs.ss_if_crossRing; - - child->m_fpu_state = m_fpu_state; - child->m_has_used_fpu = m_has_used_fpu; + auto& child_tss = child->main_thread().m_tss; + child_tss.eax = 0; // fork() returns 0 in the child :^) + child_tss.ebx = regs.ebx; + child_tss.ecx = regs.ecx; + child_tss.edx = regs.edx; + child_tss.ebp = regs.ebp; + child_tss.esp = regs.esp_if_crossRing; + child_tss.esi = regs.esi; + child_tss.edi = regs.edi; + child_tss.eflags = regs.eflags; + child_tss.eip = regs.eip; + child_tss.cs = regs.cs; + child_tss.ds = regs.ds; + child_tss.es = regs.es; + child_tss.fs = regs.fs; + child_tss.gs = regs.gs; + child_tss.ss = regs.ss_if_crossRing; #ifdef FORK_DEBUG - dbgprintf("fork: child will begin executing at %w:%x with stack %w:%x\n", child->m_tss.cs, child->m_tss.eip, child->m_tss.ss, child->m_tss.esp); + dbgprintf("fork: child will begin executing at %w:%x with stack %w:%x, kstack %w:%x\n", child_tss.cs, child_tss.eip, child_tss.ss, child_tss.esp, child_tss.ss0, child_tss.esp0); #endif { @@ -276,8 +266,11 @@ Process* Process::fork(RegisterDump& regs) system.nprocess++; } #ifdef TASK_DEBUG - kprintf("Process %u (%s) forked from %u @ %p\n", child->pid(), child->name().characters(), m_pid, child->m_tss.eip); + kprintf("Process %u (%s) forked from %u @ %p\n", child->pid(), child->name().characters(), m_pid, child_tss.eip); #endif + + child->main_thread().set_state(Thread::State::Skip1SchedulerPass); + return child; } @@ -292,6 +285,9 @@ int Process::do_exec(String path, Vector arguments, Vector envir { ASSERT(is_ring3()); + // FIXME(Thread): Kill any threads the moment we commit to the exec(). + ASSERT(thread_count() == 1); + auto parts = path.split('/'); if (parts.is_empty()) return -ENOENT; @@ -305,7 +301,6 @@ int Process::do_exec(String path, Vector arguments, Vector envir return -EACCES; if (!descriptor->metadata().size) { - kprintf("exec() of 0-length binaries not supported\n"); return -ENOTIMPL; } @@ -355,7 +350,7 @@ int Process::do_exec(String path, Vector arguments, Vector envir if (!success) { m_page_directory = move(old_page_directory); // FIXME: RAII this somehow instead. - ASSERT(current == this); + ASSERT(¤t->process() == this); MM.enter_process_paging_scope(*this); m_regions = move(old_regions); kprintf("sys$execve: Failure loading %s\n", path.characters()); @@ -366,20 +361,19 @@ int Process::do_exec(String path, Vector arguments, Vector envir if (!entry_eip) { m_page_directory = move(old_page_directory); // FIXME: RAII this somehow instead. - ASSERT(current == this); + ASSERT(¤t->process() == this); MM.enter_process_paging_scope(*this); m_regions = move(old_regions); return -ENOEXEC; } } - kfree(m_kernel_stack_for_signal_handler); - m_kernel_stack_for_signal_handler = nullptr; - m_signal_stack_user_region = nullptr; - m_display_framebuffer_region = nullptr; - set_default_signal_dispositions(); - m_signal_mask = 0; - m_pending_signals = 0; + kfree(current->m_kernel_stack_for_signal_handler); + current->m_kernel_stack_for_signal_handler = nullptr; + current->m_signal_stack_user_region = nullptr; + current->set_default_signal_dispositions(); + current->m_signal_mask = 0; + current->m_pending_signals = 0; for (int i = 0; i < m_fds.size(); ++i) { auto& daf = m_fds[i]; @@ -392,29 +386,30 @@ int Process::do_exec(String path, Vector arguments, Vector envir // We cli() manually here because we don't want to get interrupted between do_exec() and Schedule::yield(). // The reason is that the task redirection we've set up above will be clobbered by the timer IRQ. // If we used an InterruptDisabler that sti()'d on exit, we might timer tick'd too soon in exec(). - if (current == this) + if (¤t->process() == this) cli(); - Scheduler::prepare_to_modify_tss(*this); + Scheduler::prepare_to_modify_tss(main_thread()); m_name = parts.take_last(); - dword old_esp0 = m_tss.esp0; + // ss0 sp!!!!!!!!! + dword old_esp0 = main_thread().m_tss.esp0; - memset(&m_tss, 0, sizeof(m_tss)); - m_tss.eflags = 0x0202; - m_tss.eip = entry_eip; - m_tss.cs = 0x1b; - m_tss.ds = 0x23; - m_tss.es = 0x23; - m_tss.fs = 0x23; - m_tss.gs = 0x23; - m_tss.ss = 0x23; - m_tss.cr3 = page_directory().cr3(); - make_userspace_stack(move(arguments), move(environment)); - m_tss.ss0 = 0x10; - m_tss.esp0 = old_esp0; - m_tss.ss2 = m_pid; + memset(&main_thread().m_tss, 0, sizeof(main_thread().m_tss)); + main_thread().m_tss.eflags = 0x0202; + main_thread().m_tss.eip = entry_eip; + main_thread().m_tss.cs = 0x1b; + main_thread().m_tss.ds = 0x23; + main_thread().m_tss.es = 0x23; + main_thread().m_tss.fs = 0x23; + main_thread().m_tss.gs = 0x23; + main_thread().m_tss.ss = 0x23; + main_thread().m_tss.cr3 = page_directory().cr3(); + main_thread().make_userspace_stack(move(arguments), move(environment)); + main_thread().m_tss.ss0 = 0x10; + main_thread().m_tss.esp0 = old_esp0; + main_thread().m_tss.ss2 = m_pid; m_executable = descriptor->inode(); @@ -424,60 +419,13 @@ int Process::do_exec(String path, Vector arguments, Vector envir m_egid = descriptor->metadata().gid; #ifdef TASK_DEBUG - kprintf("Process %u (%s) exec'd %s @ %p\n", pid(), name().characters(), path.characters(), m_tss.eip); + kprintf("Process %u (%s) exec'd %s @ %p\n", pid(), name().characters(), path.characters(), main_thread().tss().eip); #endif - set_state(Skip1SchedulerPass); + main_thread().set_state(Thread::State::Skip1SchedulerPass); return 0; } -void Process::make_userspace_stack(Vector arguments, Vector environment) -{ - auto* region = allocate_region(LinearAddress(), default_userspace_stack_size, "stack"); - ASSERT(region); - m_stack_top3 = region->laddr().offset(default_userspace_stack_size).get(); - m_tss.esp = m_stack_top3; - - char* stack_base = (char*)region->laddr().get(); - int argc = arguments.size(); - char** argv = (char**)stack_base; - char** env = argv + arguments.size() + 1; - char* bufptr = stack_base + (sizeof(char*) * (arguments.size() + 1)) + (sizeof(char*) * (environment.size() + 1)); - - size_t total_blob_size = 0; - for (auto& a : arguments) - total_blob_size += a.length() + 1; - for (auto& e : environment) - total_blob_size += e.length() + 1; - - size_t total_meta_size = sizeof(char*) * (arguments.size() + 1) + sizeof(char*) * (environment.size() + 1); - - // FIXME: It would be better if this didn't make us panic. - ASSERT((total_blob_size + total_meta_size) < default_userspace_stack_size); - - for (int i = 0; i < arguments.size(); ++i) { - argv[i] = bufptr; - memcpy(bufptr, arguments[i].characters(), arguments[i].length()); - bufptr += arguments[i].length(); - *(bufptr++) = '\0'; - } - argv[arguments.size()] = nullptr; - - for (int i = 0; i < environment.size(); ++i) { - env[i] = bufptr; - memcpy(bufptr, environment[i].characters(), environment[i].length()); - bufptr += environment[i].length(); - *(bufptr++) = '\0'; - } - env[environment.size()] = nullptr; - - // NOTE: The stack needs to be 16-byte aligned. - push_value_on_stack((dword)env); - push_value_on_stack((dword)argv); - push_value_on_stack((dword)argc); - push_value_on_stack(0); -} - int Process::exec(String path, Vector arguments, Vector environment) { // The bulk of exec() is done by do_exec(), which ensures that all locals @@ -486,7 +434,7 @@ int Process::exec(String path, Vector arguments, Vector environm if (rc < 0) return rc; - if (current == this) { + if (¤t->process() == this) { Scheduler::yield(); ASSERT_NOT_REACHED(); } @@ -571,7 +519,7 @@ Process* Process::create_user_process(const String& path, uid_t uid, gid_t gid, system.nprocess++; } #ifdef TASK_DEBUG - kprintf("Process %u (%s) spawned @ %p\n", process->pid(), process->name().characters(), process->m_tss.eip); + kprintf("Process %u (%s) spawned @ %p\n", process->pid(), process->name().characters(), process->main_thread().tss().eip); #endif error = 0; return process; @@ -580,19 +528,18 @@ Process* Process::create_user_process(const String& path, uid_t uid, gid_t gid, Process* Process::create_kernel_process(String&& name, void (*e)()) { auto* process = new Process(move(name), (uid_t)0, (gid_t)0, (pid_t)0, Ring0); - process->m_tss.eip = (dword)e; + process->main_thread().tss().eip = (dword)e; if (process->pid() != 0) { - { - InterruptDisabler disabler; - g_processes->prepend(process); - system.nprocess++; - } + InterruptDisabler disabler; + g_processes->prepend(process); + system.nprocess++; #ifdef TASK_DEBUG - kprintf("Kernel process %u (%s) spawned @ %p\n", process->pid(), process->name().characters(), process->m_tss.eip); + kprintf("Kernel process %u (%s) spawned @ %p\n", process->pid(), process->name().characters(), process->main_thread().tss().eip); #endif } + process->main_thread().set_state(Thread::State::Runnable); return process; } @@ -603,16 +550,24 @@ Process::Process(String&& name, uid_t uid, gid_t gid, pid_t ppid, RingLevel ring , m_gid(gid) , m_euid(uid) , m_egid(gid) - , m_state(Runnable) , m_ring(ring) , m_cwd(move(cwd)) , m_executable(move(executable)) , m_tty(tty) , m_ppid(ppid) { - set_default_signal_dispositions(); + dbgprintf("Process: New process PID=%u with name=%s\n", m_pid, m_name.characters()); - memset(&m_fpu_state, 0, sizeof(FPUState)); + m_page_directory = PageDirectory::create(); +#ifdef MM_DEBUG + dbgprintf("Process %u ctor: PD=%x created\n", pid(), m_page_directory.ptr()); +#endif + + // NOTE: fork() doesn't clone all threads; the thread that called fork() becomes the main thread in the new process. + if (fork_parent) + m_main_thread = current->clone(*this); + else + m_main_thread = new Thread(*this); m_gids.set(m_gid); @@ -628,11 +583,6 @@ Process::Process(String&& name, uid_t uid, gid_t gid, pid_t ppid, RingLevel ring } } - m_page_directory = PageDirectory::create(); -#ifdef MM_DEBUG - dbgprintf("Process %u ctor: PD=%x created\n", pid(), m_page_directory.ptr()); -#endif - if (fork_parent) { m_fds.resize(fork_parent->m_fds.size()); for (int i = 0; i < fork_parent->m_fds.size(); ++i) { @@ -657,82 +607,22 @@ Process::Process(String&& name, uid_t uid, gid_t gid, pid_t ppid, RingLevel ring else m_next_region = LinearAddress(0x10000000); - if (fork_parent) { - memcpy(&m_tss, &fork_parent->m_tss, sizeof(m_tss)); - } else { - memset(&m_tss, 0, sizeof(m_tss)); - - // Only IF is set when a process boots. - m_tss.eflags = 0x0202; - word cs, ds, ss; - - if (is_ring0()) { - cs = 0x08; - ds = 0x10; - ss = 0x10; - } else { - cs = 0x1b; - ds = 0x23; - ss = 0x23; - } - - m_tss.ds = ds; - m_tss.es = ds; - m_tss.fs = ds; - m_tss.gs = ds; - m_tss.ss = ss; - m_tss.cs = cs; - } - - m_tss.cr3 = page_directory().cr3(); - - if (is_ring0()) { - // FIXME: This memory is leaked. - // But uh, there's also no kernel process termination, so I guess it's not technically leaked... - dword stack_bottom = (dword)kmalloc_eternal(default_kernel_stack_size); - m_stack_top0 = (stack_bottom + default_kernel_stack_size) & 0xffffff8; - m_tss.esp = m_stack_top0; - } else { - // Ring3 processes need a separate stack for Ring0. - m_kernel_stack = kmalloc(default_kernel_stack_size); - m_stack_top0 = ((dword)m_kernel_stack + default_kernel_stack_size) & 0xffffff8; - m_tss.ss0 = 0x10; - m_tss.esp0 = m_stack_top0; - } if (fork_parent) { m_sid = fork_parent->m_sid; m_pgid = fork_parent->m_pgid; m_umask = fork_parent->m_umask; } - - // HACK: Ring2 SS in the TSS is the current PID. - m_tss.ss2 = m_pid; - m_far_ptr.offset = 0x98765432; } Process::~Process() { - { - InterruptDisabler disabler; - system.nprocess--; - } + dbgprintf("~Process{%p} name=%s pid=%d\n", this, m_name.characters(), pid()); + InterruptDisabler disabler; + system.nprocess--; - if (g_last_fpu_process == this) - g_last_fpu_process = nullptr; - - if (selector()) - gdt_free_entry(selector()); - - if (m_kernel_stack) { - kfree(m_kernel_stack); - m_kernel_stack = nullptr; - } - - if (m_kernel_stack_for_signal_handler) { - kfree(m_kernel_stack_for_signal_handler); - m_kernel_stack_for_signal_handler = nullptr; - } + delete m_main_thread; + m_main_thread = nullptr; } void Process::dump_regions() @@ -761,242 +651,6 @@ void Process::sys$exit(int status) ASSERT_NOT_REACHED(); } -void Process::terminate_due_to_signal(byte signal) -{ - ASSERT_INTERRUPTS_DISABLED(); - ASSERT(signal < 32); - dbgprintf("terminate_due_to_signal %s(%u) <- %u\n", name().characters(), pid(), signal); - m_termination_status = 0; - m_termination_signal = signal; - die(); -} - -void Process::send_signal(byte signal, Process* sender) -{ - ASSERT(signal < 32); - - if (sender) - dbgprintf("signal: %s(%u) sent %d to %s(%u)\n", sender->name().characters(), sender->pid(), signal, name().characters(), pid()); - else - dbgprintf("signal: kernel sent %d to %s(%u)\n", signal, name().characters(), pid()); - - InterruptDisabler disabler; - m_pending_signals |= 1 << signal; -} - -bool Process::has_unmasked_pending_signals() const -{ - return m_pending_signals & ~m_signal_mask; -} - -ShouldUnblockProcess Process::dispatch_one_pending_signal() -{ - ASSERT_INTERRUPTS_DISABLED(); - dword signal_candidates = m_pending_signals & ~m_signal_mask; - ASSERT(signal_candidates); - - byte signal = 0; - for (; signal < 32; ++signal) { - if (signal_candidates & (1 << signal)) { - break; - } - } - return dispatch_signal(signal); -} - -enum class DefaultSignalAction { - Terminate, - Ignore, - DumpCore, - Stop, - Continue, -}; - -DefaultSignalAction default_signal_action(byte signal) -{ - ASSERT(signal && signal < NSIG); - - switch (signal) { - case SIGHUP: - case SIGINT: - case SIGKILL: - case SIGPIPE: - case SIGALRM: - case SIGUSR1: - case SIGUSR2: - case SIGVTALRM: - case SIGSTKFLT: - case SIGIO: - case SIGPROF: - case SIGTERM: - case SIGPWR: - return DefaultSignalAction::Terminate; - case SIGCHLD: - case SIGURG: - case SIGWINCH: - return DefaultSignalAction::Ignore; - case SIGQUIT: - case SIGILL: - case SIGTRAP: - case SIGABRT: - case SIGBUS: - case SIGFPE: - case SIGSEGV: - case SIGXCPU: - case SIGXFSZ: - case SIGSYS: - return DefaultSignalAction::DumpCore; - case SIGCONT: - return DefaultSignalAction::Continue; - case SIGSTOP: - case SIGTSTP: - case SIGTTIN: - case SIGTTOU: - return DefaultSignalAction::Stop; - } - ASSERT_NOT_REACHED(); -} - -ShouldUnblockProcess Process::dispatch_signal(byte signal) -{ - ASSERT_INTERRUPTS_DISABLED(); - ASSERT(signal < 32); - -#ifdef SIGNAL_DEBUG - kprintf("dispatch_signal %s(%u) <- %u\n", name().characters(), pid(), signal); -#endif - - auto& action = m_signal_action_data[signal]; - // FIXME: Implement SA_SIGINFO signal handlers. - ASSERT(!(action.flags & SA_SIGINFO)); - - // Mark this signal as handled. - m_pending_signals &= ~(1 << signal); - - if (signal == SIGSTOP) { - set_state(Stopped); - return ShouldUnblockProcess::No; - } - - if (signal == SIGCONT && state() == Stopped) - set_state(Runnable); - - auto handler_laddr = action.handler_or_sigaction; - if (handler_laddr.is_null()) { - switch (default_signal_action(signal)) { - case DefaultSignalAction::Stop: - set_state(Stopped); - return ShouldUnblockProcess::No; - case DefaultSignalAction::DumpCore: - case DefaultSignalAction::Terminate: - terminate_due_to_signal(signal); - return ShouldUnblockProcess::No; - case DefaultSignalAction::Ignore: - return ShouldUnblockProcess::No; - case DefaultSignalAction::Continue: - return ShouldUnblockProcess::Yes; - } - ASSERT_NOT_REACHED(); - } - - if (handler_laddr.as_ptr() == SIG_IGN) { -#ifdef SIGNAL_DEBUG - kprintf("%s(%u) ignored signal %u\n", name().characters(), pid(), signal); -#endif - return ShouldUnblockProcess::Yes; - } - - dword old_signal_mask = m_signal_mask; - dword new_signal_mask = action.mask; - if (action.flags & SA_NODEFER) - new_signal_mask &= ~(1 << signal); - else - new_signal_mask |= 1 << signal; - - m_signal_mask |= new_signal_mask; - - Scheduler::prepare_to_modify_tss(*this); - - word ret_cs = m_tss.cs; - dword ret_eip = m_tss.eip; - dword ret_eflags = m_tss.eflags; - bool interrupting_in_kernel = (ret_cs & 3) == 0; - - ProcessPagingScope paging_scope(*this); - create_signal_trampolines_if_needed(); - - if (interrupting_in_kernel) { -#ifdef SIGNAL_DEBUG - kprintf("dispatch_signal to %s(%u) in state=%s with return to %w:%x\n", name().characters(), pid(), to_string(state()), ret_cs, ret_eip); -#endif - ASSERT(is_blocked()); - m_tss_to_resume_kernel = m_tss; -#ifdef SIGNAL_DEBUG - kprintf("resume tss pc: %w:%x stack: %w:%x flags: %x cr3: %x\n", m_tss_to_resume_kernel.cs, m_tss_to_resume_kernel.eip, m_tss_to_resume_kernel.ss, m_tss_to_resume_kernel.esp, m_tss_to_resume_kernel.eflags, m_tss_to_resume_kernel.cr3); -#endif - - if (!m_signal_stack_user_region) { - m_signal_stack_user_region = allocate_region(LinearAddress(), default_userspace_stack_size, "Signal stack (user)"); - ASSERT(m_signal_stack_user_region); - } - if (!m_kernel_stack_for_signal_handler) { - m_kernel_stack_for_signal_handler = kmalloc(default_kernel_stack_size); - ASSERT(m_kernel_stack_for_signal_handler); - } - m_tss.ss = 0x23; - m_tss.esp = m_signal_stack_user_region->laddr().offset(default_userspace_stack_size).get(); - m_tss.ss0 = 0x10; - m_tss.esp0 = (dword)m_kernel_stack_for_signal_handler + default_kernel_stack_size; - - push_value_on_stack(0); - } else { - push_value_on_stack(ret_eip); - push_value_on_stack(ret_eflags); - - // PUSHA - dword old_esp = m_tss.esp; - push_value_on_stack(m_tss.eax); - push_value_on_stack(m_tss.ecx); - push_value_on_stack(m_tss.edx); - push_value_on_stack(m_tss.ebx); - push_value_on_stack(old_esp); - push_value_on_stack(m_tss.ebp); - push_value_on_stack(m_tss.esi); - push_value_on_stack(m_tss.edi); - - // Align the stack. - m_tss.esp -= 12; - } - - // PUSH old_signal_mask - push_value_on_stack(old_signal_mask); - - m_tss.cs = 0x1b; - m_tss.ds = 0x23; - m_tss.es = 0x23; - m_tss.fs = 0x23; - m_tss.gs = 0x23; - m_tss.eip = handler_laddr.get(); - - // FIXME: Should we worry about the stack being 16 byte aligned when entering a signal handler? - push_value_on_stack(signal); - - if (interrupting_in_kernel) - push_value_on_stack(m_return_to_ring0_from_signal_trampoline.get()); - else - push_value_on_stack(m_return_to_ring3_from_signal_trampoline.get()); - - ASSERT((m_tss.esp % 16) == 0); - - // FIXME: This state is such a hack. It avoids trouble if 'current' is the process receiving a signal. - set_state(Skip1SchedulerPass); - -#ifdef SIGNAL_DEBUG - kprintf("signal: Okay, %s(%u) {%s} has been primed with signal handler %w:%x\n", name().characters(), pid(), to_string(state()), m_tss.cs, m_tss.eip); -#endif - return ShouldUnblockProcess::Yes; -} - void Process::create_signal_trampolines_if_needed() { if (!m_return_to_ring3_from_signal_trampoline.is_null()) @@ -1045,36 +699,30 @@ void Process::create_signal_trampolines_if_needed() int Process::sys$restore_signal_mask(dword mask) { - m_signal_mask = mask; + current->m_signal_mask = mask; return 0; } void Process::sys$sigreturn() { InterruptDisabler disabler; - Scheduler::prepare_to_modify_tss(*this); - m_tss = m_tss_to_resume_kernel; + Scheduler::prepare_to_modify_tss(*current); + current->m_tss = current->m_tss_to_resume_kernel; #ifdef SIGNAL_DEBUG kprintf("sys$sigreturn in %s(%u)\n", name().characters(), pid()); - kprintf(" -> resuming execution at %w:%x stack %w:%x flags %x cr3 %x\n", m_tss.cs, m_tss.eip, m_tss.ss, m_tss.esp, m_tss.eflags, m_tss.cr3); + auto& tss = current->tss(); + kprintf(" -> resuming execution at %w:%x stack %w:%x flags %x cr3 %x\n", tss.cs, tss.eip, tss.ss, tss.esp, tss.eflags, tss.cr3); #endif - set_state(Skip1SchedulerPass); + current->set_state(Thread::State::Skip1SchedulerPass); Scheduler::yield(); kprintf("sys$sigreturn failed in %s(%u)\n", name().characters(), pid()); ASSERT_NOT_REACHED(); } -void Process::push_value_on_stack(dword value) -{ - m_tss.esp -= 4; - dword* stack_ptr = (dword*)m_tss.esp; - *stack_ptr = value; -} - void Process::crash() { ASSERT_INTERRUPTS_DISABLED(); - ASSERT(state() != Dead); + ASSERT(!is_dead()); m_termination_signal = SIGSEGV; dump_regions(); @@ -1190,8 +838,8 @@ ssize_t Process::sys$write(int fd, const byte* data, ssize_t size) #ifdef IO_DEBUG dbgprintf("block write on %d\n", fd); #endif - m_blocked_fd = fd; - block(BlockedWrite); + current->m_blocked_fd = fd; + current->block(Thread::State::BlockedWrite); Scheduler::yield(); } ssize_t rc = descriptor->write(*this, (const byte*)data + nwritten, size - nwritten); @@ -1205,8 +853,8 @@ ssize_t Process::sys$write(int fd, const byte* data, ssize_t size) } if (rc == 0) break; - if (has_unmasked_pending_signals()) { - block(BlockedSignal); + if (current->has_unmasked_pending_signals()) { + current->block(Thread::State::BlockedSignal); Scheduler::yield(); if (nwritten == 0) return -EINTR; @@ -1216,8 +864,8 @@ ssize_t Process::sys$write(int fd, const byte* data, ssize_t size) } else { nwritten = descriptor->write(*this, (const byte*)data, size); } - if (has_unmasked_pending_signals()) { - block(BlockedSignal); + if (current->has_unmasked_pending_signals()) { + current->block(Thread::State::BlockedSignal); Scheduler::yield(); if (nwritten == 0) return -EINTR; @@ -1239,10 +887,10 @@ ssize_t Process::sys$read(int fd, byte* buffer, ssize_t size) return -EBADF; if (descriptor->is_blocking()) { if (!descriptor->can_read(*this)) { - m_blocked_fd = fd; - block(BlockedRead); + current->m_blocked_fd = fd; + current->block(Thread::State::BlockedRead); Scheduler::yield(); - if (m_was_interrupted_while_blocked) + if (current->m_was_interrupted_while_blocked) return -EINTR; } } @@ -1545,7 +1193,7 @@ int Process::sys$kill(pid_t pid, int signal) ASSERT(pid != -1); } if (pid == m_pid) { - send_signal(signal, this); + current->send_signal(signal, this); Scheduler::yield(); return 0; } @@ -1571,9 +1219,9 @@ int Process::sys$usleep(useconds_t usec) return 0; sleep(usec / 1000); - if (m_wakeup_time > system.uptime) { - ASSERT(m_was_interrupted_while_blocked); - dword ticks_left_until_original_wakeup_time = m_wakeup_time - system.uptime; + if (current->m_wakeup_time > system.uptime) { + ASSERT(current->m_was_interrupted_while_blocked); + dword ticks_left_until_original_wakeup_time = current->m_wakeup_time - system.uptime; return ticks_left_until_original_wakeup_time / TICKS_PER_SECOND; } return 0; @@ -1584,9 +1232,9 @@ int Process::sys$sleep(unsigned seconds) if (!seconds) return 0; sleep(seconds * TICKS_PER_SECOND); - if (m_wakeup_time > system.uptime) { - ASSERT(m_was_interrupted_while_blocked); - dword ticks_left_until_original_wakeup_time = m_wakeup_time - system.uptime; + if (current->m_wakeup_time > system.uptime) { + ASSERT(current->m_was_interrupted_while_blocked); + dword ticks_left_until_original_wakeup_time = current->m_wakeup_time - system.uptime; return ticks_left_until_original_wakeup_time / TICKS_PER_SECOND; } return 0; @@ -1657,7 +1305,7 @@ int Process::reap(Process& process) } dbgprintf("reap: %s(%u) {%s}\n", process.name().characters(), process.pid(), to_string(process.state())); - ASSERT(process.state() == Dead); + ASSERT(process.is_dead()); g_processes->remove(&process); delete &process; return exit_status; @@ -1686,7 +1334,7 @@ pid_t Process::sys$waitpid(pid_t waitee, int* wstatus, int options) pid_t reaped_pid = 0; InterruptDisabler disabler; for_each_child([&reaped_pid, &exit_status] (Process& process) { - if (process.state() == Dead) { + if (process.is_dead()) { reaped_pid = process.pid(); exit_status = reap(process); } @@ -1699,7 +1347,7 @@ pid_t Process::sys$waitpid(pid_t waitee, int* wstatus, int options) auto* waitee_process = Process::from_pid(waitee); if (!waitee_process) return -ECHILD; - if (waitee_process->state() == Dead) { + if (waitee_process->is_dead()) { exit_status = reap(*waitee_process); return waitee; } @@ -1707,65 +1355,22 @@ pid_t Process::sys$waitpid(pid_t waitee, int* wstatus, int options) } } - m_waitee_pid = waitee; - block(BlockedWait); + current->m_waitee_pid = waitee; + current->block(Thread::State::BlockedWait); Scheduler::yield(); - if (m_was_interrupted_while_blocked) + if (current->m_was_interrupted_while_blocked) return -EINTR; Process* waitee_process; { InterruptDisabler disabler; // NOTE: If waitee was -1, m_waitee will have been filled in by the scheduler. - waitee_process = Process::from_pid(m_waitee_pid); + waitee_process = Process::from_pid(current->m_waitee_pid); } ASSERT(waitee_process); exit_status = reap(*waitee_process); - return m_waitee_pid; + return current->m_waitee_pid; } -void Process::unblock() -{ - if (current == this) { - system.nblocked--; - m_state = Process::Running; - return; - } - ASSERT(m_state != Process::Runnable && m_state != Process::Running); - system.nblocked--; - m_state = Process::Runnable; -} - -void Process::snooze_until(Alarm& alarm) -{ - m_snoozing_alarm = &alarm; - block(Process::BlockedSnoozing); - Scheduler::yield(); -} - -void Process::block(Process::State new_state) -{ - if (state() != Process::Running) { - kprintf("Process::block: %s(%u) block(%u/%s) with state=%u/%s\n", name().characters(), pid(), new_state, to_string(new_state), state(), to_string(state())); - } - ASSERT(state() == Process::Running); - system.nblocked++; - m_was_interrupted_while_blocked = false; - set_state(new_state); -} - -void block(Process::State state) -{ - current->block(state); - Scheduler::yield(); -} - -void sleep(dword ticks) -{ - ASSERT(current->state() == Process::Running); - current->set_wakeup_time(system.uptime + ticks); - current->block(Process::BlockedSleep); - Scheduler::yield(); -} enum class KernelMemoryCheckResult { NotInsideKernelMemory, @@ -1981,20 +1586,20 @@ int Process::sys$sigprocmask(int how, const sigset_t* set, sigset_t* old_set) if (old_set) { if (!validate_write_typed(old_set)) return -EFAULT; - *old_set = m_signal_mask; + *old_set = current->m_signal_mask; } if (set) { if (!validate_read_typed(set)) return -EFAULT; switch (how) { case SIG_BLOCK: - m_signal_mask &= ~(*set); + current->m_signal_mask &= ~(*set); break; case SIG_UNBLOCK: - m_signal_mask |= *set; + current->m_signal_mask |= *set; break; case SIG_SETMASK: - m_signal_mask = *set; + current->m_signal_mask = *set; break; default: return -EINVAL; @@ -2007,18 +1612,10 @@ int Process::sys$sigpending(sigset_t* set) { if (!validate_write_typed(set)) return -EFAULT; - *set = m_pending_signals; + *set = current->m_pending_signals; return 0; } -void Process::set_default_signal_dispositions() -{ - // FIXME: Set up all the right default actions. See signal(7). - memset(&m_signal_action_data, 0, sizeof(m_signal_action_data)); - m_signal_action_data[SIGCHLD].handler_or_sigaction = LinearAddress((dword)SIG_IGN); - m_signal_action_data[SIGWINCH].handler_or_sigaction = LinearAddress((dword)SIG_IGN); -} - int Process::sys$sigaction(int signum, const sigaction* act, sigaction* old_act) { if (signum < 1 || signum >= 32 || signum == SIGKILL || signum == SIGSTOP) @@ -2026,7 +1623,7 @@ int Process::sys$sigaction(int signum, const sigaction* act, sigaction* old_act) if (!validate_read_typed(act)) return -EFAULT; InterruptDisabler disabler; // FIXME: This should use a narrower lock. Maybe a way to ignore signals temporarily? - auto& action = m_signal_action_data[signum]; + auto& action = current->m_signal_action_data[signum]; if (old_act) { if (!validate_write_typed(old_act)) return -EFAULT; @@ -2119,10 +1716,10 @@ int Process::sys$select(const Syscall::SC_select_params* params) (void)exceptfds; if (timeout) { - m_select_timeout = *timeout; - m_select_has_timeout = true; + current->m_select_timeout = *timeout; + current->m_select_has_timeout = true; } else { - m_select_has_timeout = false; + current->m_select_has_timeout = false; } if (nfds < 0) @@ -2147,22 +1744,22 @@ int Process::sys$select(const Syscall::SC_select_params* params) }; int error = 0; - error = transfer_fds(writefds, m_select_write_fds); + error = transfer_fds(writefds, current->m_select_write_fds); if (error) return error; - error = transfer_fds(readfds, m_select_read_fds); + error = transfer_fds(readfds, current->m_select_read_fds); if (error) return error; - error = transfer_fds(readfds, m_select_exceptional_fds); + error = transfer_fds(readfds, current->m_select_exceptional_fds); if (error) return error; #ifdef DEBUG_IO - dbgprintf("%s<%u> selecting on (read:%u, write:%u), timeout=%p\n", name().characters(), pid(), m_select_read_fds.size(), m_select_write_fds.size(), timeout); + dbgprintf("%s<%u> selecting on (read:%u, write:%u), timeout=%p\n", name().characters(), pid(), current->m_select_read_fds.size(), current->m_select_write_fds.size(), timeout); #endif if (!timeout || (timeout->tv_sec || timeout->tv_usec)) { - block(BlockedSelect); + current->block(Thread::State::BlockedSelect); Scheduler::yield(); } @@ -2171,7 +1768,7 @@ int Process::sys$select(const Syscall::SC_select_params* params) if (readfds) { memset(readfds, 0, sizeof(fd_set)); auto bitmap = Bitmap::wrap((byte*)readfds, FD_SETSIZE); - for (int fd : m_select_read_fds) { + for (int fd : current->m_select_read_fds) { auto* descriptor = file_descriptor(fd); if (!descriptor) continue; @@ -2185,7 +1782,7 @@ int Process::sys$select(const Syscall::SC_select_params* params) if (writefds) { memset(writefds, 0, sizeof(fd_set)); auto bitmap = Bitmap::wrap((byte*)writefds, FD_SETSIZE); - for (int fd : m_select_write_fds) { + for (int fd : current->m_select_write_fds) { auto* descriptor = file_descriptor(fd); if (!descriptor) continue; @@ -2206,17 +1803,17 @@ int Process::sys$poll(pollfd* fds, int nfds, int timeout) if (!validate_read_typed(fds)) return -EFAULT; - m_select_write_fds.clear_with_capacity(); - m_select_read_fds.clear_with_capacity(); + current->m_select_write_fds.clear_with_capacity(); + current->m_select_read_fds.clear_with_capacity(); for (int i = 0; i < nfds; ++i) { if (fds[i].events & POLLIN) - m_select_read_fds.append(fds[i].fd); + current->m_select_read_fds.append(fds[i].fd); if (fds[i].events & POLLOUT) - m_select_write_fds.append(fds[i].fd); + current->m_select_write_fds.append(fds[i].fd); } if (timeout < 0) { - block(BlockedSelect); + current->block(Thread::State::BlockedSelect); Scheduler::yield(); } @@ -2316,6 +1913,7 @@ int Process::sys$chown(const char* pathname, uid_t uid, gid_t gid) void Process::finalize() { ASSERT(current == g_finalizer); + dbgprintf("Finalizing Process %s(%u)\n", m_name.characters(), m_pid); m_fds.clear(); m_tty = nullptr; @@ -2323,7 +1921,8 @@ void Process::finalize() { InterruptDisabler disabler; if (auto* parent_process = Process::from_pid(m_ppid)) { - if (parent_process->m_signal_action_data[SIGCHLD].flags & SA_NOCLDWAIT) { + // FIXME(Thread): What should we do here? Should we look at all threads' signal actions? + if (parent_process->main_thread().m_signal_action_data[SIGCHLD].flags & SA_NOCLDWAIT) { // NOTE: If the parent doesn't care about this process, let it go. m_ppid = 0; } else { @@ -2331,14 +1930,18 @@ void Process::finalize() } } } - m_blocked_socket = nullptr; - - set_state(Dead); } void Process::die() { - set_state(Dying); + { + InterruptDisabler disabler; + for_each_thread([] (Thread& thread) { + if (thread.state() != Thread::State::Dead) + thread.set_state(Thread::State::Dying); + return IterationDecision::Continue; + }); + } if (!Scheduler::is_active()) Scheduler::pick_next_and_switch_now(); @@ -2376,31 +1979,6 @@ size_t Process::amount_shared() const return amount; } -void Process::finalize_dying_processes() -{ - Vector dying_processes; - { - InterruptDisabler disabler; - dying_processes.ensure_capacity(system.nprocess); - for (auto* process = g_processes->head(); process; process = process->next()) { - if (process->state() == Process::Dying) - dying_processes.append(process); - } - } - for (auto* process : dying_processes) - process->finalize(); -} - -bool Process::tick() -{ - ++m_ticks; - if (tss().cs & 3) - ++m_ticks_in_user; - else - ++m_ticks_in_kernel; - return --m_ticks_left; -} - int Process::sys$socket(int domain, int type, int protocol) { if (number_of_open_file_descriptors() >= m_max_open_file_descriptors) @@ -2515,19 +2093,6 @@ int Process::sys$connect(int sockfd, const sockaddr* address, socklen_t address_ return 0; } -KResult Process::wait_for_connect(Socket& socket) -{ - if (socket.is_connected()) - return KSuccess; - m_blocked_socket = socket; - block(BlockedConnect); - Scheduler::yield(); - m_blocked_socket = nullptr; - if (!socket.is_connected()) - return KResult(-ECONNREFUSED); - return KSuccess; -} - ssize_t Process::sys$sendto(const Syscall::SC_sendto_params* params) { if (!validate_read_typed(params)) @@ -2850,34 +2415,6 @@ int Process::sys$get_shared_buffer_size(int shared_buffer_id) return shared_buffer.size(); } -const char* to_string(Process::State state) -{ - switch (state) { - case Process::Invalid: return "Invalid"; - case Process::Runnable: return "Runnable"; - case Process::Running: return "Running"; - case Process::Dying: return "Dying"; - case Process::Dead: return "Dead"; - case Process::Stopped: return "Stopped"; - case Process::Skip1SchedulerPass: return "Skip1"; - case Process::Skip0SchedulerPasses: return "Skip0"; - case Process::BlockedSleep: return "Sleep"; - case Process::BlockedWait: return "Wait"; - case Process::BlockedRead: return "Read"; - case Process::BlockedWrite: return "Write"; - case Process::BlockedSignal: return "Signal"; - case Process::BlockedSelect: return "Select"; - case Process::BlockedLurking: return "Lurking"; - case Process::BlockedConnect: return "Connect"; - case Process::BlockedReceive: return "Receive"; - case Process::BeingInspected: return "Inspect"; - case Process::BlockedSnoozing: return "Snoozing"; - } - kprintf("to_string(Process::State): Invalid state: %u\n", state); - ASSERT_NOT_REACHED(); - return nullptr; -} - const char* to_string(Process::Priority priority) { switch (priority) { @@ -2889,3 +2426,29 @@ const char* to_string(Process::Priority priority) ASSERT_NOT_REACHED(); return nullptr; } + +void Process::terminate_due_to_signal(byte signal) +{ + ASSERT_INTERRUPTS_DISABLED(); + ASSERT(signal < 32); + dbgprintf("terminate_due_to_signal %s(%u) <- %u\n", name().characters(), pid(), signal); + m_termination_status = 0; + m_termination_signal = signal; + die(); +} + +void Process::send_signal(byte signal, Process* sender) +{ + // FIXME(Thread): Find the appropriate thread to deliver the signal to. + main_thread().send_signal(signal, sender); +} + +int Process::thread_count() const +{ + int count = 0; + for_each_thread([&count] (auto&) { + ++count; + return IterationDecision::Continue; + }); + return count; +} diff --git a/Kernel/Process.h b/Kernel/Process.h index 75f5a866378..20084c4e739 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -1,8 +1,6 @@ #pragma once #include "types.h" -#include "TSS.h" -#include "i386.h" #include "TTY.h" #include "Syscall.h" #include @@ -12,9 +10,9 @@ #include #include #include +#include #include -class Alarm; class FileDescriptor; class PageDirectory; class Region; @@ -31,26 +29,11 @@ struct CoolGlobals { extern CoolGlobals* g_cool_globals; #endif -enum class ShouldUnblockProcess { No = 0, Yes }; - -struct SignalActionData { - LinearAddress handler_or_sigaction; - dword mask { 0 }; - int flags { 0 }; - LinearAddress restorer; -}; - -struct DisplayInfo { - unsigned width; - unsigned height; - unsigned bpp; - unsigned pitch; -}; - void kgettimeofday(timeval&); class Process : public InlineLinkedListNode, public Weakable { friend class InlineLinkedListNode; + friend class Thread; public: static Process* create_kernel_process(String&& name, void (*entry)()); static Process* create_user_process(const String& path, uid_t, gid_t, pid_t ppid, int& error, Vector&& arguments = Vector(), Vector&& environment = Vector(), TTY* = nullptr); @@ -58,29 +41,6 @@ public: static Vector all_pids(); static Vector all_processes(); - static void finalize_dying_processes(); - - enum State { - Invalid = 0, - Runnable, - Running, - Skip1SchedulerPass, - Skip0SchedulerPasses, - Dying, - Dead, - Stopped, - BeingInspected, - BlockedLurking, - BlockedSleep, - BlockedWait, - BlockedRead, - BlockedWrite, - BlockedSignal, - BlockedSelect, - BlockedConnect, - BlockedReceive, - BlockedSnoozing, - }; enum Priority { LowPriority, @@ -93,19 +53,20 @@ public: Ring3 = 3, }; + // FIXME(Thread): Is this really how this should work? + bool is_dead() const { return main_thread().state() == Thread::State::Dead; } + + Thread::State state() const { return main_thread().state(); } + + Thread& main_thread() { return *m_main_thread; } + const Thread& main_thread() const { return *m_main_thread; } + bool is_ring0() const { return m_ring == Ring0; } bool is_ring3() const { return m_ring == Ring3; } - bool is_stopped() const { return m_state == Stopped; } - bool is_blocked() const - { - return m_state == BlockedSleep || m_state == BlockedWait || m_state == BlockedRead || m_state == BlockedWrite || m_state == BlockedSignal || m_state == BlockedSelect; - } PageDirectory& page_directory() { return *m_page_directory; } const PageDirectory& page_directory() const { return *m_page_directory; } - bool in_kernel() const { return (m_tss.cs & 0x03) == 0; } - static Process* from_pid(pid_t); void set_priority(Priority p) { m_priority = p; } @@ -115,10 +76,6 @@ public: pid_t pid() const { return m_pid; } pid_t sid() const { return m_sid; } pid_t pgid() const { return m_pgid; } - dword ticks() const { return m_ticks; } - word selector() const { return m_far_ptr.selector; } - TSS32& tss() { return m_tss; } - State state() const { return m_state; } uid_t uid() const { return m_uid; } gid_t gid() const { return m_gid; } const HashTable& gids() const { return m_gids; } @@ -130,34 +87,14 @@ public: bool in_group(gid_t) const; - const FarPtr& far_ptr() const { return m_far_ptr; } - FileDescriptor* file_descriptor(int fd); const FileDescriptor* file_descriptor(int fd) const; - void block(Process::State); - void unblock(); - - void set_wakeup_time(dword t) { m_wakeup_time = t; } - dword wakeup_time() const { return m_wakeup_time; } - - void snooze_until(Alarm&); - template static void for_each(Callback); template static void for_each_in_pgrp(pid_t, Callback); - template static void for_each_in_state(State, Callback); - template static void for_each_living(Callback); template void for_each_child(Callback); + template void for_each_thread(Callback) const; - bool tick(); - void set_ticks_left(dword t) { m_ticks_left = t; } - dword ticks_left() const { return m_ticks_left; } - - dword kernel_stack_base() const { return (dword)m_kernel_stack; }; - dword kernel_stack_for_signal_handler_base() const { return (dword)m_kernel_stack_for_signal_handler; }; - - void set_selector(word s) { m_far_ptr.selector = s; } - void set_state(State s) { m_state = s; } void die(); void finalize(); @@ -182,7 +119,6 @@ public: int sys$stat(const char*, stat*); int sys$lseek(int fd, off_t, int whence); int sys$kill(pid_t pid, int sig); - int sys$geterror() { return m_error; } [[noreturn]] void sys$exit(int status); [[noreturn]] void sys$sigreturn(); pid_t sys$waitpid(pid_t, int* wstatus, int options); @@ -249,8 +185,6 @@ public: int sys$seal_shared_buffer(int shared_buffer_id); int sys$get_shared_buffer_size(int shared_buffer_id); - KResult wait_for_connect(Socket&); - static void initialize(); [[noreturn]] void crash(); @@ -263,21 +197,12 @@ public: const Vector>& regions() const { return m_regions; } void dump_regions(); - void did_schedule() { ++m_times_scheduled; } - dword times_scheduled() const { return m_times_scheduled; } - dword m_ticks_in_user { 0 }; dword m_ticks_in_kernel { 0 }; dword m_ticks_in_user_for_dead_children { 0 }; dword m_ticks_in_kernel_for_dead_children { 0 }; - pid_t waitee_pid() const { return m_waitee_pid; } - - dword frame_ptr() const { return m_tss.ebp; } - dword stack_ptr() const { return m_tss.esp; } - dword stack_top() const { return m_tss.ss == 0x10 ? m_stack_top0 : m_stack_top3; } - bool validate_read_from_kernel(LinearAddress) const; bool validate_read(const void*, ssize_t) const; @@ -292,13 +217,6 @@ public: int number_of_open_file_descriptors() const; int max_open_file_descriptors() const { return m_max_open_file_descriptors; } - void send_signal(byte signal, Process* sender); - - ShouldUnblockProcess dispatch_one_pending_signal(); - ShouldUnblockProcess dispatch_signal(byte signal); - bool has_unmasked_pending_signals() const; - void terminate_due_to_signal(byte signal); - size_t amount_virtual() const; size_t amount_resident() const; size_t amount_shared() const; @@ -308,16 +226,18 @@ public: bool is_superuser() const { return m_euid == 0; } - FPUState& fpu_state() { return m_fpu_state; } - bool has_used_fpu() const { return m_has_used_fpu; } - void set_has_used_fpu(bool b) { m_has_used_fpu = b; } - Region* allocate_region_with_vmo(LinearAddress, size_t, Retained&&, size_t offset_in_vmo, String&& name, bool is_readable, bool is_writable); Region* allocate_file_backed_region(LinearAddress, size_t, RetainPtr&&, String&& name, bool is_readable, bool is_writable); Region* allocate_region(LinearAddress, size_t, String&& name, bool is_readable = true, bool is_writable = true, bool commit = true); bool deallocate_region(Region& region); - void set_blocked_socket(Socket* socket) { m_blocked_socket = socket; } + void set_being_inspected(bool b) { m_being_inspected = b; } + bool is_being_inspected() const { return m_being_inspected; } + + void terminate_due_to_signal(byte signal); + void send_signal(byte, Process* sender); + + int thread_count() const; private: friend class MemoryManager; @@ -327,22 +247,21 @@ private: Process(String&& name, uid_t, gid_t, pid_t ppid, RingLevel, RetainPtr&& cwd = nullptr, RetainPtr&& executable = nullptr, TTY* = nullptr, Process* fork_parent = nullptr); int do_exec(String path, Vector arguments, Vector environment); - void push_value_on_stack(dword); - void make_userspace_stack(Vector arguments, Vector environment); int alloc_fd(); - void set_default_signal_dispositions(); void disown_all_shared_buffers(); void create_signal_trampolines_if_needed(); + Thread* m_main_thread { nullptr }; + RetainPtr m_page_directory; Process* m_prev { nullptr }; Process* m_next { nullptr }; String m_name; - void (*m_entry)() { nullptr }; + pid_t m_pid { 0 }; uid_t m_uid { 0 }; gid_t m_gid { 0 }; @@ -350,17 +269,9 @@ private: gid_t m_egid { 0 }; pid_t m_sid { 0 }; pid_t m_pgid { 0 }; - dword m_ticks { 0 }; - dword m_ticks_left { 0 }; - dword m_stack_top0 { 0 }; - dword m_stack_top3 { 0 }; - FarPtr m_far_ptr; - State m_state { Invalid }; + Priority m_priority { NormalPriority }; - dword m_wakeup_time { 0 }; - TSS32 m_tss; - TSS32 m_tss_to_resume_kernel; - FPUState m_fpu_state; + struct FileDescriptorAndFlags { operator bool() const { return !!descriptor; } void clear() { descriptor = nullptr; flags = 0; } @@ -370,23 +281,8 @@ private: }; Vector m_fds; RingLevel m_ring { Ring0 }; - int m_error { 0 }; - void* m_kernel_stack { nullptr }; - void* m_kernel_stack_for_signal_handler { nullptr }; - dword m_times_scheduled { 0 }; - pid_t m_waitee_pid { -1 }; - int m_blocked_fd { -1 }; - Vector m_select_read_fds; - Vector m_select_write_fds; - Vector m_select_exceptional_fds; - timeval m_select_timeout; - bool m_select_has_timeout { false }; + int m_max_open_file_descriptors { 16 }; - SignalActionData m_signal_action_data[32]; - dword m_pending_signals { 0 }; - dword m_signal_mask { 0 }; - RetainPtr m_blocked_socket; - Alarm* m_snoozing_alarm { nullptr }; byte m_termination_status { 0 }; byte m_termination_signal { 0 }; @@ -409,34 +305,28 @@ private: pid_t m_ppid { 0 }; mode_t m_umask { 022 }; - bool m_was_interrupted_while_blocked { false }; - static void notify_waiters(pid_t waitee, int exit_status, int signal); HashTable m_gids; - Region* m_signal_stack_user_region { nullptr }; + bool m_being_inspected { false }; - RetainPtr m_display_framebuffer_region; - - bool m_has_used_fpu { false }; + int m_next_tid { 0 }; }; -extern Process* current; - class ProcessInspectionHandle { public: ProcessInspectionHandle(Process& process) : m_process(process) - , m_original_state(process.state()) { - if (&process != current) - m_process.set_state(Process::BeingInspected); + if (&process != ¤t->process()) { + ASSERT(!m_process.is_being_inspected()); + m_process.set_being_inspected(true); + } } - ~ProcessInspectionHandle() { - m_process.set_state(m_original_state); + m_process.set_being_inspected(false); } Process& process() { return m_process; } @@ -454,15 +344,10 @@ public: Process& operator*() { return m_process; } private: Process& m_process; - Process::State m_original_state { Process::Invalid }; }; -extern const char* to_string(Process::State); extern const char* to_string(Process::Priority); -extern void block(Process::State); -extern void sleep(dword ticks); - extern InlineLinkedList* g_processes; template @@ -492,6 +377,21 @@ inline void Process::for_each_child(Callback callback) } } +template +inline void Process::for_each_thread(Callback callback) const +{ + InterruptDisabler disabler; + pid_t my_pid = pid(); + for (auto* thread = g_threads->head(); thread;) { + auto* next_thread = thread->next(); + if (thread->pid() == my_pid) { + if (callback(*thread) == IterationDecision::Abort) + break; + } + thread = next_thread; + } +} + template inline void Process::for_each_in_pgrp(pid_t pgid, Callback callback) { @@ -506,30 +406,6 @@ inline void Process::for_each_in_pgrp(pid_t pgid, Callback callback) } } -template -inline void Process::for_each_in_state(State state, Callback callback) -{ - ASSERT_INTERRUPTS_DISABLED(); - for (auto* process = g_processes->head(); process;) { - auto* next_process = process->next(); - if (process->state() == state) - callback(*process); - process = next_process; - } -} - -template -inline void Process::for_each_living(Callback callback) -{ - ASSERT_INTERRUPTS_DISABLED(); - for (auto* process = g_processes->head(); process;) { - auto* next_process = process->next(); - if (process->state() != Process::Dead && process->state() != Process::Dying) - callback(*process); - process = next_process; - } -} - inline bool InodeMetadata::may_read(Process& process) const { return may_read(process.euid(), process.gids()); @@ -544,3 +420,8 @@ inline bool InodeMetadata::may_execute(Process& process) const { return may_execute(process.euid(), process.gids()); } + +inline int Thread::pid() const +{ + return m_process.pid(); +} diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp index 84cd0fcd04b..0bdcb544c2e 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -23,9 +23,9 @@ static dword time_slice_for(Process::Priority priority) ASSERT_NOT_REACHED(); } -Process* current; -Process* g_last_fpu_process; -Process* g_finalizer; +Thread* current; +Thread* g_last_fpu_thread; +Thread* g_finalizer; static Process* s_colonel_process; struct TaskRedirectionData { @@ -52,138 +52,144 @@ bool Scheduler::pick_next() if (!current) { // XXX: The first ever context_switch() goes to the idle process. // This to setup a reliable place we can return to. - return context_switch(*s_colonel_process); + return context_switch(s_colonel_process->main_thread()); } auto now_sec = RTC::now(); auto now_usec = (suseconds_t)((PIT::ticks_since_boot() % 1000) * 1000); - // Check and unblock processes whose wait conditions have been met. - Process::for_each([&] (Process& process) { - if (process.state() == Process::BlockedSleep) { - if (process.wakeup_time() <= system.uptime) - process.unblock(); - return true; + // Check and unblock threads whose wait conditions have been met. + Thread::for_each([&] (Thread& thread) { + auto& process = thread.process(); + +// dbgprintf("pick_next, checking on %s(%u:%u) in state %s\n", process.name().impl()->characters(), process.pid(), thread.tid(), to_string(thread.state())); + + if (thread.state() == Thread::BlockedSleep) { + if (thread.wakeup_time() <= system.uptime) + thread.unblock(); + return IterationDecision::Continue; } - if (process.state() == Process::BlockedWait) { - process.for_each_child([&process] (Process& child) { - if (child.state() != Process::Dead) + if (thread.state() == Thread::BlockedWait) { + process.for_each_child([&] (Process& child) { + if (child.state() != Thread::Dead) return true; - if (process.waitee_pid() == -1 || process.waitee_pid() == child.pid()) { - process.m_waitee_pid = child.pid(); - process.unblock(); + if (thread.waitee_pid() == -1 || thread.waitee_pid() == child.pid()) { + thread.m_waitee_pid = child.pid(); + thread.unblock(); return false; } return true; }); - return true; + return IterationDecision::Continue; } - if (process.state() == Process::BlockedRead) { - ASSERT(process.m_blocked_fd != -1); + if (thread.state() == Thread::BlockedRead) { + ASSERT(thread.m_blocked_fd != -1); // FIXME: Block until the amount of data wanted is available. - if (process.m_fds[process.m_blocked_fd].descriptor->can_read(process)) - process.unblock(); - return true; + if (process.m_fds[thread.m_blocked_fd].descriptor->can_read(process)) + thread.unblock(); + return IterationDecision::Continue; } - if (process.state() == Process::BlockedWrite) { - ASSERT(process.m_blocked_fd != -1); - if (process.m_fds[process.m_blocked_fd].descriptor->can_write(process)) - process.unblock(); - return true; + if (thread.state() == Thread::BlockedWrite) { + ASSERT(thread.m_blocked_fd != -1); + if (process.m_fds[thread.m_blocked_fd].descriptor->can_write(process)) + thread.unblock(); + return IterationDecision::Continue; } - if (process.state() == Process::BlockedConnect) { - ASSERT(process.m_blocked_socket); - if (process.m_blocked_socket->is_connected()) - process.unblock(); - return true; + if (thread.state() == Thread::BlockedConnect) { + ASSERT(thread.m_blocked_socket); + if (thread.m_blocked_socket->is_connected()) + thread.unblock(); + return IterationDecision::Continue; } - if (process.state() == Process::BlockedReceive) { - ASSERT(process.m_blocked_socket); - auto& socket = *process.m_blocked_socket; + if (thread.state() == Thread::BlockedReceive) { + ASSERT(thread.m_blocked_socket); + auto& socket = *thread.m_blocked_socket; // FIXME: Block until the amount of data wanted is available. bool timed_out = now_sec > socket.receive_deadline().tv_sec || (now_sec == socket.receive_deadline().tv_sec && now_usec >= socket.receive_deadline().tv_usec); if (timed_out || socket.can_read(SocketRole::None)) { - process.unblock(); - process.m_blocked_socket = nullptr; - return true; + thread.unblock(); + thread.m_blocked_socket = nullptr; + return IterationDecision::Continue; } - return true; + return IterationDecision::Continue; } - if (process.state() == Process::BlockedSelect) { - if (process.m_select_has_timeout) { - if (now_sec > process.m_select_timeout.tv_sec || (now_sec == process.m_select_timeout.tv_sec && now_usec >= process.m_select_timeout.tv_usec)) { - process.unblock(); - return true; + if (thread.state() == Thread::BlockedSelect) { + if (thread.m_select_has_timeout) { + if (now_sec > thread.m_select_timeout.tv_sec || (now_sec == thread.m_select_timeout.tv_sec && now_usec >= thread.m_select_timeout.tv_usec)) { + thread.unblock(); + return IterationDecision::Continue; } } - for (int fd : process.m_select_read_fds) { + for (int fd : thread.m_select_read_fds) { if (process.m_fds[fd].descriptor->can_read(process)) { - process.unblock(); - return true; + thread.unblock(); + return IterationDecision::Continue; } } - for (int fd : process.m_select_write_fds) { + for (int fd : thread.m_select_write_fds) { if (process.m_fds[fd].descriptor->can_write(process)) { - process.unblock(); - return true; + thread.unblock(); + return IterationDecision::Continue; } } - return true; + return IterationDecision::Continue; } - if (process.state() == Process::BlockedSnoozing) { - if (process.m_snoozing_alarm->is_ringing()) { - process.m_snoozing_alarm = nullptr; - process.unblock(); + if (thread.state() == Thread::BlockedSnoozing) { + if (thread.m_snoozing_alarm->is_ringing()) { + thread.m_snoozing_alarm = nullptr; + thread.unblock(); } - return true; + return IterationDecision::Continue; } - if (process.state() == Process::Skip1SchedulerPass) { - process.set_state(Process::Skip0SchedulerPasses); - return true; + if (thread.state() == Thread::Skip1SchedulerPass) { + thread.set_state(Thread::Skip0SchedulerPasses); + return IterationDecision::Continue; } - if (process.state() == Process::Skip0SchedulerPasses) { - process.set_state(Process::Runnable); - return true; + if (thread.state() == Thread::Skip0SchedulerPasses) { + thread.set_state(Thread::Runnable); + return IterationDecision::Continue; } - if (process.state() == Process::Dead) { - if (current != &process && (!process.ppid() || !Process::from_pid(process.ppid()))) { + if (thread.state() == Thread::Dying) { + ASSERT(g_finalizer); + if (g_finalizer->state() == Thread::BlockedLurking) + g_finalizer->unblock(); + return IterationDecision::Continue; + } + + return IterationDecision::Continue; + }); + + Process::for_each([&] (Process& process) { + if (process.is_dead()) { + if (current != &process.main_thread() && (!process.ppid() || !Process::from_pid(process.ppid()))) { auto name = process.name(); auto pid = process.pid(); auto exit_status = Process::reap(process); dbgprintf("reaped unparented process %s(%u), exit status: %u\n", name.characters(), pid, exit_status); } - return true; } - - if (process.state() == Process::Dying) { - ASSERT(g_finalizer); - if (g_finalizer->state() == Process::BlockedLurking) - g_finalizer->unblock(); - return true; - } - return true; }); // Dispatch any pending signals. // FIXME: Do we really need this to be a separate pass over the process list? - Process::for_each_living([] (auto& process) { - if (!process.has_unmasked_pending_signals()) + Thread::for_each_living([] (Thread& thread) { + if (!thread.has_unmasked_pending_signals()) return true; // FIXME: It would be nice if the Scheduler didn't have to worry about who is "current" // For now, avoid dispatching signals to "current" and do it in a scheduling pass // while some other process is interrupted. Otherwise a mess will be made. - if (&process == current) + if (&thread == current) return true; // We know how to interrupt blocked processes, but if they are just executing // at some random point in the kernel, let them continue. They'll be in userspace @@ -191,60 +197,61 @@ bool Scheduler::pick_next() // FIXME: Maybe we could check when returning from a syscall if there's a pending // signal and dispatch it then and there? Would that be doable without the // syscall effectively being "interrupted" despite having completed? - if (process.in_kernel() && !process.is_blocked() && !process.is_stopped()) + if (thread.in_kernel() && !thread.is_blocked() && !thread.is_stopped()) return true; // NOTE: dispatch_one_pending_signal() may unblock the process. - bool was_blocked = process.is_blocked(); - if (process.dispatch_one_pending_signal() == ShouldUnblockProcess::No) + bool was_blocked = thread.is_blocked(); + if (thread.dispatch_one_pending_signal() == ShouldUnblockThread::No) return true; if (was_blocked) { - dbgprintf("Unblock %s(%u) due to signal\n", process.name().characters(), process.pid()); - process.m_was_interrupted_while_blocked = true; - process.unblock(); + dbgprintf("Unblock %s(%u) due to signal\n", thread.process().name().characters(), thread.pid()); + thread.m_was_interrupted_while_blocked = true; + thread.unblock(); } return true; }); #ifdef SCHEDULER_DEBUG dbgprintf("Scheduler choices:\n"); - for (auto* process = g_processes->head(); process; process = process->next()) { - //if (process->state() == Process::BlockedWait || process->state() == Process::BlockedSleep) + for (auto* thread = g_threads->head(); thread; thread = thread->next()) { + //if (process->state() == Thread::BlockedWait || process->state() == Thread::BlockedSleep) // continue; - dbgprintf("[K%x] % 12s %s(%u) @ %w:%x\n", process, to_string(process->state()), process->name().characters(), process->pid(), process->tss().cs, process->tss().eip); + auto* process = &thread->process(); + dbgprintf("[K%x] % 12s %s(%u:%u) @ %w:%x\n", process, to_string(thread->state()), process->name().characters(), process->pid(), thread->tid(), thread->tss().cs, thread->tss().eip); } #endif - auto* previous_head = g_processes->head(); + auto* previous_head = g_threads->head(); for (;;) { // Move head to tail. - g_processes->append(g_processes->remove_head()); - auto* process = g_processes->head(); + g_threads->append(g_threads->remove_head()); + auto* thread = g_threads->head(); - if (process->state() == Process::Runnable || process->state() == Process::Running) { + if (!thread->process().is_being_inspected() && (thread->state() == Thread::Runnable || thread->state() == Thread::Running)) { #ifdef SCHEDULER_DEBUG - kprintf("switch to %s(%u) @ %w:%x\n", process->name().characters(), process->pid(), process->tss().cs, process->tss().eip); + kprintf("switch to %s(%u:%u) @ %w:%x\n", thread->process().name().characters(), thread->process().pid(), thread->tid(), thread->tss().cs, thread->tss().eip); #endif - return context_switch(*process); + return context_switch(*thread); } - if (process == previous_head) { + if (thread == previous_head) { // Back at process_head, nothing wants to run. Send in the colonel! - return context_switch(*s_colonel_process); + return context_switch(s_colonel_process->main_thread()); } } } -bool Scheduler::donate_to(Process* beneficiary, const char* reason) +bool Scheduler::donate_to(Thread* beneficiary, const char* reason) { (void)reason; unsigned ticks_left = current->ticks_left(); - if (!beneficiary || beneficiary->state() != Process::Runnable || ticks_left <= 1) { + if (!beneficiary || beneficiary->state() != Thread::Runnable || ticks_left <= 1) { return yield(); } - unsigned ticks_to_donate = min(ticks_left - 1, time_slice_for(beneficiary->priority())); + unsigned ticks_to_donate = min(ticks_left - 1, time_slice_for(beneficiary->process().priority())); #ifdef SCHEDULER_DEBUG - dbgprintf("%s(%u) donating %u ticks to %s(%u), reason=%s\n", current->name().characters(), current->pid(), ticks_to_donate, beneficiary->name().characters(), beneficiary->pid(), reason); + dbgprintf("%s(%u:%u) donating %u ticks to %s(%u:%u), reason=%s\n", current->process().name().characters(), current->pid(), current->tid(), ticks_to_donate, beneficiary->process().name().characters(), beneficiary->pid(), beneficiary->tid(), reason); #endif context_switch(*beneficiary); beneficiary->set_ticks_left(ticks_to_donate); @@ -256,12 +263,12 @@ bool Scheduler::yield() { InterruptDisabler disabler; ASSERT(current); - //dbgprintf("%s<%u> yield()\n", current->name().characters(), current->pid()); +// dbgprintf("%s(%u:%u) yield()\n", current->process().name().characters(), current->pid(), current->tid()); if (!pick_next()) return 1; - //dbgprintf("yield() jumping to new process: %x (%s)\n", current->far_ptr().selector, current->name().characters()); +// dbgprintf("yield() jumping to new process: sel=%x, %s(%u:%u)\n", current->far_ptr().selector, current->process().name().characters(), current->pid(), current->tid()); switch_now(); return 0; } @@ -284,39 +291,44 @@ void Scheduler::switch_now() ); } -bool Scheduler::context_switch(Process& process) +bool Scheduler::context_switch(Thread& thread) { - process.set_ticks_left(time_slice_for(process.priority())); - process.did_schedule(); + thread.set_ticks_left(time_slice_for(thread.process().priority())); - if (current == &process) + // FIXME(Thread): This is such a hack. + if (&thread == &s_colonel_process->main_thread()) + thread.set_ticks_left(1); + + thread.did_schedule(); + + if (current == &thread) return false; if (current) { // If the last process hasn't blocked (still marked as running), // mark it as runnable for the next round. - if (current->state() == Process::Running) - current->set_state(Process::Runnable); + if (current->state() == Thread::Running) + current->set_state(Thread::Runnable); #ifdef LOG_EVERY_CONTEXT_SWITCH - dbgprintf("Scheduler: %s(%u) -> %s(%u) %w:%x\n", - current->name().characters(), current->pid(), - process.name().characters(), process.pid(), - process.tss().cs, process.tss().eip); + dbgprintf("Scheduler: %s(%u:%u) -> %s(%u:%u) %w:%x\n", + current->process().name().characters(), current->process().pid(), current->tid(), + thread.process().name().characters(), thread.process().pid(), thread.tid(), + thread.tss().cs, thread.tss().eip); #endif } - current = &process; - process.set_state(Process::Running); + current = &thread; + thread.set_state(Thread::Running); #ifdef COOL_GLOBALS - g_cool_globals->current_pid = process.pid(); + g_cool_globals->current_pid = thread.process().pid(); #endif - if (!process.selector()) { - process.set_selector(gdt_alloc_entry()); - auto& descriptor = get_gdt_entry(process.selector()); - descriptor.set_base(&process.tss()); + if (!thread.selector()) { + thread.set_selector(gdt_alloc_entry()); + auto& descriptor = get_gdt_entry(thread.selector()); + descriptor.set_base(&thread.tss()); descriptor.set_limit(0xffff); descriptor.dpl = 0; descriptor.segment_present = 1; @@ -326,7 +338,7 @@ bool Scheduler::context_switch(Process& process) descriptor.descriptor_type = 0; } - auto& descriptor = get_gdt_entry(process.selector()); + auto& descriptor = get_gdt_entry(thread.selector()); descriptor.type = 11; // Busy TSS flush_gdt(); return true; @@ -355,12 +367,12 @@ void Scheduler::prepare_for_iret_to_new_process() load_task_register(s_redirection.selector); } -void Scheduler::prepare_to_modify_tss(Process& process) +void Scheduler::prepare_to_modify_tss(Thread& thread) { // This ensures that a currently running process modifying its own TSS // in order to yield() and end up somewhere else doesn't just end up // right after the yield(). - if (current == &process) + if (current == &thread) load_task_register(s_redirection.selector); } diff --git a/Kernel/Scheduler.h b/Kernel/Scheduler.h index 918b553b3a0..04c80e6bd82 100644 --- a/Kernel/Scheduler.h +++ b/Kernel/Scheduler.h @@ -3,11 +3,12 @@ #include class Process; +class Thread; struct RegisterDump; -extern Process* current; -extern Process* g_last_fpu_process; -extern Process* g_finalizer; +extern Thread* current; +extern Thread* g_last_fpu_thread; +extern Thread* g_finalizer; class Scheduler { public: @@ -17,9 +18,9 @@ public: static void pick_next_and_switch_now(); static void switch_now(); static bool yield(); - static bool donate_to(Process*, const char* reason); - static bool context_switch(Process&); - static void prepare_to_modify_tss(Process&); + static bool donate_to(Thread*, const char* reason); + static bool context_switch(Thread&); + static void prepare_to_modify_tss(Thread&); static Process* colonel(); static bool is_active(); private: diff --git a/Kernel/SlavePTY.cpp b/Kernel/SlavePTY.cpp index ddbbcc31760..93d2a0b6529 100644 --- a/Kernel/SlavePTY.cpp +++ b/Kernel/SlavePTY.cpp @@ -8,8 +8,8 @@ SlavePTY::SlavePTY(MasterPTY& master, unsigned index) , m_master(master) , m_index(index) { - set_uid(current->uid()); - set_gid(current->gid()); + set_uid(current->process().uid()); + set_gid(current->process().gid()); DevPtsFS::the().register_slave_pty(*this); set_size(80, 25); } diff --git a/Kernel/StdLib.cpp b/Kernel/StdLib.cpp index ed18781b726..20c07f93411 100644 --- a/Kernel/StdLib.cpp +++ b/Kernel/StdLib.cpp @@ -35,6 +35,18 @@ void* memcpy(void* dest_ptr, const void* src_ptr, size_t n) return dest_ptr; } +void* memmove(void* dest, const void* src, size_t n) +{ + if (dest < src) + return memcpy(dest, src, n); + + byte *pd = (byte*)dest; + const byte *ps = (const byte*)src; + for (pd += n, ps += n; n--;) + *--pd = *--ps; + return dest; +} + char* strcpy(char* dest, const char *src) { auto* dest_ptr = dest; diff --git a/Kernel/StdLib.h b/Kernel/StdLib.h index 3af0ba98d79..88a40eed6e5 100644 --- a/Kernel/StdLib.h +++ b/Kernel/StdLib.h @@ -15,6 +15,7 @@ void* memset(void*, int, size_t); char *strdup(const char*); int memcmp(const void*, const void*, size_t); char* strrchr(const char* str, int ch); +void* memmove(void* dest, const void* src, size_t n); inline word ntohs(word w) { return (w & 0xff) << 8 | ((w >> 8) & 0xff); } inline word htons(word w) { return (w & 0xff) << 8 | ((w >> 8) & 0xff); } diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index b952aa598da..ffb59b53866 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -61,182 +61,182 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2, Console::the().put_char(arg1 & 0xff); break; case Syscall::SC_sleep: - return current->sys$sleep((unsigned)arg1); + return current->process().sys$sleep((unsigned)arg1); case Syscall::SC_usleep: - return current->sys$usleep((unsigned)arg1); + return current->process().sys$usleep((unsigned)arg1); case Syscall::SC_gettimeofday: - return current->sys$gettimeofday((timeval*)arg1); + return current->process().sys$gettimeofday((timeval*)arg1); case Syscall::SC_get_dir_entries: - return current->sys$get_dir_entries((int)arg1, (void*)arg2, (size_t)arg3); + return current->process().sys$get_dir_entries((int)arg1, (void*)arg2, (size_t)arg3); case Syscall::SC_lstat: - return current->sys$lstat((const char*)arg1, (stat*)arg2); + return current->process().sys$lstat((const char*)arg1, (stat*)arg2); case Syscall::SC_stat: - return current->sys$stat((const char*)arg1, (stat*)arg2); + return current->process().sys$stat((const char*)arg1, (stat*)arg2); case Syscall::SC_getcwd: - return current->sys$getcwd((char*)arg1, (size_t)arg2); + return current->process().sys$getcwd((char*)arg1, (size_t)arg2); case Syscall::SC_open: - return current->sys$open((const char*)arg1, (int)arg2, (mode_t)arg3); + return current->process().sys$open((const char*)arg1, (int)arg2, (mode_t)arg3); case Syscall::SC_write: - return current->sys$write((int)arg1, (const byte*)arg2, (ssize_t)arg3); + return current->process().sys$write((int)arg1, (const byte*)arg2, (ssize_t)arg3); case Syscall::SC_close: - return current->sys$close((int)arg1); + return current->process().sys$close((int)arg1); case Syscall::SC_read: - return current->sys$read((int)arg1, (byte*)arg2, (ssize_t)arg3); + return current->process().sys$read((int)arg1, (byte*)arg2, (ssize_t)arg3); case Syscall::SC_lseek: - return current->sys$lseek((int)arg1, (off_t)arg2, (int)arg3); + return current->process().sys$lseek((int)arg1, (off_t)arg2, (int)arg3); case Syscall::SC_kill: - return current->sys$kill((pid_t)arg1, (int)arg2); + return current->process().sys$kill((pid_t)arg1, (int)arg2); case Syscall::SC_getuid: - return current->sys$getuid(); + return current->process().sys$getuid(); case Syscall::SC_getgid: - return current->sys$getgid(); + return current->process().sys$getgid(); case Syscall::SC_getpid: - return current->sys$getpid(); + return current->process().sys$getpid(); case Syscall::SC_getppid: - return current->sys$getppid(); + return current->process().sys$getppid(); case Syscall::SC_waitpid: - return current->sys$waitpid((pid_t)arg1, (int*)arg2, (int)arg3); + return current->process().sys$waitpid((pid_t)arg1, (int*)arg2, (int)arg3); case Syscall::SC_mmap: - return (dword)current->sys$mmap((const SC_mmap_params*)arg1); + return (dword)current->process().sys$mmap((const SC_mmap_params*)arg1); case Syscall::SC_select: - return current->sys$select((const SC_select_params*)arg1); + return current->process().sys$select((const SC_select_params*)arg1); case Syscall::SC_poll: - return current->sys$poll((pollfd*)arg1, (int)arg2, (int)arg3); + return current->process().sys$poll((pollfd*)arg1, (int)arg2, (int)arg3); case Syscall::SC_munmap: - return current->sys$munmap((void*)arg1, (size_t)arg2); + return current->process().sys$munmap((void*)arg1, (size_t)arg2); case Syscall::SC_gethostname: - return current->sys$gethostname((char*)arg1, (size_t)arg2); + return current->process().sys$gethostname((char*)arg1, (size_t)arg2); case Syscall::SC_exit: cli(); - current->sys$exit((int)arg1); + current->process().sys$exit((int)arg1); ASSERT_NOT_REACHED(); return 0; case Syscall::SC_chdir: - return current->sys$chdir((const char*)arg1); + return current->process().sys$chdir((const char*)arg1); case Syscall::SC_uname: - return current->sys$uname((utsname*)arg1); + return current->process().sys$uname((utsname*)arg1); case Syscall::SC_set_mmap_name: - return current->sys$set_mmap_name((void*)arg1, (size_t)arg2, (const char*)arg3); + return current->process().sys$set_mmap_name((void*)arg1, (size_t)arg2, (const char*)arg3); case Syscall::SC_readlink: - return current->sys$readlink((const char*)arg1, (char*)arg2, (size_t)arg3); + return current->process().sys$readlink((const char*)arg1, (char*)arg2, (size_t)arg3); case Syscall::SC_ttyname_r: - return current->sys$ttyname_r((int)arg1, (char*)arg2, (size_t)arg3); + return current->process().sys$ttyname_r((int)arg1, (char*)arg2, (size_t)arg3); case Syscall::SC_ptsname_r: - return current->sys$ptsname_r((int)arg1, (char*)arg2, (size_t)arg3); + return current->process().sys$ptsname_r((int)arg1, (char*)arg2, (size_t)arg3); case Syscall::SC_setsid: - return current->sys$setsid(); + return current->process().sys$setsid(); case Syscall::SC_getsid: - return current->sys$getsid((pid_t)arg1); + return current->process().sys$getsid((pid_t)arg1); case Syscall::SC_setpgid: - return current->sys$setpgid((pid_t)arg1, (pid_t)arg2); + return current->process().sys$setpgid((pid_t)arg1, (pid_t)arg2); case Syscall::SC_getpgid: - return current->sys$getpgid((pid_t)arg1); + return current->process().sys$getpgid((pid_t)arg1); case Syscall::SC_getpgrp: - return current->sys$getpgrp(); + return current->process().sys$getpgrp(); case Syscall::SC_fork: - return current->sys$fork(regs); + return current->process().sys$fork(regs); case Syscall::SC_execve: - return current->sys$execve((const char*)arg1, (const char**)arg2, (const char**)arg3); + return current->process().sys$execve((const char*)arg1, (const char**)arg2, (const char**)arg3); case Syscall::SC_geteuid: - return current->sys$geteuid(); + return current->process().sys$geteuid(); case Syscall::SC_getegid: - return current->sys$getegid(); + return current->process().sys$getegid(); case Syscall::SC_isatty: - return current->sys$isatty((int)arg1); + return current->process().sys$isatty((int)arg1); case Syscall::SC_getdtablesize: - return current->sys$getdtablesize(); + return current->process().sys$getdtablesize(); case Syscall::SC_dup: - return current->sys$dup((int)arg1); + return current->process().sys$dup((int)arg1); case Syscall::SC_dup2: - return current->sys$dup2((int)arg1, (int)arg2); + return current->process().sys$dup2((int)arg1, (int)arg2); case Syscall::SC_sigaction: - return current->sys$sigaction((int)arg1, (const sigaction*)arg2, (sigaction*)arg3); + return current->process().sys$sigaction((int)arg1, (const sigaction*)arg2, (sigaction*)arg3); case Syscall::SC_umask: - return current->sys$umask((mode_t)arg1); + return current->process().sys$umask((mode_t)arg1); case Syscall::SC_getgroups: - return current->sys$getgroups((ssize_t)arg1, (gid_t*)arg2); + return current->process().sys$getgroups((ssize_t)arg1, (gid_t*)arg2); case Syscall::SC_setgroups: - return current->sys$setgroups((ssize_t)arg1, (const gid_t*)arg2); + return current->process().sys$setgroups((ssize_t)arg1, (const gid_t*)arg2); case Syscall::SC_sigreturn: - current->sys$sigreturn(); + current->process().sys$sigreturn(); ASSERT_NOT_REACHED(); return 0; case Syscall::SC_sigprocmask: - return current->sys$sigprocmask((int)arg1, (const sigset_t*)arg2, (sigset_t*)arg3); + return current->process().sys$sigprocmask((int)arg1, (const sigset_t*)arg2, (sigset_t*)arg3); case Syscall::SC_pipe: - return current->sys$pipe((int*)arg1); + return current->process().sys$pipe((int*)arg1); case Syscall::SC_killpg: - return current->sys$killpg((int)arg1, (int)arg2); + return current->process().sys$killpg((int)arg1, (int)arg2); case Syscall::SC_setuid: - return current->sys$setuid((uid_t)arg1); + return current->process().sys$setuid((uid_t)arg1); case Syscall::SC_setgid: - return current->sys$setgid((gid_t)arg1); + return current->process().sys$setgid((gid_t)arg1); case Syscall::SC_alarm: - return current->sys$alarm((unsigned)arg1); + return current->process().sys$alarm((unsigned)arg1); case Syscall::SC_access: - return current->sys$access((const char*)arg1, (int)arg2); + return current->process().sys$access((const char*)arg1, (int)arg2); case Syscall::SC_fcntl: - return current->sys$fcntl((int)arg1, (int)arg2, (dword)arg3); + return current->process().sys$fcntl((int)arg1, (int)arg2, (dword)arg3); case Syscall::SC_ioctl: - return current->sys$ioctl((int)arg1, (unsigned)arg2, (unsigned)arg3); + return current->process().sys$ioctl((int)arg1, (unsigned)arg2, (unsigned)arg3); case Syscall::SC_fstat: - return current->sys$fstat((int)arg1, (stat*)arg2); + return current->process().sys$fstat((int)arg1, (stat*)arg2); case Syscall::SC_mkdir: - return current->sys$mkdir((const char*)arg1, (mode_t)arg2); + return current->process().sys$mkdir((const char*)arg1, (mode_t)arg2); case Syscall::SC_times: - return current->sys$times((tms*)arg1); + return current->process().sys$times((tms*)arg1); case Syscall::SC_utime: - return current->sys$utime((const char*)arg1, (const utimbuf*)arg2); + return current->process().sys$utime((const char*)arg1, (const utimbuf*)arg2); case Syscall::SC_sync: return sync(); case Syscall::SC_link: - return current->sys$link((const char*)arg1, (const char*)arg2); + return current->process().sys$link((const char*)arg1, (const char*)arg2); case Syscall::SC_unlink: - return current->sys$unlink((const char*)arg1); + return current->process().sys$unlink((const char*)arg1); case Syscall::SC_symlink: - return current->sys$symlink((const char*)arg1, (const char*)arg2); + return current->process().sys$symlink((const char*)arg1, (const char*)arg2); case Syscall::SC_read_tsc: - return current->sys$read_tsc((dword*)arg1, (dword*)arg2); + return current->process().sys$read_tsc((dword*)arg1, (dword*)arg2); case Syscall::SC_rmdir: - return current->sys$rmdir((const char*)arg1); + return current->process().sys$rmdir((const char*)arg1); case Syscall::SC_chmod: - return current->sys$chmod((const char*)arg1, (mode_t)arg2); + return current->process().sys$chmod((const char*)arg1, (mode_t)arg2); case Syscall::SC_fchmod: - return current->sys$fchmod((int)arg1, (mode_t)arg2); + return current->process().sys$fchmod((int)arg1, (mode_t)arg2); case Syscall::SC_socket: - return current->sys$socket((int)arg1, (int)arg2, (int)arg3); + return current->process().sys$socket((int)arg1, (int)arg2, (int)arg3); case Syscall::SC_bind: - return current->sys$bind((int)arg1, (const sockaddr*)arg2, (socklen_t)arg3); + return current->process().sys$bind((int)arg1, (const sockaddr*)arg2, (socklen_t)arg3); case Syscall::SC_listen: - return current->sys$listen((int)arg1, (int)arg2); + return current->process().sys$listen((int)arg1, (int)arg2); case Syscall::SC_accept: - return current->sys$accept((int)arg1, (sockaddr*)arg2, (socklen_t*)arg3); + return current->process().sys$accept((int)arg1, (sockaddr*)arg2, (socklen_t*)arg3); case Syscall::SC_connect: - return current->sys$connect((int)arg1, (const sockaddr*)arg2, (socklen_t)arg3); + return current->process().sys$connect((int)arg1, (const sockaddr*)arg2, (socklen_t)arg3); case Syscall::SC_create_shared_buffer: - return current->sys$create_shared_buffer((pid_t)arg1, (size_t)arg2, (void**)arg3); + return current->process().sys$create_shared_buffer((pid_t)arg1, (size_t)arg2, (void**)arg3); case Syscall::SC_get_shared_buffer: - return (dword)current->sys$get_shared_buffer((int)arg1); + return (dword)current->process().sys$get_shared_buffer((int)arg1); case Syscall::SC_release_shared_buffer: - return current->sys$release_shared_buffer((int)arg1); + return current->process().sys$release_shared_buffer((int)arg1); case Syscall::SC_chown: - return current->sys$chown((const char*)arg1, (uid_t)arg2, (gid_t)arg3); + return current->process().sys$chown((const char*)arg1, (uid_t)arg2, (gid_t)arg3); case Syscall::SC_restore_signal_mask: - return current->sys$restore_signal_mask((dword)arg1); + return current->process().sys$restore_signal_mask((dword)arg1); case Syscall::SC_seal_shared_buffer: - return current->sys$seal_shared_buffer((int)arg1); + return current->process().sys$seal_shared_buffer((int)arg1); case Syscall::SC_get_shared_buffer_size: - return current->sys$get_shared_buffer_size((int)arg1); + return current->process().sys$get_shared_buffer_size((int)arg1); case Syscall::SC_sendto: - return current->sys$sendto((const SC_sendto_params*)arg1); + return current->process().sys$sendto((const SC_sendto_params*)arg1); case Syscall::SC_recvfrom: - return current->sys$recvfrom((const SC_recvfrom_params*)arg1); + return current->process().sys$recvfrom((const SC_recvfrom_params*)arg1); case Syscall::SC_getsockopt: - return current->sys$getsockopt((const SC_getsockopt_params*)arg1); + return current->process().sys$getsockopt((const SC_getsockopt_params*)arg1); case Syscall::SC_setsockopt: - return current->sys$setsockopt((const SC_setsockopt_params*)arg1); + return current->process().sys$setsockopt((const SC_setsockopt_params*)arg1); default: - kprintf("<%u> int0x82: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); + kprintf("<%u> int0x82: Unknown function %u requested {%x, %x, %x}\n", current->process().pid(), function, arg1, arg2, arg3); break; } return 0; diff --git a/Kernel/TCPSocket.cpp b/Kernel/TCPSocket.cpp index 159c8388275..2f7f759914b 100644 --- a/Kernel/TCPSocket.cpp +++ b/Kernel/TCPSocket.cpp @@ -167,7 +167,7 @@ KResult TCPSocket::protocol_connect() m_state = State::Connecting; current->set_blocked_socket(this); - block(Process::BlockedConnect); + block(Thread::BlockedConnect); Scheduler::yield(); ASSERT(is_connected()); diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp new file mode 100644 index 00000000000..e3cd3d4d7a1 --- /dev/null +++ b/Kernel/Thread.cpp @@ -0,0 +1,511 @@ +#include +#include +#include +#include +#include +#include + +InlineLinkedList* g_threads; +static const dword default_kernel_stack_size = 16384; +static const dword default_userspace_stack_size = 65536; + +Thread::Thread(Process& process) + : m_process(process) + , m_tid(process.m_next_tid++) +{ + dbgprintf("Thread: New thread TID=%u in %s(%u)\n", m_tid, process.name().characters(), process.pid()); + set_default_signal_dispositions(); + memset(&m_fpu_state, 0, sizeof(FPUState)); + memset(&m_tss, 0, sizeof(m_tss)); + + // Only IF is set when a process boots. + m_tss.eflags = 0x0202; + word cs, ds, ss; + + if (m_process.is_ring0()) { + cs = 0x08; + ds = 0x10; + ss = 0x10; + } else { + cs = 0x1b; + ds = 0x23; + ss = 0x23; + } + + m_tss.ds = ds; + m_tss.es = ds; + m_tss.fs = ds; + m_tss.gs = ds; + m_tss.ss = ss; + m_tss.cs = cs; + + m_tss.cr3 = m_process.page_directory().cr3(); + + if (m_process.is_ring0()) { + // FIXME: This memory is leaked. + // But uh, there's also no kernel process termination, so I guess it's not technically leaked... + dword stack_bottom = (dword)kmalloc_eternal(default_kernel_stack_size); + m_stack_top0 = (stack_bottom + default_kernel_stack_size) & 0xffffff8; + m_tss.esp = m_stack_top0; + } else { + // Ring3 processes need a separate stack for Ring0. + m_kernel_stack = kmalloc(default_kernel_stack_size); + m_stack_top0 = ((dword)m_kernel_stack + default_kernel_stack_size) & 0xffffff8; + m_tss.ss0 = 0x10; + m_tss.esp0 = m_stack_top0; + } + + // HACK: Ring2 SS in the TSS is the current PID. + m_tss.ss2 = m_process.pid(); + m_far_ptr.offset = 0x98765432; + + InterruptDisabler disabler; + g_threads->prepend(this); +} + +Thread::~Thread() +{ + dbgprintf("~Thread{%p}\n", this); + { + InterruptDisabler disabler; + g_threads->remove(this); + } + + if (g_last_fpu_thread == this) + g_last_fpu_thread = nullptr; + + if (selector()) + gdt_free_entry(selector()); + + if (m_kernel_stack) { + kfree(m_kernel_stack); + m_kernel_stack = nullptr; + } + + if (m_kernel_stack_for_signal_handler) { + kfree(m_kernel_stack_for_signal_handler); + m_kernel_stack_for_signal_handler = nullptr; + } +} + +void Thread::unblock() +{ + if (current == this) { + system.nblocked--; + m_state = Thread::Running; + return; + } + ASSERT(m_state != Thread::Runnable && m_state != Thread::Running); + system.nblocked--; + m_state = Thread::Runnable; +} + +void Thread::snooze_until(Alarm& alarm) +{ + m_snoozing_alarm = &alarm; + block(Thread::BlockedSnoozing); + Scheduler::yield(); +} + +void Thread::block(Thread::State new_state) +{ + if (state() != Thread::Running) { + kprintf("Thread::block: %s(%u) block(%u/%s) with state=%u/%s\n", process().name().characters(), process().pid(), new_state, to_string(new_state), state(), to_string(state())); + } + ASSERT(state() == Thread::Running); + system.nblocked++; + m_was_interrupted_while_blocked = false; + set_state(new_state); +} + +void block(Thread::State state) +{ + current->block(state); + Scheduler::yield(); +} + +void sleep(dword ticks) +{ + ASSERT(current->state() == Thread::Running); + current->set_wakeup_time(system.uptime + ticks); + current->block(Thread::BlockedSleep); + Scheduler::yield(); +} + +const char* to_string(Thread::State state) +{ + switch (state) { + case Thread::Invalid: return "Invalid"; + case Thread::Runnable: return "Runnable"; + case Thread::Running: return "Running"; + case Thread::Dying: return "Dying"; + case Thread::Dead: return "Dead"; + case Thread::Stopped: return "Stopped"; + case Thread::Skip1SchedulerPass: return "Skip1"; + case Thread::Skip0SchedulerPasses: return "Skip0"; + case Thread::BlockedSleep: return "Sleep"; + case Thread::BlockedWait: return "Wait"; + case Thread::BlockedRead: return "Read"; + case Thread::BlockedWrite: return "Write"; + case Thread::BlockedSignal: return "Signal"; + case Thread::BlockedSelect: return "Select"; + case Thread::BlockedLurking: return "Lurking"; + case Thread::BlockedConnect: return "Connect"; + case Thread::BlockedReceive: return "Receive"; + case Thread::BlockedSnoozing: return "Snoozing"; + } + kprintf("to_string(Thread::State): Invalid state: %u\n", state); + ASSERT_NOT_REACHED(); + return nullptr; +} + +void Thread::finalize() +{ + dbgprintf("Finalizing Thread %u in %s(%u)\n", tid(), m_process.name().characters(), pid()); + m_blocked_socket = nullptr; + set_state(Thread::State::Dead); + + if (this == &m_process.main_thread()) + m_process.finalize(); +} + +void Thread::finalize_dying_threads() +{ + Vector dying_threads; + { + InterruptDisabler disabler; + for_each_in_state(Thread::State::Dying, [&] (Thread& thread) { + dying_threads.append(&thread); + }); + } + for (auto* thread : dying_threads) + thread->finalize(); +} + +bool Thread::tick() +{ + ++m_ticks; + if (tss().cs & 3) + ++m_process.m_ticks_in_user; + else + ++m_process.m_ticks_in_kernel; + return --m_ticks_left; +} + +void Thread::send_signal(byte signal, Process* sender) +{ + ASSERT(signal < 32); + + if (sender) + dbgprintf("signal: %s(%u) sent %d to %s(%u)\n", sender->name().characters(), sender->pid(), signal, process().name().characters(), pid()); + else + dbgprintf("signal: kernel sent %d to %s(%u)\n", signal, process().name().characters(), pid()); + + InterruptDisabler disabler; + m_pending_signals |= 1 << signal; +} + +bool Thread::has_unmasked_pending_signals() const +{ + return m_pending_signals & ~m_signal_mask; +} + +ShouldUnblockThread Thread::dispatch_one_pending_signal() +{ + ASSERT_INTERRUPTS_DISABLED(); + dword signal_candidates = m_pending_signals & ~m_signal_mask; + ASSERT(signal_candidates); + + byte signal = 0; + for (; signal < 32; ++signal) { + if (signal_candidates & (1 << signal)) { + break; + } + } + return dispatch_signal(signal); +} + +enum class DefaultSignalAction { + Terminate, + Ignore, + DumpCore, + Stop, + Continue, +}; + +DefaultSignalAction default_signal_action(byte signal) +{ + ASSERT(signal && signal < NSIG); + + switch (signal) { + case SIGHUP: + case SIGINT: + case SIGKILL: + case SIGPIPE: + case SIGALRM: + case SIGUSR1: + case SIGUSR2: + case SIGVTALRM: + case SIGSTKFLT: + case SIGIO: + case SIGPROF: + case SIGTERM: + case SIGPWR: + return DefaultSignalAction::Terminate; + case SIGCHLD: + case SIGURG: + case SIGWINCH: + return DefaultSignalAction::Ignore; + case SIGQUIT: + case SIGILL: + case SIGTRAP: + case SIGABRT: + case SIGBUS: + case SIGFPE: + case SIGSEGV: + case SIGXCPU: + case SIGXFSZ: + case SIGSYS: + return DefaultSignalAction::DumpCore; + case SIGCONT: + return DefaultSignalAction::Continue; + case SIGSTOP: + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + return DefaultSignalAction::Stop; + } + ASSERT_NOT_REACHED(); +} + +ShouldUnblockThread Thread::dispatch_signal(byte signal) +{ + ASSERT_INTERRUPTS_DISABLED(); + ASSERT(signal < 32); + +#ifdef SIGNAL_DEBUG + kprintf("dispatch_signal %s(%u) <- %u\n", name().characters(), pid(), signal); +#endif + + auto& action = m_signal_action_data[signal]; + // FIXME: Implement SA_SIGINFO signal handlers. + ASSERT(!(action.flags & SA_SIGINFO)); + + // Mark this signal as handled. + m_pending_signals &= ~(1 << signal); + + if (signal == SIGSTOP) { + set_state(Stopped); + return ShouldUnblockThread::No; + } + + if (signal == SIGCONT && state() == Stopped) + set_state(Runnable); + + auto handler_laddr = action.handler_or_sigaction; + if (handler_laddr.is_null()) { + switch (default_signal_action(signal)) { + case DefaultSignalAction::Stop: + set_state(Stopped); + return ShouldUnblockThread::No; + case DefaultSignalAction::DumpCore: + case DefaultSignalAction::Terminate: + m_process.terminate_due_to_signal(signal); + return ShouldUnblockThread::No; + case DefaultSignalAction::Ignore: + return ShouldUnblockThread::No; + case DefaultSignalAction::Continue: + return ShouldUnblockThread::Yes; + } + ASSERT_NOT_REACHED(); + } + + if (handler_laddr.as_ptr() == SIG_IGN) { +#ifdef SIGNAL_DEBUG + kprintf("%s(%u) ignored signal %u\n", name().characters(), pid(), signal); +#endif + return ShouldUnblockThread::Yes; + } + + dword old_signal_mask = m_signal_mask; + dword new_signal_mask = action.mask; + if (action.flags & SA_NODEFER) + new_signal_mask &= ~(1 << signal); + else + new_signal_mask |= 1 << signal; + + m_signal_mask |= new_signal_mask; + + Scheduler::prepare_to_modify_tss(*this); + + word ret_cs = m_tss.cs; + dword ret_eip = m_tss.eip; + dword ret_eflags = m_tss.eflags; + bool interrupting_in_kernel = (ret_cs & 3) == 0; + + ProcessPagingScope paging_scope(m_process); + m_process.create_signal_trampolines_if_needed(); + + if (interrupting_in_kernel) { +#ifdef SIGNAL_DEBUG + kprintf("dispatch_signal to %s(%u) in state=%s with return to %w:%x\n", name().characters(), pid(), to_string(state()), ret_cs, ret_eip); +#endif + ASSERT(is_blocked()); + m_tss_to_resume_kernel = m_tss; +#ifdef SIGNAL_DEBUG + kprintf("resume tss pc: %w:%x stack: %w:%x flags: %x cr3: %x\n", m_tss_to_resume_kernel.cs, m_tss_to_resume_kernel.eip, m_tss_to_resume_kernel.ss, m_tss_to_resume_kernel.esp, m_tss_to_resume_kernel.eflags, m_tss_to_resume_kernel.cr3); +#endif + + if (!m_signal_stack_user_region) { + m_signal_stack_user_region = m_process.allocate_region(LinearAddress(), default_userspace_stack_size, "Signal stack (user)"); + ASSERT(m_signal_stack_user_region); + } + if (!m_kernel_stack_for_signal_handler) { + m_kernel_stack_for_signal_handler = kmalloc(default_kernel_stack_size); + ASSERT(m_kernel_stack_for_signal_handler); + } + m_tss.ss = 0x23; + m_tss.esp = m_signal_stack_user_region->laddr().offset(default_userspace_stack_size).get(); + m_tss.ss0 = 0x10; + m_tss.esp0 = (dword)m_kernel_stack_for_signal_handler + default_kernel_stack_size; + + push_value_on_stack(0); + } else { + push_value_on_stack(ret_eip); + push_value_on_stack(ret_eflags); + + // PUSHA + dword old_esp = m_tss.esp; + push_value_on_stack(m_tss.eax); + push_value_on_stack(m_tss.ecx); + push_value_on_stack(m_tss.edx); + push_value_on_stack(m_tss.ebx); + push_value_on_stack(old_esp); + push_value_on_stack(m_tss.ebp); + push_value_on_stack(m_tss.esi); + push_value_on_stack(m_tss.edi); + + // Align the stack. + m_tss.esp -= 12; + } + + // PUSH old_signal_mask + push_value_on_stack(old_signal_mask); + + m_tss.cs = 0x1b; + m_tss.ds = 0x23; + m_tss.es = 0x23; + m_tss.fs = 0x23; + m_tss.gs = 0x23; + m_tss.eip = handler_laddr.get(); + + // FIXME: Should we worry about the stack being 16 byte aligned when entering a signal handler? + push_value_on_stack(signal); + + if (interrupting_in_kernel) + push_value_on_stack(m_process.m_return_to_ring0_from_signal_trampoline.get()); + else + push_value_on_stack(m_process.m_return_to_ring3_from_signal_trampoline.get()); + + ASSERT((m_tss.esp % 16) == 0); + + // FIXME: This state is such a hack. It avoids trouble if 'current' is the process receiving a signal. + set_state(Skip1SchedulerPass); + +#ifdef SIGNAL_DEBUG + kprintf("signal: Okay, %s(%u) {%s} has been primed with signal handler %w:%x\n", name().characters(), pid(), to_string(state()), m_tss.cs, m_tss.eip); +#endif + return ShouldUnblockThread::Yes; +} + +void Thread::set_default_signal_dispositions() +{ + // FIXME: Set up all the right default actions. See signal(7). + memset(&m_signal_action_data, 0, sizeof(m_signal_action_data)); + m_signal_action_data[SIGCHLD].handler_or_sigaction = LinearAddress((dword)SIG_IGN); + m_signal_action_data[SIGWINCH].handler_or_sigaction = LinearAddress((dword)SIG_IGN); +} + +void Thread::push_value_on_stack(dword value) +{ + m_tss.esp -= 4; + dword* stack_ptr = (dword*)m_tss.esp; + *stack_ptr = value; +} + +void Thread::make_userspace_stack(Vector arguments, Vector environment) +{ + auto* region = m_process.allocate_region(LinearAddress(), default_userspace_stack_size, "stack"); + ASSERT(region); + m_stack_top3 = region->laddr().offset(default_userspace_stack_size).get(); + m_tss.esp = m_stack_top3; + + char* stack_base = (char*)region->laddr().get(); + int argc = arguments.size(); + char** argv = (char**)stack_base; + char** env = argv + arguments.size() + 1; + char* bufptr = stack_base + (sizeof(char*) * (arguments.size() + 1)) + (sizeof(char*) * (environment.size() + 1)); + + size_t total_blob_size = 0; + for (auto& a : arguments) + total_blob_size += a.length() + 1; + for (auto& e : environment) + total_blob_size += e.length() + 1; + + size_t total_meta_size = sizeof(char*) * (arguments.size() + 1) + sizeof(char*) * (environment.size() + 1); + + // FIXME: It would be better if this didn't make us panic. + ASSERT((total_blob_size + total_meta_size) < default_userspace_stack_size); + + for (int i = 0; i < arguments.size(); ++i) { + argv[i] = bufptr; + memcpy(bufptr, arguments[i].characters(), arguments[i].length()); + bufptr += arguments[i].length(); + *(bufptr++) = '\0'; + } + argv[arguments.size()] = nullptr; + + for (int i = 0; i < environment.size(); ++i) { + env[i] = bufptr; + memcpy(bufptr, environment[i].characters(), environment[i].length()); + bufptr += environment[i].length(); + *(bufptr++) = '\0'; + } + env[environment.size()] = nullptr; + + // NOTE: The stack needs to be 16-byte aligned. + push_value_on_stack((dword)env); + push_value_on_stack((dword)argv); + push_value_on_stack((dword)argc); + push_value_on_stack(0); +} + +Thread* Thread::clone(Process& process) +{ + auto* clone = new Thread(process); + memcpy(clone->m_signal_action_data, m_signal_action_data, sizeof(m_signal_action_data)); + clone->m_signal_mask = m_signal_mask; + //clone->m_tss = m_tss; + clone->m_fpu_state = m_fpu_state; + clone->m_has_used_fpu = m_has_used_fpu; + return clone; +} + +KResult Thread::wait_for_connect(Socket& socket) +{ + if (socket.is_connected()) + return KSuccess; + m_blocked_socket = socket; + block(Thread::State::BlockedConnect); + Scheduler::yield(); + m_blocked_socket = nullptr; + if (!socket.is_connected()) + return KResult(-ECONNREFUSED); + return KSuccess; +} + +void Thread::initialize() +{ + g_threads = new InlineLinkedList; + Scheduler::initialize(); +} diff --git a/Kernel/Thread.h b/Kernel/Thread.h new file mode 100644 index 00000000000..85be55caa7a --- /dev/null +++ b/Kernel/Thread.h @@ -0,0 +1,205 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +class Alarm; +class Process; +class Region; +class Socket; + +enum class ShouldUnblockThread { No = 0, Yes }; + +struct SignalActionData { + LinearAddress handler_or_sigaction; + dword mask { 0 }; + int flags { 0 }; + LinearAddress restorer; +}; + +class Thread : public InlineLinkedListNode { + friend class Process; + friend class Scheduler; +public: + explicit Thread(Process&); + ~Thread(); + + static void initialize(); + static void finalize_dying_threads(); + + int tid() const { return m_tid; } + int pid() const; + + Process& process() { return m_process; } + const Process& process() const { return m_process; } + + void finalize(); + + enum State { + Invalid = 0, + Runnable, + Running, + Skip1SchedulerPass, + Skip0SchedulerPasses, + Dying, + Dead, + Stopped, + BlockedLurking, + BlockedSleep, + BlockedWait, + BlockedRead, + BlockedWrite, + BlockedSignal, + BlockedSelect, + BlockedConnect, + BlockedReceive, + BlockedSnoozing, + }; + + void did_schedule() { ++m_times_scheduled; } + dword times_scheduled() const { return m_times_scheduled; } + + bool is_stopped() const { return m_state == Stopped; } + bool is_blocked() const + { + return m_state == BlockedSleep || m_state == BlockedWait || m_state == BlockedRead || m_state == BlockedWrite || m_state == BlockedSignal || m_state == BlockedSelect; + } + bool in_kernel() const { return (m_tss.cs & 0x03) == 0; } + + dword frame_ptr() const { return m_tss.ebp; } + dword stack_ptr() const { return m_tss.esp; } + dword stack_top() const { return m_tss.ss == 0x10 ? m_stack_top0 : m_stack_top3; } + + word selector() const { return m_far_ptr.selector; } + TSS32& tss() { return m_tss; } + State state() const { return m_state; } + dword ticks() const { return m_ticks; } + pid_t waitee_pid() const { return m_waitee_pid; } + + void block(Thread::State); + void unblock(); + + void set_wakeup_time(dword t) { m_wakeup_time = t; } + dword wakeup_time() const { return m_wakeup_time; } + void snooze_until(Alarm&); + KResult wait_for_connect(Socket&); + + const FarPtr& far_ptr() const { return m_far_ptr; } + + bool tick(); + void set_ticks_left(dword t) { m_ticks_left = t; } + dword ticks_left() const { return m_ticks_left; } + + dword kernel_stack_base() const { return (dword)m_kernel_stack; } + dword kernel_stack_for_signal_handler_base() const { return (dword)m_kernel_stack_for_signal_handler; } + + void set_selector(word s) { m_far_ptr.selector = s; } + void set_state(State s) { m_state = s; } + + void send_signal(byte signal, Process* sender); + + ShouldUnblockThread dispatch_one_pending_signal(); + ShouldUnblockThread dispatch_signal(byte signal); + bool has_unmasked_pending_signals() const; + void terminate_due_to_signal(byte signal); + + FPUState& fpu_state() { return m_fpu_state; } + bool has_used_fpu() const { return m_has_used_fpu; } + void set_has_used_fpu(bool b) { m_has_used_fpu = b; } + + void set_blocked_socket(Socket* socket) { m_blocked_socket = socket; } + + void set_default_signal_dispositions(); + void push_value_on_stack(dword); + void make_userspace_stack(Vector arguments, Vector environment); + + Thread* clone(Process&); + + // For InlineLinkedList + Thread* m_prev { nullptr }; + Thread* m_next { nullptr }; + + template static void for_each_in_state(State, Callback); + template static void for_each_living(Callback); + template static void for_each(Callback); + +private: + Process& m_process; + int m_tid { -1 }; + TSS32 m_tss; + TSS32 m_tss_to_resume_kernel; + FarPtr m_far_ptr; + dword m_ticks { 0 }; + dword m_ticks_left { 0 }; + dword m_stack_top0 { 0 }; + dword m_stack_top3 { 0 }; + dword m_wakeup_time { 0 }; + dword m_times_scheduled { 0 }; + dword m_pending_signals { 0 }; + dword m_signal_mask { 0 }; + void* m_kernel_stack { nullptr }; + void* m_kernel_stack_for_signal_handler { nullptr }; + pid_t m_waitee_pid { -1 }; + int m_blocked_fd { -1 }; + timeval m_select_timeout; + SignalActionData m_signal_action_data[32]; + RetainPtr m_blocked_socket; + Region* m_signal_stack_user_region { nullptr }; + Alarm* m_snoozing_alarm { nullptr }; + Vector m_select_read_fds; + Vector m_select_write_fds; + Vector m_select_exceptional_fds; + State m_state { Invalid }; + FPUState m_fpu_state; + bool m_select_has_timeout { false }; + bool m_has_used_fpu { false }; + bool m_was_interrupted_while_blocked { false }; +}; + +extern InlineLinkedList* g_threads; + +const char* to_string(Thread::State); +void block(Thread::State); +void sleep(dword ticks); + +template +inline void Thread::for_each_in_state(State state, Callback callback) +{ + ASSERT_INTERRUPTS_DISABLED(); + for (auto* thread = g_threads->head(); thread;) { + auto* next_thread = thread->next(); + if (thread->state() == state) + callback(*thread); + thread = next_thread; + } +} + +template +inline void Thread::for_each_living(Callback callback) +{ + ASSERT_INTERRUPTS_DISABLED(); + for (auto* thread = g_threads->head(); thread;) { + auto* next_thread = thread->next(); + if (thread->state() != Thread::State::Dead && thread->state() != Thread::State::Dying) + callback(*thread); + thread = next_thread; + } +} + +template +inline void Thread::for_each(Callback callback) +{ + ASSERT_INTERRUPTS_DISABLED(); + for (auto* thread = g_threads->head(); thread;) { + auto* next_thread = thread->next(); + if (callback(*thread) == IterationDecision::Abort) + return; + thread = next_thread; + } +} + diff --git a/Kernel/VirtualFileSystem.cpp b/Kernel/VirtualFileSystem.cpp index 1f65d6742a9..e03efde33c4 100644 --- a/Kernel/VirtualFileSystem.cpp +++ b/Kernel/VirtualFileSystem.cpp @@ -138,7 +138,7 @@ KResult VFS::utime(const String& path, Inode& base, time_t atime, time_t mtime) auto& inode = *descriptor_or_error.value()->inode(); if (inode.fs().is_readonly()) return KResult(-EROFS); - if (inode.metadata().uid != current->euid()) + if (inode.metadata().uid != current->process().euid()) return KResult(-EACCES); int error = inode.set_atime(atime); @@ -175,11 +175,11 @@ KResultOr> VFS::open(const String& path, int options, m // NOTE: Read permission is a bit weird, since O_RDONLY == 0, // so we check if (NOT write_only OR read_and_write) if (!(options & O_WRONLY) || (options & O_RDWR)) { - if (!metadata.may_read(*current)) + if (!metadata.may_read(current->process())) return KResult(-EACCES); } if ((options & O_WRONLY) || (options & O_RDWR)) { - if (!metadata.may_write(*current)) + if (!metadata.may_write(current->process())) return KResult(-EACCES); if (metadata.is_directory()) return KResult(-EISDIR); @@ -188,7 +188,6 @@ KResultOr> VFS::open(const String& path, int options, m if (metadata.is_device()) { auto it = m_devices.find(encoded_device(metadata.major_device, metadata.minor_device)); if (it == m_devices.end()) { - kprintf("VFS::open: no such device %u,%u\n", metadata.major_device, metadata.minor_device); return KResult(-ENODEV); } auto descriptor_or_error = (*it).value->open(options); @@ -217,7 +216,7 @@ KResultOr> VFS::create(const String& path, int options, return KResult(-ENOENT); if (existing_file_or_error.error() != -ENOENT) return existing_file_or_error.error(); - if (!parent_inode->metadata().may_write(*current)) + if (!parent_inode->metadata().may_write(current->process())) return KResult(-EACCES); FileSystemPath p(path); @@ -241,7 +240,7 @@ KResult VFS::mkdir(const String& path, mode_t mode, Inode& base) if (result.error() != -ENOENT) return result.error(); - if (!parent_inode->metadata().may_write(*current)) + if (!parent_inode->metadata().may_write(current->process())) return KResult(-EACCES); FileSystemPath p(path); @@ -261,15 +260,15 @@ KResult VFS::access(const String& path, int mode, Inode& base) auto inode = inode_or_error.value(); auto metadata = inode->metadata(); if (mode & R_OK) { - if (!metadata.may_read(*current)) + if (!metadata.may_read(current->process())) return KResult(-EACCES); } if (mode & W_OK) { - if (!metadata.may_write(*current)) + if (!metadata.may_write(current->process())) return KResult(-EACCES); } if (mode & X_OK) { - if (!metadata.may_execute(*current)) + if (!metadata.may_execute(current->process())) return KResult(-EACCES); } return KSuccess; @@ -283,7 +282,7 @@ KResultOr> VFS::open_directory(const String& path, Inode& base) auto inode = inode_or_error.value(); if (!inode->is_directory()) return KResult(-ENOTDIR); - if (!inode->metadata().may_execute(*current)) + if (!inode->metadata().may_execute(current->process())) return KResult(-EACCES); return Retained(*inode); } @@ -293,7 +292,7 @@ KResult VFS::chmod(Inode& inode, mode_t mode) if (inode.fs().is_readonly()) return KResult(-EROFS); - if (current->euid() != inode.metadata().uid && !current->is_superuser()) + if (current->process().euid() != inode.metadata().uid && !current->process().is_superuser()) return KResult(-EPERM); // Only change the permission bits. @@ -320,19 +319,19 @@ KResult VFS::chown(const String& path, uid_t a_uid, gid_t a_gid, Inode& base) if (inode->fs().is_readonly()) return KResult(-EROFS); - if (current->euid() != inode->metadata().uid && !current->is_superuser()) + if (current->process().euid() != inode->metadata().uid && !current->process().is_superuser()) return KResult(-EPERM); uid_t new_uid = inode->metadata().uid; gid_t new_gid = inode->metadata().gid; if (a_uid != (uid_t)-1) { - if (current->euid() != a_uid && !current->is_superuser()) + if (current->process().euid() != a_uid && !current->process().is_superuser()) return KResult(-EPERM); new_uid = a_uid; } if (a_gid != (gid_t)-1) { - if (!current->in_group(a_gid) && !current->is_superuser()) + if (!current->process().in_group(a_gid) && !current->process().is_superuser()) return KResult(-EPERM); new_gid = a_gid; } @@ -377,7 +376,7 @@ KResult VFS::link(const String& old_path, const String& new_path, Inode& base) if (parent_inode->fs().is_readonly()) return KResult(-EROFS); - if (!parent_inode->metadata().may_write(*current)) + if (!parent_inode->metadata().may_write(current->process())) return KResult(-EACCES); return parent_inode->add_child(old_inode->identifier(), FileSystemPath(new_path).basename(), 0); @@ -394,7 +393,7 @@ KResult VFS::unlink(const String& path, Inode& base) if (inode->is_directory()) return KResult(-EISDIR); - if (!parent_inode->metadata().may_write(*current)) + if (!parent_inode->metadata().may_write(current->process())) return KResult(-EACCES); return parent_inode->remove_child(FileSystemPath(path).basename()); @@ -410,7 +409,7 @@ KResult VFS::symlink(const String& target, const String& linkpath, Inode& base) return KResult(-ENOENT); if (existing_file_or_error.error() != -ENOENT) return existing_file_or_error.error(); - if (!parent_inode->metadata().may_write(*current)) + if (!parent_inode->metadata().may_write(current->process())) return KResult(-EACCES); FileSystemPath p(linkpath); @@ -442,7 +441,7 @@ KResult VFS::rmdir(const String& path, Inode& base) if (!inode->is_directory()) return KResult(-ENOTDIR); - if (!parent_inode->metadata().may_write(*current)) + if (!parent_inode->metadata().may_write(current->process())) return KResult(-EACCES); if (inode->directory_entry_count() != 2) @@ -561,7 +560,7 @@ KResultOr VFS::resolve_path(const String& path, InodeIdentifier #endif return KResult(-ENOTDIR); } - if (!metadata.may_execute(*current)) + if (!metadata.may_execute(current->process())) return KResult(-EACCES); auto parent = crumb_id; crumb_id = crumb_inode->lookup(part); @@ -612,7 +611,6 @@ KResultOr VFS::resolve_path(const String& path, InodeIdentifier ASSERT(crumb_id.is_valid()); } } - return crumb_id; } diff --git a/Kernel/i386.cpp b/Kernel/i386.cpp index 4feb155e266..44103f0b82d 100644 --- a/Kernel/i386.cpp +++ b/Kernel/i386.cpp @@ -126,7 +126,7 @@ static void dump(const DumpType& regs) { word ss; dword esp; - if (current->is_ring0()) { + if (!current || current->process().is_ring0()) { ss = regs.ds; esp = regs.esp; } else { @@ -144,7 +144,7 @@ static void dump(const DumpType& regs) kprintf("eax=%x ebx=%x ecx=%x edx=%x\n", regs.eax, regs.ebx, regs.ecx, regs.edx); kprintf("ebp=%x esp=%x esi=%x edi=%x\n", regs.ebp, esp, regs.esi, regs.edi); - if (current->validate_read((void*)regs.eip, 8)) { + if (current && current->process().validate_read((void*)regs.eip, 8)) { byte* codeptr = (byte*)regs.eip; kprintf("code: %b %b %b %b %b %b %b %b\n", codeptr[0], @@ -163,17 +163,21 @@ static void dump(const DumpType& regs) EH_ENTRY_NO_CODE(6); void exception_6_handler(RegisterDump& regs) { - kprintf("%s invalid opcode: %u(%s)\n", current->is_ring0() ? "Kernel" : "Process", current->pid(), current->name().characters()); + if (!current) { + kprintf("#UD with !current\n"); + hang(); + } + kprintf("%s invalid opcode: %u(%s)\n", current->process().is_ring0() ? "Kernel" : "Process", current->pid(), current->process().name().characters()); dump(regs); - if (current->is_ring0()) { + if (current->process().is_ring0()) { kprintf("Oh shit, we've crashed in ring 0 :(\n"); hang(); } hang(); - current->crash(); + current->process().crash(); } // 7: FPU not available exception @@ -183,14 +187,14 @@ void exception_7_handler(RegisterDump& regs) (void)regs; asm volatile("clts"); - if (g_last_fpu_process == current) + if (g_last_fpu_thread == current) return; - if (g_last_fpu_process) { - asm volatile("fnsave %0":"=m"(g_last_fpu_process->fpu_state())); + if (g_last_fpu_thread) { + asm volatile("fnsave %0":"=m"(g_last_fpu_thread->fpu_state())); } else { asm volatile("fnclex"); } - g_last_fpu_process = current; + g_last_fpu_thread = current; if (current->has_used_fpu()) { asm volatile("frstor %0"::"m"(current->fpu_state())); @@ -200,7 +204,7 @@ void exception_7_handler(RegisterDump& regs) } #ifdef FPU_EXCEPTION_DEBUG - kprintf("%s FPU not available exception: %u(%s)\n", current->is_ring0() ? "Kernel" : "Process", current->pid(), current->name().characters()); + kprintf("%s FPU not available exception: %u(%s)\n", current->process().is_ring0() ? "Kernel" : "Process", current->pid(), current->process().name().characters()); dump(regs); #endif } @@ -210,16 +214,16 @@ void exception_7_handler(RegisterDump& regs) EH_ENTRY_NO_CODE(0); void exception_0_handler(RegisterDump& regs) { - kprintf("%s DIVIDE ERROR: %u(%s)\n", current->is_ring0() ? "Kernel" : "User", current->pid(), current->name().characters()); + kprintf("%s DIVIDE ERROR: %u(%s)\n", current->process().is_ring0() ? "Kernel" : "User", current->pid(), current->process().name().characters()); dump(regs); - if (current->is_ring0()) { + if (current->process().is_ring0()) { kprintf("Oh shit, we've crashed in ring 0 :(\n"); hang(); } - current->crash(); + current->process().crash(); } @@ -227,16 +231,16 @@ void exception_0_handler(RegisterDump& regs) EH_ENTRY(13); void exception_13_handler(RegisterDumpWithExceptionCode& regs) { - kprintf("%s GPF: %u(%s)\n", current->is_ring0() ? "Kernel" : "User", current->pid(), current->name().characters()); + kprintf("%s GPF: %u(%s)\n", current->process().is_ring0() ? "Kernel" : "User", current->pid(), current->process().name().characters()); dump(regs); - if (current->is_ring0()) { + if (current->process().is_ring0()) { kprintf("Oh shit, we've crashed in ring 0 :(\n"); hang(); } - current->crash(); + current->process().crash(); } // 14: Page Fault @@ -253,7 +257,7 @@ void exception_14_handler(RegisterDumpWithExceptionCode& regs) #ifdef PAGE_FAULT_DEBUG dbgprintf("%s(%u): ring%u %s page fault in PD=%x, %s L%x\n", - current->name().characters(), + current->process().name().characters(), current->pid(), regs.cs & 3, regs.exception_code & 1 ? "PV" : "NP", @@ -270,12 +274,13 @@ void exception_14_handler(RegisterDumpWithExceptionCode& regs) if (response == PageFaultResponse::ShouldCrash) { kprintf("%s(%u) unrecoverable page fault, %s laddr=%p\n", - current->name().characters(), + current->process().name().characters(), current->pid(), regs.exception_code & 2 ? "write" : "read", faultAddress); dump(regs); - current->crash(); + hang(); + current->process().crash(); } else if (response == PageFaultResponse::Continue) { #ifdef PAGE_FAULT_DEBUG dbgprintf("Continuing after resolved page fault\n"); diff --git a/Kernel/init.cpp b/Kernel/init.cpp index b397255cc83..6fc8797bf3a 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -26,9 +26,9 @@ #include "E1000NetworkAdapter.h" #include -#define SPAWN_LAUNCHER +//#define SPAWN_LAUNCHER //#define SPAWN_GUITEST2 -//#define SPAWN_FILE_MANAGER +#define SPAWN_FILE_MANAGER //#define SPAWN_PROCESS_MANAGER //#define SPAWN_TEXT_EDITOR //#define SPAWN_FONTEDITOR @@ -78,7 +78,9 @@ VFS* vfs; vfs->mount_root(e2fs.copy_ref()); + dbgprintf("Load ksyms\n"); load_ksyms(); + dbgprintf("Loaded ksyms\n"); vfs->mount(ProcFS::the(), "/proc"); vfs->mount(DevPtsFS::the(), "/dev/pts"); @@ -130,7 +132,7 @@ VFS* vfs; Process::create_kernel_process("spawn_stress", spawn_stress); #endif - current->sys$exit(0); + current->process().sys$exit(0); ASSERT_NOT_REACHED(); } @@ -177,6 +179,7 @@ VFS* vfs; devptsfs->initialize(); Process::initialize(); + Thread::initialize(); Process::create_kernel_process("init_stage2", init_stage2); Process::create_kernel_process("syncd", [] { for (;;) { @@ -186,10 +189,10 @@ VFS* vfs; }); Process::create_kernel_process("Finalizer", [] { g_finalizer = current; - current->set_priority(Process::LowPriority); + current->process().set_priority(Process::LowPriority); for (;;) { - Process::finalize_dying_processes(); - current->block(Process::BlockedLurking); + Thread::finalize_dying_threads(); + current->block(Thread::BlockedLurking); Scheduler::yield(); } }); diff --git a/Kernel/kassert.h b/Kernel/kassert.h index 774afe2a629..e40b26deb61 100644 --- a/Kernel/kassert.h +++ b/Kernel/kassert.h @@ -8,6 +8,7 @@ #define ASSERT(expr) (static_cast(expr) ? (void)0 : __assertion_failed(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__)) #define CRASH() do { asm volatile("ud2"); } while(0) #define RELEASE_ASSERT(x) do { if (!(x)) CRASH(); } while(0) +//#define ASSERT RELEASE_ASSERT #define ASSERT_NOT_REACHED() ASSERT(false) #define ASSERT_INTERRUPTS_DISABLED() ASSERT(!(cpu_flags() & 0x200)) #define ASSERT_INTERRUPTS_ENABLED() ASSERT(cpu_flags() & 0x200) diff --git a/Kernel/kmalloc.cpp b/Kernel/kmalloc.cpp index 4689a9b1962..7e448ce2f24 100644 --- a/Kernel/kmalloc.cpp +++ b/Kernel/kmalloc.cpp @@ -101,7 +101,7 @@ void* kmalloc_impl(size_t size) real_size = size + sizeof(allocation_t); if (sum_free < real_size) { - kprintf("%s<%u> kmalloc(): PANIC! Out of memory (sucks, dude)\nsum_free=%u, real_size=%u\n", current->name().characters(), current->pid(), sum_free, real_size); + kprintf("%s<%u> kmalloc(): PANIC! Out of memory (sucks, dude)\nsum_free=%u, real_size=%u\n", current->process().name().characters(), current->pid(), sum_free, real_size); hang(); } @@ -161,7 +161,7 @@ void* kmalloc_impl(size_t size) } } - kprintf("%s<%u> kmalloc(): PANIC! Out of memory (no suitable block for size %u)\n", current->name().characters(), current->pid(), size); + kprintf("%s<%u> kmalloc(): PANIC! Out of memory (no suitable block for size %u)\n", current->process().name().characters(), current->pid(), size); hang(); } diff --git a/Kernel/kprintf.cpp b/Kernel/kprintf.cpp index b4c52b69056..cdcc67c7b4b 100644 --- a/Kernel/kprintf.cpp +++ b/Kernel/kprintf.cpp @@ -8,7 +8,11 @@ static void console_putch(char*&, char ch) { - Console::the().write(*current, (byte*)&ch, 1); + if (!current) { + IO::out8(0xe9, ch); + return; + } + Console::the().write(current->process(), (byte*)&ch, 1); } int kprintf(const char* fmt, ...) diff --git a/SharedGraphics/PNGLoader.cpp b/SharedGraphics/PNGLoader.cpp index f248571cfb2..97d2c629834 100644 --- a/SharedGraphics/PNGLoader.cpp +++ b/SharedGraphics/PNGLoader.cpp @@ -9,6 +9,8 @@ #include #include +//#define PNG_STOPWATCH_DEBUG + struct PNG_IHDR { NetworkOrdered width; NetworkOrdered height; @@ -246,7 +248,9 @@ template [[gnu::noinline]] static void unfilter(PNGLoadingContext& context) { { +#ifdef PNG_STOPWATCH_DEBUG Stopwatch sw("load_png_impl: unfilter: unpack"); +#endif // First unpack the scanlines to RGBA: switch (context.color_type) { case 2: @@ -275,7 +279,9 @@ template auto dummy_scanline = ByteBuffer::create_zeroed(context.width * sizeof(RGBA32)); +#ifdef PNG_STOPWATCH_DEBUG Stopwatch sw("load_png_impl: unfilter: process"); +#endif for (int y = 0; y < context.height; ++y) { auto filter = context.scanlines[y].filter; if (filter == 0) { @@ -318,7 +324,9 @@ template static RetainPtr load_png_impl(const byte* data, int data_size) { +#ifdef PNG_STOPWATCH_DEBUG Stopwatch sw("load_png_impl: total"); +#endif const byte* data_ptr = data; int data_remaining = data_size; @@ -336,7 +344,9 @@ static RetainPtr load_png_impl(const byte* data, int data_size) data_remaining -= sizeof(png_header); { +#ifdef PNG_STOPWATCH_DEBUG Stopwatch sw("load_png_impl: read chunks"); +#endif Streamer streamer(data_ptr, data_remaining); while (!streamer.at_end()) { if (!process_chunk(streamer, context)) { @@ -346,7 +356,9 @@ static RetainPtr load_png_impl(const byte* data, int data_size) } { +#ifdef PNG_STOPWATCH_DEBUG Stopwatch sw("load_png_impl: uncompress"); +#endif unsigned long srclen = context.compressed_data.size() - 6; unsigned long destlen = context.decompression_buffer_size; int ret = puff(context.decompression_buffer, &destlen, context.compressed_data.data() + 2, &srclen); @@ -356,7 +368,9 @@ static RetainPtr load_png_impl(const byte* data, int data_size) } { +#ifdef PNG_STOPWATCH_DEBUG Stopwatch sw("load_png_impl: extract scanlines"); +#endif context.scanlines.ensure_capacity(context.height); Streamer streamer(context.decompression_buffer, context.decompression_buffer_size); for (int y = 0; y < context.height; ++y) { @@ -372,7 +386,9 @@ static RetainPtr load_png_impl(const byte* data, int data_size) } { +#ifdef PNG_STOPWATCH_DEBUG Stopwatch sw("load_png_impl: create bitmap"); +#endif context.bitmap = GraphicsBitmap::create(context.has_alpha() ? GraphicsBitmap::Format::RGBA32 : GraphicsBitmap::Format::RGB32, { context.width, context.height }); }