From 2436959c84314a8105e09a0a12bc33cc7bbd91f5 Mon Sep 17 00:00:00 2001 From: nakst <> Date: Sat, 13 Nov 2021 15:18:37 +0000 Subject: [PATCH] cleanup scheduler --- arch/x86_64/kernel.cpp | 2 +- drivers/acpica.cpp | 2 +- drivers/i8254x.cpp | 2 +- kernel/cache.cpp | 8 +- kernel/main.cpp | 8 +- kernel/memory.cpp | 10 +- kernel/module.h | 4 +- kernel/objects.cpp | 4 +- kernel/posix.cpp | 6 +- kernel/scheduler.cpp | 467 ++++++++++++++++++------------------- kernel/synchronisation.cpp | 71 +++--- kernel/syscall.cpp | 23 +- shared/strings.cpp | 4 +- 13 files changed, 288 insertions(+), 323 deletions(-) diff --git a/arch/x86_64/kernel.cpp b/arch/x86_64/kernel.cpp index 193b963..d8ae63a 100644 --- a/arch/x86_64/kernel.cpp +++ b/arch/x86_64/kernel.cpp @@ -477,7 +477,7 @@ extern "C" void InterruptHandler(InterruptContext *context) { EsMemoryZero(&crashReason, sizeof(EsCrashReason)); crashReason.errorCode = ES_FATAL_ERROR_PROCESSOR_EXCEPTION; crashReason.duringSystemCall = (EsSyscallType) -1; - scheduler.CrashProcess(currentThread->process, &crashReason); + ProcessCrash(currentThread->process, &crashReason); resolved:; diff --git a/drivers/acpica.cpp b/drivers/acpica.cpp index 8bf33ba..1a3ef65 100644 --- a/drivers/acpica.cpp +++ b/drivers/acpica.cpp @@ -122,7 +122,7 @@ ES_EXTERN_C ACPI_STATUS AcpiOsExecute(ACPI_EXECUTE_TYPE type, ACPI_OSD_EXEC_CALL event->function = function; event->context = context; - Thread *thread = scheduler.SpawnThread("ACPICAEvent", (uintptr_t) RunACPICAEvent, (uintptr_t) event); + Thread *thread = ThreadSpawn("ACPICAEvent", (uintptr_t) RunACPICAEvent, (uintptr_t) event); if (acpiEventCount == 256) { KernelPanic("AcpiOsExecute - Exceeded maximum event count, 256.\n"); diff --git a/drivers/i8254x.cpp b/drivers/i8254x.cpp index 6b71ea9..961dc54 100644 --- a/drivers/i8254x.cpp +++ b/drivers/i8254x.cpp @@ -196,7 +196,7 @@ bool Controller::Transmit(void *dataVirtual, uintptr_t dataPhysical, size_t data void Controller::DispatchThread() { while (true) { KEvent *events[] = { &receiveEvent }; - KWaitEvents(events, 1); + KEventWaitMultiple(events, 1); NetInterfaceSetConnected(this, RD_REGISTER_STATUS() & (1 << 1)); diff --git a/kernel/cache.cpp b/kernel/cache.cpp index 1ee7bd8..0bfb35a 100644 --- a/kernel/cache.cpp +++ b/kernel/cache.cpp @@ -1214,12 +1214,12 @@ void CCWriteBehindThread() { // Wait for a reason to want to write behind. // - The CC_WAIT_FOR_WRITE_BEHIND timer expires. // - The number of available page frames is low (pmm.availableLow). - // - The system is shutting down and so the cache must be flushed (scheduler.killedEvent). + // - The system is shutting down and so the cache must be flushed (scheduler.allProcessesTerminatedEvent). // - The modified list is getting full (activeSectionManager.modifiedGettingFull). KTimer timer = {}; KTimerSet(&timer, CC_WAIT_FOR_WRITE_BEHIND - lastWriteMs); - KEvent *events[] = { &timer.event, &pmm.availableLow, &scheduler.killedEvent, &activeSectionManager.modifiedGettingFull }; - scheduler.WaitEvents(events, sizeof(events) / sizeof(events[0])); + KEvent *events[] = { &timer.event, &pmm.availableLow, &scheduler.allProcessesTerminatedEvent, &activeSectionManager.modifiedGettingFull }; + KEventWaitMultiple(events, sizeof(events) / sizeof(events[0])); KTimerRemove(&timer); } @@ -1251,7 +1251,7 @@ void CCInitialise() { activeSectionManager.sectionCount); KEventSet(&activeSectionManager.modifiedNonFull); - activeSectionManager.writeBackThread = scheduler.SpawnThread("CCWriteBehind", (uintptr_t) CCWriteBehindThread, 0, ES_FLAGS_DEFAULT); + activeSectionManager.writeBackThread = ThreadSpawn("CCWriteBehind", (uintptr_t) CCWriteBehindThread, 0, ES_FLAGS_DEFAULT); activeSectionManager.writeBackThread->isPageGenerator = true; } diff --git a/kernel/main.cpp b/kernel/main.cpp index c83d15c..d35bd19 100644 --- a/kernel/main.cpp +++ b/kernel/main.cpp @@ -14,7 +14,7 @@ #include "kernel.h" void KernelInitialise() { - kernelProcess = scheduler.SpawnProcess(PROCESS_KERNEL); // Spawn the kernel process. + kernelProcess = ProcessSpawn(PROCESS_KERNEL); // Spawn the kernel process. MMInitialise(); // Initialise the memory manager. KThreadCreate("KernelMain", KernelMain); // Create the KernelMain thread. ArchInitialise(); // Start processors and initialise CPULocalStorage. @@ -22,11 +22,11 @@ void KernelInitialise() { } void KernelMain(uintptr_t) { - desktopProcess = scheduler.SpawnProcess(PROCESS_DESKTOP); // Spawn the desktop process. + desktopProcess = ProcessSpawn(PROCESS_DESKTOP); // Spawn the desktop process. DriversInitialise(); // Load the root device. - desktopProcess->Start(EsLiteral(K_DESKTOP_EXECUTABLE)); // Start the desktop process. + ProcessStart(desktopProcess, EsLiteral(K_DESKTOP_EXECUTABLE)); // Start the desktop process. KEventWait(&shutdownEvent, ES_WAIT_NO_TIMEOUT); // Wait for a shutdown request. - scheduler.Shutdown(); // Kill user processes. + ProcessTerminateAll(); // Terminate all user processes. FSShutdown(); // Flush file cache and unmount filesystems. DriversShutdown(); // Inform drivers of shutdown. ArchShutdown(); // Power off or restart the computer. diff --git a/kernel/memory.cpp b/kernel/memory.cpp index 4692791..cf9d01b 100644 --- a/kernel/memory.cpp +++ b/kernel/memory.cpp @@ -1677,7 +1677,7 @@ void MMBalanceThread() { // For every memory region... MMSpace *space = process->vmm; - scheduler.SetTemporaryAddressSpace(space); + ThreadSetTemporaryAddressSpace(space); KMutexAcquire(&space->reserveMutex); LinkedItem *item = pmm.nextRegionToBalance ? &pmm.nextRegionToBalance->itemNonGuard : space->usedRegionsNonGuard.firstItem; @@ -1735,7 +1735,7 @@ void MMBalanceThread() { } KMutexRelease(&space->reserveMutex); - scheduler.SetTemporaryAddressSpace(nullptr); + ThreadSetTemporaryAddressSpace(nullptr); CloseHandleToObject(process, KERNEL_OBJECT_PROCESS); } } @@ -2298,9 +2298,9 @@ void MMInitialise() { pmm.zeroPageEvent.autoReset = true; MMCommit(PHYSICAL_MEMORY_MANIPULATION_REGION_PAGES * K_PAGE_SIZE, true); - pmm.zeroPageThread = scheduler.SpawnThread("MMZero", (uintptr_t) MMZeroPageThread, 0, SPAWN_THREAD_LOW_PRIORITY); - scheduler.SpawnThread("MMBalance", (uintptr_t) MMBalanceThread, 0, ES_FLAGS_DEFAULT)->isPageGenerator = true; - scheduler.SpawnThread("MMObjTrim", (uintptr_t) MMObjectCacheTrimThread, 0, ES_FLAGS_DEFAULT); + pmm.zeroPageThread = ThreadSpawn("MMZero", (uintptr_t) MMZeroPageThread, 0, SPAWN_THREAD_LOW_PRIORITY); + ThreadSpawn("MMBalance", (uintptr_t) MMBalanceThread, 0, ES_FLAGS_DEFAULT)->isPageGenerator = true; + ThreadSpawn("MMObjTrim", (uintptr_t) MMObjectCacheTrimThread, 0, ES_FLAGS_DEFAULT); } { diff --git a/kernel/module.h b/kernel/module.h index 0936bc4..24e86cc 100644 --- a/kernel/module.h +++ b/kernel/module.h @@ -280,7 +280,7 @@ struct KEvent { // Waiting and notifying. Can wait on multiple at once. Can be s bool KEventSet(KEvent *event, bool maybeAlreadySet = false); void KEventReset(KEvent *event); bool KEventPoll(KEvent *event); // TODO Remove this! Currently it is only used by KAudioFillBuffersFromMixer. -bool KEventWait(KEvent *event, uint64_t timeoutMs = ES_WAIT_NO_TIMEOUT); // See KWaitEvents to wait for multiple events. Returns false if the wait timed out. +bool KEventWait(KEvent *event, uint64_t timeoutMs = ES_WAIT_NO_TIMEOUT); // See KEventWaitMultiple to wait for multiple events. Returns false if the wait timed out. struct KWriterLock { // One writer or many readers. K_PRIVATE @@ -424,7 +424,7 @@ bool KThreadCreate(const char *cName, void (*startAddress)(uintptr_t), uintptr_t extern "C" void KThreadTerminate(); // Terminates the current thread. Kernel threads can only be terminated by themselves. void KYield(); -uintptr_t KWaitEvents(KEvent **events, size_t count); +uintptr_t KEventWaitMultiple(KEvent **events, size_t count); struct KWorkGroup { inline void Initialise() { diff --git a/kernel/objects.cpp b/kernel/objects.cpp index fde6500..0b076fb 100644 --- a/kernel/objects.cpp +++ b/kernel/objects.cpp @@ -219,7 +219,7 @@ void CloseHandleToObject(void *object, KernelObjectType type, uint32_t flags) { if (previous == 0) { KernelPanic("CloseHandleToProcess - All handles to process %x have been closed.\n", process); } else if (previous == 1) { - scheduler.RemoveProcess(process); + ProcessRemove(process); } } break; @@ -230,7 +230,7 @@ void CloseHandleToObject(void *object, KernelObjectType type, uint32_t flags) { if (previous == 0) { KernelPanic("CloseHandleToObject - All handles to thread %x have been closed.\n", thread); } else if (previous == 1) { - scheduler.RemoveThread(thread); + ThreadRemove(thread); } } break; diff --git a/kernel/posix.cpp b/kernel/posix.cpp index e1236ef..5d8d713 100644 --- a/kernel/posix.cpp +++ b/kernel/posix.cpp @@ -463,7 +463,7 @@ namespace POSIX { // EsPrint("vfork->\n"); // Allocate the process. - Process *forkProcess = currentThread->posixData->forkProcess = scheduler.SpawnProcess(); + Process *forkProcess = currentThread->posixData->forkProcess = ProcessSpawn(PROCESS_NORMAL); forkProcess->pgid = currentProcess->pgid; // Clone our FDs. @@ -518,7 +518,7 @@ namespace POSIX { // Start the process. - if (!process->Start(path, syscall.arguments[1])) { + if (!ProcessStart(process, path, syscall.arguments[1])) { EsHeapFree(path, 0, K_FIXED); return -ENOMEM; } @@ -591,7 +591,7 @@ namespace POSIX { if (processHandle) { return processHandle; } else { - scheduler.TerminateProcess(currentProcess, syscall.arguments[0]); + ProcessTerminate(currentProcess, syscall.arguments[0]); } } break; diff --git a/kernel/scheduler.cpp b/kernel/scheduler.cpp index a48497c..33c0911 100644 --- a/kernel/scheduler.cpp +++ b/kernel/scheduler.cpp @@ -6,6 +6,37 @@ // TODO Simplify or remove asynchronous task thread semantics. // TODO Break up or remove dispatchSpinlock. +// How thread termination works: +// 1. ThreadTerminate +// - terminating is set to true. +// - If the thread is executing, then on the next context switch, ThreadKill is called on a async task. +// - If it is not executing, ThreadKill is called on a async task. +// - Note, terminatableState must be set to THREAD_TERMINATABLE. +// - When a thread terminates itself, its terminatableState is automatically set to THREAD_TERMINATABLE. +// 2. ThreadKill +// - Removes the thread from the lists, frees the stacks, and sets killedEvent. +// - The thread's handles to itself and its process are closed. +// - If this is the last thread in the process, ProcessKill is called. +// 3. CloseHandleToObject KERNEL_OBJECT_THREAD +// - If the last handle to the thread has been closed, then ThreadRemove is called. +// 4. ThreadRemove +// - The thread structure is deallocated. + +// How process termination works: +// 1. ProcessTerminate (optional) +// - preventNewThreads is set to true. +// - ThreadTerminate is called on each thread, leading to an eventual call to ProcessKill. +// - This is optional because ProcessKill is called if all threads get terminated naturally; in this case, preventNewThreads is never set. +// 2. ProcessKill +// - Removes the process from the lists, destroys the handle table and memory space, and sets killedEvent. +// - Sends a message to Desktop informing it the process was killed. +// - Since ProcessKill is only called from ThreadKill, there is an associated closing of a process handle from the killed thread. +// 3. CloseHandleToObject KERNEL_OBJECT_PROCESS +// - If the last handle to the process has been closed, then ProcessRemove is called. +// 4. ProcessRemove +// - Destroys the message queue, and closes the handle to the executable node. +// - The process and memory space structures are deallocated. + #ifndef IMPLEMENTATION #define THREAD_PRIORITY_NORMAL (0) // Lower value = higher priority. @@ -109,9 +140,6 @@ enum ProcessType { }; struct Process { - bool StartWithNode(KNode *node); - bool Start(char *imagePath, size_t imagePathLength); - MMSpace *vmm; HandleTable handleTable; MessageQueue messageQueue; @@ -139,7 +167,7 @@ struct Process { // Termination: bool allThreadsTerminated; - bool preventNewThreads; // Set by TerminateProcess. + bool preventNewThreads; // Set by ProcessTerminate. int exitStatus; // TODO Remove this. KEvent killedEvent; @@ -163,106 +191,37 @@ struct Scheduler { // External API: void Yield(InterruptContext *context); - -#define SPAWN_THREAD_USERLAND (1 << 0) -#define SPAWN_THREAD_LOW_PRIORITY (1 << 1) -#define SPAWN_THREAD_PAUSED (1 << 2) -#define SPAWN_THREAD_ASYNC_TASK (1 << 3) -#define SPAWN_THREAD_IDLE (1 << 4) - Thread *SpawnThread(const char *cName, uintptr_t startAddress, uintptr_t argument1 = 0, - uint32_t flags = ES_FLAGS_DEFAULT, Process *process = nullptr, uintptr_t argument2 = 0); - void PauseThread(Thread *thread, bool resume /* true to resume, false to pause */); - - Process *SpawnProcess(ProcessType processType = PROCESS_NORMAL); - void PauseProcess(Process *process, bool resume); - void CrashProcess(Process *process, EsCrashReason *reason); - - // How thread termination works: - // 1. TerminateThread - // - terminating is set to true. - // - If the thread is executing, then on the next context switch, KillThread is called on a async task. - // - If it is not executing, KillThread is called on a async task. - // - Note, terminatableState must be set to THREAD_TERMINATABLE. - // - When a thread terminates itself, its terminatableState is automatically set to THREAD_TERMINATABLE. - // 2. KillThread - // - Removes the thread from the lists, frees the stacks, and sets killedEvent. - // - The thread's handles to itself and its process are closed. - // - If this is the last thread in the process, KillProcess is called. - // 3. CloseHandleToObject KERNEL_OBJECT_THREAD - // - If the last handle to the thread has been closed, then RemoveThread is called. - // 4. RemoveThread - // - The thread structure is deallocated. - void TerminateThread(Thread *thread); - - // How process termination works: - // 1. TerminateProcess (optional) - // - preventNewThreads is set to true. - // - TerminateThread is called on each thread, leading to an eventual call to KillProcess. - // - This is optional because KillProcess is called if all threads get terminated naturally; in this case, preventNewThreads is never set. - // 2. KillProcess - // - Removes the process from the lists, destroys the handle table and memory space, and sets killedEvent. - // - Sends a message to Desktop informing it the process was killed. - // - Since KillProcess is only called from KillThread, there is an associated closing of a process handle from the killed thread. - // 3. CloseHandleToObject KERNEL_OBJECT_PROCESS - // - If the last handle to the process has been closed, then RemoveProcess is called. - // 4. RemoveProcess - // - Destroys the message queue, and closes the handle to the executable node. - // - The process and memory space structures are deallocated. - void TerminateProcess(Process *process, int status); - - Process *OpenProcess(uint64_t id); - - void WaitMutex(KMutex *mutex); - uintptr_t WaitEvents(KEvent **events, size_t count); // Returns index of notified object. - - // Set a temporary address space for the current thread. Used by some asynchronous tasks, and the memory manager's balancer. - void SetTemporaryAddressSpace(MMSpace *space); - - void Shutdown(); - - // Internal functions: - void CreateProcessorThreads(CPULocalStorage *local); - - void RemoveProcess(Process *process); // Do not call. Use TerminateProcess/CloseHandleToObject. - void RemoveThread(Thread *thread); // Do not call. Use TerminateThread/CloseHandleToObject. void AddActiveThread(Thread *thread, bool start /* put it at the start of the active list */); // Add an active thread into the queue. void MaybeUpdateActiveList(Thread *thread); // After changing the priority of a thread, call this to move it to the correct active thread queue if needed. - void NotifyObject(LinkedList *blockedThreads, bool unblockAll, Thread *previousMutexOwner = nullptr); void UnblockThread(Thread *unblockedThread, Thread *previousMutexOwner = nullptr); - Thread *PickThread(CPULocalStorage *local); // Pick the next thread to execute. int8_t GetThreadEffectivePriority(Thread *thread); // Variables: KSpinlock dispatchSpinlock; // For accessing synchronisation objects, thread states, scheduling lists, etc. TODO Break this up! - KMutex allThreadsMutex; // For accessing the allThreads list. - KMutex allProcessesMutex; // For accessing the allProcesses list. KSpinlock activeTimersSpinlock; // For accessing the activeTimers lists. - KSpinlock asyncTaskSpinlock; // For accessing the per-CPU asyncTaskList. - - KEvent killedEvent; // Set during shutdown when all processes have been terminated. - volatile uintptr_t blockShutdownProcessCount; - volatile size_t activeProcessCount; - - Pool threadPool, processPool, mmSpacePool; - LinkedList activeThreads[THREAD_PRIORITY_COUNT]; LinkedList pausedThreads; LinkedList activeTimers; + + KMutex allThreadsMutex; // For accessing the allThreads list. + KMutex allProcessesMutex; // For accessing the allProcesses list. + KSpinlock asyncTaskSpinlock; // For accessing the per-CPU asyncTaskList. LinkedList allThreads; LinkedList allProcesses; + Pool threadPool, processPool, mmSpacePool; + EsObjectID nextThreadID, nextProcessID, nextProcessorID; + + KEvent allProcessesTerminatedEvent; // Set during shutdown when all processes have been terminated. + volatile uintptr_t blockShutdownProcessCount; + volatile size_t activeProcessCount; volatile bool started, panic, shutdown; - uint64_t timeMs; - EsObjectID nextThreadID; - EsObjectID nextProcessID; - EsObjectID nextProcessorID; - #ifdef DEBUG_BUILD EsThreadEventLogEntry *volatile threadEventLog; volatile uintptr_t threadEventLogPosition; @@ -270,6 +229,21 @@ struct Scheduler { #endif }; +Process *ProcessSpawn(ProcessType processType); +void ProcessRemove(Process *process); +void ProcessTerminate(Process *process, int status); +void ThreadRemove(Thread *thread); +void ThreadTerminate(Thread *thread); +void ThreadSetTemporaryAddressSpace(MMSpace *space); + +#define SPAWN_THREAD_USERLAND (1 << 0) +#define SPAWN_THREAD_LOW_PRIORITY (1 << 1) +#define SPAWN_THREAD_PAUSED (1 << 2) +#define SPAWN_THREAD_ASYNC_TASK (1 << 3) +#define SPAWN_THREAD_IDLE (1 << 4) +Thread *ThreadSpawn(const char *cName, uintptr_t startAddress, uintptr_t argument1 = 0, + uint32_t flags = ES_FLAGS_DEFAULT, Process *process = nullptr, uintptr_t argument2 = 0); + Process _kernelProcess; Process *kernelProcess = &_kernelProcess; Process *desktopProcess; @@ -379,7 +353,7 @@ void Scheduler::MaybeUpdateActiveList(Thread *thread) { activeThreads[effectivePriority].InsertStart(&thread->item); } -Thread *Scheduler::SpawnThread(const char *cName, uintptr_t startAddress, uintptr_t argument1, uint32_t flags, Process *process, uintptr_t argument2) { +Thread *ThreadSpawn(const char *cName, uintptr_t startAddress, uintptr_t argument1, uint32_t flags, Process *process, uintptr_t argument2) { bool userland = flags & SPAWN_THREAD_USERLAND; Thread *parentThread = GetCurrentThread(); @@ -388,7 +362,7 @@ Thread *Scheduler::SpawnThread(const char *cName, uintptr_t startAddress, uintpt } if (userland && process == kernelProcess) { - KernelPanic("Scheduler::SpawnThread - Cannot add userland thread to kernel process.\n"); + KernelPanic("ThreadSpawn - Cannot add userland thread to kernel process.\n"); } // Adding the thread to the owner's list of threads and adding the thread to a scheduling list @@ -396,10 +370,11 @@ Thread *Scheduler::SpawnThread(const char *cName, uintptr_t startAddress, uintpt KMutexAcquire(&process->threadsMutex); EsDefer(KMutexRelease(&process->threadsMutex)); - if (shutdown && userland) return nullptr; - if (process->preventNewThreads) return nullptr; + if (process->preventNewThreads) { + return nullptr; + } - Thread *thread = (Thread *) threadPool.Add(sizeof(Thread)); + Thread *thread = (Thread *) scheduler.threadPool.Add(sizeof(Thread)); if (!thread) return nullptr; KernelLog(LOG_INFO, "Scheduler", "spawn thread", "Created thread, %x to start at %x\n", thread, startAddress); @@ -438,7 +413,7 @@ Thread *Scheduler::SpawnThread(const char *cName, uintptr_t startAddress, uintpt if (!stack) goto fail; skipStackAllocation:; - // If PauseProcess is called while a thread in that process is spawning a new thread, mark the thread as paused. + // If ProcessPause is called while a thread in that process is spawning a new thread, mark the thread as paused. // This is synchronized under the threadsMutex. thread->paused = (parentThread && process == parentThread->process && parentThread->paused) || (flags & SPAWN_THREAD_PAUSED); @@ -456,7 +431,7 @@ Thread *Scheduler::SpawnThread(const char *cName, uintptr_t startAddress, uintpt thread->userStackCommit = userStackCommit; thread->terminatableState = userland ? THREAD_TERMINATABLE : THREAD_IN_SYSCALL; thread->type = (flags & SPAWN_THREAD_ASYNC_TASK) ? THREAD_ASYNC_TASK : (flags & SPAWN_THREAD_IDLE) ? THREAD_IDLE : THREAD_NORMAL; - thread->id = __sync_fetch_and_add(&nextThreadID, 1); + thread->id = __sync_fetch_and_add(&scheduler.nextThreadID, 1); thread->process = process; thread->item.thisItem = thread; thread->allItem.thisItem = thread; @@ -472,9 +447,9 @@ Thread *Scheduler::SpawnThread(const char *cName, uintptr_t startAddress, uintpt process->threads.InsertEnd(&thread->processItem); - KMutexAcquire(&allThreadsMutex); - allThreads.InsertStart(&thread->allItem); - KMutexRelease(&allThreadsMutex); + KMutexAcquire(&scheduler.allThreadsMutex); + scheduler.allThreads.InsertStart(&thread->allItem); + KMutexRelease(&scheduler.allThreadsMutex); // Each thread owns a handles to the owner process. // This makes sure the process isn't destroyed before all its threads have been destroyed. @@ -487,9 +462,9 @@ Thread *Scheduler::SpawnThread(const char *cName, uintptr_t startAddress, uintpt if (thread->type == THREAD_NORMAL) { // Add the thread to the start of the active thread list to make sure that it runs immediately. - KSpinlockAcquire(&dispatchSpinlock); - AddActiveThread(thread, true); - KSpinlockRelease(&dispatchSpinlock); + KSpinlockAcquire(&scheduler.dispatchSpinlock); + scheduler.AddActiveThread(thread, true); + KSpinlockRelease(&scheduler.dispatchSpinlock); } else { // Idle and asynchronous task threads don't need to be added to a scheduling list. } @@ -501,17 +476,17 @@ Thread *Scheduler::SpawnThread(const char *cName, uintptr_t startAddress, uintpt fail:; if (stack) MMFree(process->vmm, (void *) stack); if (kernelStack) MMFree(kernelMMSpace, (void *) kernelStack); - threadPool.Remove(thread); + scheduler.threadPool.Remove(thread); return nullptr; } -void KillProcess(Process *process) { - // This function should only be called by KillThread, when the last thread in the process exits. +void ProcessKill(Process *process) { + // This function should only be called by ThreadKill, when the last thread in the process exits. // There should be at least one remaining handle to the process here, owned by that thread. - // It will be closed at the end of the KillThread function. + // It will be closed at the end of the ThreadKill function. if (!process->handles) { - KernelPanic("KillProcess - Process %x is on the allProcesses list but there are no handles to it.\n", process); + KernelPanic("ProcessKill - Process %x is on the allProcesses list but there are no handles to it.\n", process); } KernelLog(LOG_INFO, "Scheduler", "killing process", "Killing process (%d) %x...\n", process->id, process); @@ -559,7 +534,7 @@ void KillProcess(Process *process) { process->handleTable.Destroy(); // Destroy the virtual memory space. - // Don't actually deallocate it yet though; that is done on an async task queued by RemoveProcess. + // Don't actually deallocate it yet though; that is done on an async task queued by ProcessRemove. // This must be destroyed after the handle table! MMSpaceDestroy(process->vmm); @@ -573,9 +548,9 @@ void KillProcess(Process *process) { } } -void KillThread(KAsyncTask *task) { +void ThreadKill(KAsyncTask *task) { Thread *thread = EsContainerOf(Thread, killAsyncTask, task); - scheduler.SetTemporaryAddressSpace(thread->process->vmm); + ThreadSetTemporaryAddressSpace(thread->process->vmm); KMutexAcquire(&scheduler.allThreadsMutex); scheduler.allThreads.Remove(&thread->allItem); @@ -590,7 +565,7 @@ void KillThread(KAsyncTask *task) { "Killing thread (ID %d, %d remain in process %d) %x...\n", thread->id, thread->process->threads.count, thread->process->id, thread); if (lastThread) { - KillProcess(thread->process); + ProcessKill(thread->process); } MMFree(kernelMMSpace, (void *) thread->kernelStackBase); @@ -603,7 +578,7 @@ void KillThread(KAsyncTask *task) { CloseHandleToObject(thread, KERNEL_OBJECT_THREAD); } -void Scheduler::TerminateProcess(Process *process, int status) { +void ProcessTerminate(Process *process, int status) { KMutexAcquire(&process->threadsMutex); KernelLog(LOG_INFO, "Scheduler", "terminate process", "Terminating process %d '%z' with status %i...\n", @@ -622,25 +597,25 @@ void Scheduler::TerminateProcess(Process *process, int status) { thread = thread->nextItem; if (threadObject != currentThread) { - TerminateThread(threadObject); + ThreadTerminate(threadObject); } else if (isCurrentProcess) { foundCurrentThread = true; } else { - KernelPanic("Scheduler::TerminateProcess - Found current thread in the wrong process?!\n"); + KernelPanic("Scheduler::ProcessTerminate - Found current thread in the wrong process?!\n"); } } KMutexRelease(&process->threadsMutex); if (!foundCurrentThread && isCurrentProcess) { - KernelPanic("Scheduler::TerminateProcess - Could not find current thread in the current process?!\n"); + KernelPanic("Scheduler::ProcessTerminate - Could not find current thread in the current process?!\n"); } else if (isCurrentProcess) { // This doesn't return. - TerminateThread(currentThread); + ThreadTerminate(currentThread); } } -void Scheduler::TerminateThread(Thread *thread) { +void ThreadTerminate(Thread *thread) { // Overview: // Set terminating true, and paused false. // Is this the current thread? @@ -678,7 +653,7 @@ void Scheduler::TerminateThread(Thread *thread) { // We cannot return to the previous function as it expects to be killed. ProcessorFakeTimerInterrupt(); - KernelPanic("Scheduler::TerminateThread - ProcessorFakeTimerInterrupt returned.\n"); + KernelPanic("Scheduler::ThreadTerminate - ProcessorFakeTimerInterrupt returned.\n"); } else { if (thread->terminatableState == THREAD_TERMINATABLE) { // We're in user code.. @@ -688,13 +663,13 @@ void Scheduler::TerminateThread(Thread *thread) { // is pre-empted, it will be terminated. } else { if (thread->state != THREAD_ACTIVE) { - KernelPanic("Scheduler::TerminateThread - Terminatable thread non-active.\n"); + KernelPanic("Scheduler::ThreadTerminate - Terminatable thread non-active.\n"); } // The thread is terminatable and it isn't executing. // Remove it from its queue, and then remove the thread. thread->item.RemoveFromList(); - KRegisterAsyncTask(&thread->killAsyncTask, KillThread); + KRegisterAsyncTask(&thread->killAsyncTask, ThreadKill); yield = true; } } else if (thread->terminatableState == THREAD_USER_BLOCK_REQUEST) { @@ -709,7 +684,7 @@ void Scheduler::TerminateThread(Thread *thread) { // Unblock the thread. // See comment above. if (thread->state == THREAD_WAITING_MUTEX || thread->state == THREAD_WAITING_EVENT) { - UnblockThread(thread); + scheduler.UnblockThread(thread); } } } else { @@ -725,7 +700,7 @@ void Scheduler::TerminateThread(Thread *thread) { if (yield) ProcessorFakeTimerInterrupt(); // Process the asynchronous task. } -void NewProcess() { +void ProcessLoadExecutable() { Process *thisProcess = GetCurrentThread()->process; KernelLog(LOG_INFO, "Scheduler", "new process", @@ -795,7 +770,7 @@ void NewProcess() { if (thisProcess->creationFlags & ES_PROCESS_CREATE_PAUSED) threadFlags |= SPAWN_THREAD_PAUSED; thisProcess->executableState = ES_PROCESS_EXECUTABLE_LOADED; - thisProcess->executableMainThread = scheduler.SpawnThread("MainThread", api.startAddress, + thisProcess->executableMainThread = ThreadSpawn("MainThread", api.startAddress, (uintptr_t) startupInformation, threadFlags, thisProcess); if (!thisProcess->executableMainThread) { @@ -805,7 +780,7 @@ void NewProcess() { if (!success) { if (thisProcess->type != PROCESS_NORMAL) { - KernelPanic("NewProcess - Failed to start the critical process %z.\n", thisProcess->cExecutableName); + KernelPanic("ProcessLoadExecutable - Failed to start the critical process %z.\n", thisProcess->cExecutableName); } thisProcess->executableState = ES_PROCESS_EXECUTABLE_FAILED_TO_LOAD; @@ -814,37 +789,17 @@ void NewProcess() { KEventSet(&thisProcess->executableLoadAttemptComplete); } -bool Process::Start(char *imagePath, size_t imagePathLength) { - uint64_t flags = ES_FILE_READ | ES_NODE_FAIL_IF_NOT_FOUND; - KNodeInformation node = FSNodeOpen(imagePath, imagePathLength, flags); - bool result = false; - - if (!ES_CHECK_ERROR(node.error)) { - if (node.node->directoryEntry->type == ES_NODE_FILE) { - result = StartWithNode(node.node); - } - - CloseHandleToObject(node.node, KERNEL_OBJECT_NODE, flags); - } - - if (!result && type == PROCESS_DESKTOP) { - KernelPanic("Process::Start - Could not load the desktop executable.\n"); - } - - return result; -} - -bool Process::StartWithNode(KNode *node) { +bool ProcessStartWithNode(Process *process, KNode *node) { // Make sure nobody has tried to start the process. KSpinlockAcquire(&scheduler.dispatchSpinlock); - if (executableStartRequest) { + if (process->executableStartRequest) { KSpinlockRelease(&scheduler.dispatchSpinlock); return false; } - executableStartRequest = true; + process->executableStartRequest = true; KSpinlockRelease(&scheduler.dispatchSpinlock); @@ -853,45 +808,45 @@ bool Process::StartWithNode(KNode *node) { KWriterLockTake(&node->writerLock, K_LOCK_SHARED); size_t byteCount = node->directoryEntry->item.key.longKeyBytes; if (byteCount > ES_SNAPSHOT_MAX_PROCESS_NAME_LENGTH) byteCount = ES_SNAPSHOT_MAX_PROCESS_NAME_LENGTH; - EsMemoryCopy(cExecutableName, node->directoryEntry->item.key.longKey, byteCount); - cExecutableName[byteCount] = 0; + EsMemoryCopy(process->cExecutableName, node->directoryEntry->item.key.longKey, byteCount); + process->cExecutableName[byteCount] = 0; KWriterLockReturn(&node->writerLock, K_LOCK_SHARED); // Initialise the memory space. - bool success = MMSpaceInitialise(vmm); + bool success = MMSpaceInitialise(process->vmm); if (!success) return false; // NOTE If you change these flags, make sure to update the flags when the handle is closed! if (!OpenHandleToObject(node, KERNEL_OBJECT_NODE, ES_FILE_READ)) { - KernelPanic("Process::StartWithNode - Could not open read handle to node %x.\n", node); + KernelPanic("ProcessStartWithNode - Could not open read handle to node %x.\n", node); } - executableNode = node; + process->executableNode = node; __sync_fetch_and_add(&scheduler.activeProcessCount, 1); __sync_fetch_and_add(&scheduler.blockShutdownProcessCount, 1); // Add the process to the list of all processes, // and spawn the kernel thread to load the executable. // This is synchronized under allProcessesMutex so that the process can't be terminated or paused - // until newProcessThread has been spawned. + // until loadExecutableThread has been spawned. KMutexAcquire(&scheduler.allProcessesMutex); - scheduler.allProcesses.InsertEnd(&allItem); - Thread *newProcessThread = scheduler.SpawnThread("NewProcess", (uintptr_t) NewProcess, 0, ES_FLAGS_DEFAULT, this); + scheduler.allProcesses.InsertEnd(&process->allItem); + Thread *loadExecutableThread = ThreadSpawn("ExecLoad", (uintptr_t) ProcessLoadExecutable, 0, ES_FLAGS_DEFAULT, process); KMutexRelease(&scheduler.allProcessesMutex); - if (!newProcessThread) { - CloseHandleToObject(this, KERNEL_OBJECT_PROCESS); + if (!loadExecutableThread) { + CloseHandleToObject(process, KERNEL_OBJECT_PROCESS); return false; } // Wait for the executable to be loaded. - CloseHandleToObject(newProcessThread, KERNEL_OBJECT_THREAD); - KEventWait(&executableLoadAttemptComplete, ES_WAIT_NO_TIMEOUT); + CloseHandleToObject(loadExecutableThread, KERNEL_OBJECT_THREAD); + KEventWait(&process->executableLoadAttemptComplete, ES_WAIT_NO_TIMEOUT); - if (executableState == ES_PROCESS_EXECUTABLE_FAILED_TO_LOAD) { + if (process->executableState == ES_PROCESS_EXECUTABLE_FAILED_TO_LOAD) { KernelLog(LOG_ERROR, "Scheduler", "executable load failure", "Executable failed to load.\n"); return false; } @@ -899,23 +854,43 @@ bool Process::StartWithNode(KNode *node) { return true; } -Process *Scheduler::SpawnProcess(ProcessType processType) { - if (shutdown) return nullptr; +bool ProcessStart(Process *process, char *imagePath, size_t imagePathLength) { + uint64_t flags = ES_FILE_READ | ES_NODE_FAIL_IF_NOT_FOUND; + KNodeInformation node = FSNodeOpen(imagePath, imagePathLength, flags); + bool result = false; - Process *process = processType == PROCESS_KERNEL ? kernelProcess : (Process *) processPool.Add(sizeof(Process)); + if (!ES_CHECK_ERROR(node.error)) { + if (node.node->directoryEntry->type == ES_NODE_FILE) { + result = ProcessStartWithNode(process, node.node); + } + + CloseHandleToObject(node.node, KERNEL_OBJECT_NODE, flags); + } + + if (!result && process->type == PROCESS_DESKTOP) { + KernelPanic("ProcessStart - Could not load the desktop executable.\n"); + } + + return result; +} + +Process *ProcessSpawn(ProcessType processType) { + if (scheduler.shutdown) return nullptr; + + Process *process = processType == PROCESS_KERNEL ? kernelProcess : (Process *) scheduler.processPool.Add(sizeof(Process)); if (!process) { return nullptr; } - process->vmm = processType == PROCESS_KERNEL ? kernelMMSpace : (MMSpace *) mmSpacePool.Add(sizeof(MMSpace)); + process->vmm = processType == PROCESS_KERNEL ? kernelMMSpace : (MMSpace *) scheduler.mmSpacePool.Add(sizeof(MMSpace)); if (!process->vmm) { - processPool.Remove(process); + scheduler.processPool.Remove(process); return nullptr; } - process->id = __sync_fetch_and_add(&nextProcessID, 1); + process->id = __sync_fetch_and_add(&scheduler.nextProcessID, 1); process->vmm->referenceCount = 1; process->allItem.thisItem = process; process->handles = 1; @@ -931,15 +906,15 @@ Process *Scheduler::SpawnProcess(ProcessType processType) { return process; } -void Scheduler::SetTemporaryAddressSpace(MMSpace *space) { - KSpinlockAcquire(&dispatchSpinlock); +void ThreadSetTemporaryAddressSpace(MMSpace *space) { + KSpinlockAcquire(&scheduler.dispatchSpinlock); Thread *thread = GetCurrentThread(); MMSpace *oldSpace = thread->temporaryAddressSpace ?: kernelMMSpace; thread->temporaryAddressSpace = space; MMSpace *newSpace = space ?: kernelMMSpace; MMSpaceOpenReference(newSpace); ProcessorSetAddressSpace(&newSpace->data); - KSpinlockRelease(&dispatchSpinlock); + KSpinlockRelease(&scheduler.dispatchSpinlock); MMSpaceCloseReference(oldSpace); } @@ -959,15 +934,15 @@ void AsyncTaskThread() { item->Remove(); KSpinlockRelease(&scheduler.asyncTaskSpinlock); callback(task); // This may cause the task to be deallocated. - scheduler.SetTemporaryAddressSpace(nullptr); // The task may have modified the address space. + ThreadSetTemporaryAddressSpace(nullptr); // The task may have modified the address space. local->inAsyncTask = false; } } } void Scheduler::CreateProcessorThreads(CPULocalStorage *local) { - local->asyncTaskThread = SpawnThread("AsyncTasks", (uintptr_t) AsyncTaskThread, 0, SPAWN_THREAD_ASYNC_TASK); - local->currentThread = local->idleThread = SpawnThread("Idle", 0, 0, SPAWN_THREAD_IDLE); + local->asyncTaskThread = ThreadSpawn("AsyncTasks", (uintptr_t) AsyncTaskThread, 0, SPAWN_THREAD_ASYNC_TASK); + local->currentThread = local->idleThread = ThreadSpawn("Idle", 0, 0, SPAWN_THREAD_IDLE); local->processorID = __sync_fetch_and_add(&nextProcessorID, 1); if (local->processorID >= K_MAX_PROCESSORS) { @@ -975,7 +950,7 @@ void Scheduler::CreateProcessorThreads(CPULocalStorage *local) { } } -void Scheduler::RemoveProcess(Process *process) { +void ProcessRemove(Process *process) { KernelLog(LOG_INFO, "Scheduler", "remove process", "Removing process %d.\n", process->id); if (process->executableNode) { @@ -997,11 +972,11 @@ void Scheduler::RemoveProcess(Process *process) { scheduler.processPool.Remove(process); if (1 == __sync_fetch_and_sub(&scheduler.blockShutdownProcessCount, 1)) { - KEventSet(&scheduler.killedEvent); + KEventSet(&scheduler.allProcessesTerminatedEvent); } } -void Scheduler::RemoveThread(Thread *thread) { +void ThreadRemove(Thread *thread) { KernelLog(LOG_INFO, "Scheduler", "remove thread", "Removing thread %d.\n", thread->id); // The last handle to the thread has been closed, @@ -1021,17 +996,75 @@ void Scheduler::RemoveThread(Thread *thread) { scheduler.threadPool.Remove(thread); } -void Scheduler::CrashProcess(Process *process, EsCrashReason *crashReason) { +void ThreadPause(Thread *thread, bool resume) { + KSpinlockAcquire(&scheduler.dispatchSpinlock); + + if (thread->paused == !resume) { + return; + } + + thread->paused = !resume; + + if (!resume && thread->terminatableState == THREAD_TERMINATABLE) { + if (thread->state == THREAD_ACTIVE) { + if (thread->executing) { + if (thread == GetCurrentThread()) { + KSpinlockRelease(&scheduler.dispatchSpinlock); + + // Yield. + ProcessorFakeTimerInterrupt(); + + if (thread->paused) { + KernelPanic("ThreadPause - Current thread incorrectly resumed.\n"); + } + } else { + // The thread is executing, but on a different processor. + // Send them an IPI to stop. + ProcessorSendYieldIPI(thread); + // TODO The interrupt context might not be set at this point. + } + } else { + // Remove the thread from the active queue, and put it into the paused queue. + thread->item.RemoveFromList(); + scheduler.AddActiveThread(thread, false); + } + } else { + // The thread doesn't need to be in the paused queue as it won't run anyway. + // If it is unblocked, then AddActiveThread will put it into the correct queue. + } + } else if (resume && thread->item.list == &scheduler.pausedThreads) { + // Remove the thread from the paused queue, and put it into the active queue. + scheduler.pausedThreads.Remove(&thread->item); + scheduler.AddActiveThread(thread, false); + } + + KSpinlockRelease(&scheduler.dispatchSpinlock); +} + +void ProcessPause(Process *process, bool resume) { + KMutexAcquire(&process->threadsMutex); + LinkedItem *thread = process->threads.firstItem; + + while (thread) { + Thread *threadObject = thread->thisItem; + thread = thread->nextItem; + ThreadPause(threadObject, resume); + } + + KMutexRelease(&process->threadsMutex); +} + +void ProcessCrash(Process *process, EsCrashReason *crashReason) { if (process == kernelProcess) { - KernelPanic("Scheduler::CrashProcess - Kernel process has crashed (%d).\n", crashReason->errorCode); + KernelPanic("ProcessCrash - Kernel process has crashed (%d).\n", crashReason->errorCode); } if (process->type != PROCESS_NORMAL) { - KernelPanic("Scheduler::CrashProcess - A critical process has crashed (%d).\n", crashReason->errorCode); + KernelPanic("ProcessCrash - A critical process has crashed (%d).\n", crashReason->errorCode); } if (GetCurrentThread()->process != process) { - KernelPanic("Scheduler::CrashProcess - Attempt to crash process from different process.\n"); + KernelPanic("ProcessCrash - Attempt to crash process from different process.\n"); } KMutexAcquire(&process->crashMutex); @@ -1047,7 +1080,7 @@ void Scheduler::CrashProcess(Process *process, EsCrashReason *crashReason) { EsMemoryCopy(&process->crashReason, crashReason, sizeof(EsCrashReason)); - if (!shutdown) { + if (!scheduler.shutdown) { _EsMessageWithObject m; EsMemoryZero(&m, sizeof(m)); m.message.type = ES_MSG_APPLICATION_CRASH; @@ -1059,65 +1092,7 @@ void Scheduler::CrashProcess(Process *process, EsCrashReason *crashReason) { KMutexRelease(&process->crashMutex); // TODO Shouldn't this be done before sending the desktop message? - scheduler.PauseProcess(GetCurrentThread()->process, false); -} - -void Scheduler::PauseThread(Thread *thread, bool resume) { - KSpinlockAcquire(&dispatchSpinlock); - - if (thread->paused == !resume) { - return; - } - - thread->paused = !resume; - - if (!resume && thread->terminatableState == THREAD_TERMINATABLE) { - if (thread->state == THREAD_ACTIVE) { - if (thread->executing) { - if (thread == GetCurrentThread()) { - KSpinlockRelease(&dispatchSpinlock); - - // Yield. - ProcessorFakeTimerInterrupt(); - - if (thread->paused) { - KernelPanic("Scheduler::PauseThread - Current thread incorrectly resumed.\n"); - } - } else { - // The thread is executing, but on a different processor. - // Send them an IPI to stop. - ProcessorSendYieldIPI(thread); - // TODO The interrupt context might not be set at this point. - } - } else { - // Remove the thread from the active queue, and put it into the paused queue. - thread->item.RemoveFromList(); - AddActiveThread(thread, false); - } - } else { - // The thread doesn't need to be in the paused queue as it won't run anyway. - // If it is unblocked, then AddActiveThread will put it into the correct queue. - } - } else if (resume && thread->item.list == &pausedThreads) { - // Remove the thread from the paused queue, and put it into the active queue. - pausedThreads.Remove(&thread->item); - AddActiveThread(thread, false); - } - - KSpinlockRelease(&dispatchSpinlock); -} - -void Scheduler::PauseProcess(Process *process, bool resume) { - KMutexAcquire(&process->threadsMutex); - LinkedItem *thread = process->threads.firstItem; - - while (thread) { - Thread *threadObject = thread->thisItem; - thread = thread->nextItem; - PauseThread(threadObject, resume); - } - - KMutexRelease(&process->threadsMutex); + ProcessPause(GetCurrentThread()->process, false); } Thread *Scheduler::PickThread(CPULocalStorage *local) { @@ -1205,7 +1180,7 @@ void Scheduler::Yield(InterruptContext *context) { if (killThread) { local->currentThread->state = THREAD_TERMINATED; KernelLog(LOG_INFO, "Scheduler", "terminate yielded thread", "Terminated yielded thread %x\n", local->currentThread); - KRegisterAsyncTask(&local->currentThread->killAsyncTask, KillThread); + KRegisterAsyncTask(&local->currentThread->killAsyncTask, ThreadKill); } // If the thread is waiting for an object to be notified, put it in the relevant blockedThreads list. @@ -1296,35 +1271,35 @@ void Scheduler::Yield(InterruptContext *context) { KernelPanic("Scheduler::Yield - DoContextSwitch unexpectedly returned.\n"); } -void Scheduler::Shutdown() { +void ProcessTerminateAll() { scheduler.shutdown = true; // Close our handle to the desktop process. CloseHandleToObject(desktopProcess->executableMainThread, KERNEL_OBJECT_THREAD); CloseHandleToObject(desktopProcess, KERNEL_OBJECT_PROCESS); - KernelLog(LOG_INFO, "Scheduler", "killing all processes", "Scheduler::Destroy - Killing all processes....\n"); + KernelLog(LOG_INFO, "Scheduler", "terminating all processes", "ProcessTerminateAll - Terminating all processes....\n"); while (true) { - KMutexAcquire(&allProcessesMutex); - Process *process = allProcesses.firstItem->thisItem; + KMutexAcquire(&scheduler.allProcessesMutex); + Process *process = scheduler.allProcesses.firstItem->thisItem; while (process && (process->preventNewThreads || process == kernelProcess)) { LinkedItem *item = process->allItem.nextItem; process = item ? item->thisItem : nullptr; } - KMutexRelease(&allProcessesMutex); + KMutexRelease(&scheduler.allProcessesMutex); if (!process) break; - TerminateProcess(process, -1); + ProcessTerminate(process, -1); } - KEventWait(&killedEvent); + KEventWait(&scheduler.allProcessesTerminatedEvent); } -Process *Scheduler::OpenProcess(uint64_t id) { - KMutexAcquire(&allProcessesMutex); +Process *ProcessOpen(uint64_t id) { + KMutexAcquire(&scheduler.allProcessesMutex); LinkedItem *item = scheduler.allProcesses.firstItem; Process *result = nullptr; @@ -1340,16 +1315,16 @@ Process *Scheduler::OpenProcess(uint64_t id) { item = item->nextItem; } - KMutexRelease(&allProcessesMutex); + KMutexRelease(&scheduler.allProcessesMutex); return result; } bool KThreadCreate(const char *cName, void (*startAddress)(uintptr_t), uintptr_t argument) { - return scheduler.SpawnThread(cName, (uintptr_t) startAddress, argument) ? true : false; + return ThreadSpawn(cName, (uintptr_t) startAddress, argument) ? true : false; } void KThreadTerminate() { - scheduler.TerminateThread(GetCurrentThread()); + ThreadTerminate(GetCurrentThread()); } void KYield() { diff --git a/kernel/synchronisation.cpp b/kernel/synchronisation.cpp index 37926fb..594d02e 100644 --- a/kernel/synchronisation.cpp +++ b/kernel/synchronisation.cpp @@ -140,10 +140,34 @@ bool KMutexAcquire(KMutex *mutex) { __sync_synchronize(); if (GetLocalStorage() && GetLocalStorage()->schedulerReady) { + if (currentThread->state != THREAD_ACTIVE) { + KernelPanic("KWaitMutex - Attempting to wait on a mutex in a non-active thread.\n"); + } + + currentThread->blocking.mutex = mutex; + __sync_synchronize(); + // Instead of spinning on the lock, // let's tell the scheduler to not schedule this thread // until it's released. - scheduler.WaitMutex(mutex); + currentThread->state = THREAD_WAITING_MUTEX; + + KSpinlockAcquire(&scheduler.dispatchSpinlock); + // Is the owner of this mutex executing? + // If not, there's no point in spinning on it. + bool spin = mutex && mutex->owner && mutex->owner->executing; + KSpinlockRelease(&scheduler.dispatchSpinlock); + + if (!spin && currentThread->blocking.mutex->owner) { + ProcessorFakeTimerInterrupt(); + } + + // Early exit if this is a user request to block the thread and the thread is terminating. + while ((!currentThread->terminating || currentThread->terminatableState != THREAD_USER_BLOCK_REQUEST) && mutex->owner) { + currentThread->state = THREAD_WAITING_MUTEX; + } + + currentThread->state = THREAD_ACTIVE; if (currentThread->terminating && currentThread->terminatableState == THREAD_USER_BLOCK_REQUEST) { // We didn't acquire the mutex because the thread is terminating. @@ -358,13 +382,13 @@ bool KEventWait(KEvent *_this, uint64_t timeoutMs) { events[0] = _this; if (timeoutMs == (uint64_t) ES_WAIT_NO_TIMEOUT) { - int index = scheduler.WaitEvents(events, 1); + int index = KEventWaitMultiple(events, 1); return index == 0; } else { KTimer timer = {}; KTimerSet(&timer, timeoutMs); events[1] = &timer.event; - int index = scheduler.WaitEvents(events, 2); + int index = KEventWaitMultiple(events, 2); KTimerRemove(&timer); return index == 0; } @@ -624,42 +648,13 @@ void KTimerRemove(KTimer *timer) { KSpinlockRelease(&scheduler.activeTimersSpinlock); } -void Scheduler::WaitMutex(KMutex *mutex) { - Thread *thread = GetCurrentThread(); - - if (thread->state != THREAD_ACTIVE) { - KernelPanic("Scheduler::WaitMutex - Attempting to wait on a mutex in a non-active thread.\n"); - } - - thread->blocking.mutex = mutex; - __sync_synchronize(); - thread->state = THREAD_WAITING_MUTEX; - - KSpinlockAcquire(&dispatchSpinlock); - // Is the owner of this mutex executing? - // If not, there's no point in spinning on it. - bool spin = mutex && mutex->owner && mutex->owner->executing; - KSpinlockRelease(&dispatchSpinlock); - - if (!spin && thread->blocking.mutex->owner) { - ProcessorFakeTimerInterrupt(); - } - - // Early exit if this is a user request to block the thread and the thread is terminating. - while ((!thread->terminating || thread->terminatableState != THREAD_USER_BLOCK_REQUEST) && mutex->owner) { - thread->state = THREAD_WAITING_MUTEX; - } - - thread->state = THREAD_ACTIVE; -} - -uintptr_t Scheduler::WaitEvents(KEvent **events, size_t count) { +uintptr_t KEventWaitMultiple(KEvent **events, size_t count) { if (count > ES_MAX_WAIT_COUNT) { - KernelPanic("Scheduler::WaitEvents - count (%d) > ES_MAX_WAIT_COUNT (%d)\n", count, ES_MAX_WAIT_COUNT); + KernelPanic("KEventWaitMultiple - count (%d) > ES_MAX_WAIT_COUNT (%d)\n", count, ES_MAX_WAIT_COUNT); } else if (!count) { - KernelPanic("Scheduler::WaitEvents - Count is 0.\n"); + KernelPanic("KEventWaitMultiple - Count is 0.\n"); } else if (!ProcessorAreInterruptsEnabled()) { - KernelPanic("Scheduler::WaitEvents - Interrupts disabled.\n"); + KernelPanic("KEventWaitMultiple - Interrupts disabled.\n"); } Thread *thread = GetCurrentThread(); @@ -703,10 +698,6 @@ uintptr_t Scheduler::WaitEvents(KEvent **events, size_t count) { return -1; // Exited from termination. } -uintptr_t KWaitEvents(KEvent **events, size_t count) { - return scheduler.WaitEvents(events, count); -} - void Scheduler::UnblockThread(Thread *unblockedThread, Thread *previousMutexOwner) { KSpinlockAssertLocked(&dispatchSpinlock); diff --git a/kernel/syscall.cpp b/kernel/syscall.cpp index 86c288c..d21e059 100644 --- a/kernel/syscall.cpp +++ b/kernel/syscall.cpp @@ -176,7 +176,7 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_PROCESS_CREATE) { // Create the process. if (error == ES_SUCCESS) { - Process *process = scheduler.SpawnProcess(); + Process *process = ProcessSpawn(PROCESS_NORMAL); if (!process) { error = ES_ERROR_INSUFFICIENT_RESOURCES; @@ -198,7 +198,7 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_PROCESS_CREATE) { // Start the process. - if (error != ES_SUCCESS || !process->StartWithNode(executableObject)) { + if (error != ES_SUCCESS || !ProcessStartWithNode(process, executableObject)) { error = ES_ERROR_UNKNOWN; // TODO. CloseHandleToObject(process, KERNEL_OBJECT_PROCESS); } else { @@ -498,10 +498,10 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_THREAD_TERMINATE) { { SYSCALL_HANDLE(argument0, KERNEL_OBJECT_THREAD, thread, Thread); if (thread == currentThread) self = true; - else scheduler.TerminateThread(thread); + else ThreadTerminate(thread); } - if (self) scheduler.TerminateThread(currentThread); + if (self) ThreadTerminate(currentThread); SYSCALL_RETURN(ES_SUCCESS, false); } @@ -514,10 +514,10 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_PROCESS_TERMINATE) { { SYSCALL_HANDLE(argument0, KERNEL_OBJECT_PROCESS, process, Process); if (process == currentProcess) self = true; - else scheduler.TerminateProcess(process, argument1); + else ProcessTerminate(process, argument1); } - if (self) scheduler.TerminateProcess(currentProcess, argument1); + if (self) ProcessTerminate(currentProcess, argument1); SYSCALL_RETURN(ES_SUCCESS, false); } @@ -525,14 +525,13 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_PROCESS_TERMINATE) { SYSCALL_IMPLEMENT(ES_SYSCALL_THREAD_CREATE) { EsThreadInformation thread; EsMemoryZero(&thread, sizeof(EsThreadInformation)); - Thread *threadObject = scheduler.SpawnThread("Syscall", argument0, argument3, SPAWN_THREAD_USERLAND, currentProcess, argument1); + Thread *threadObject = ThreadSpawn("Syscall", argument0, argument3, SPAWN_THREAD_USERLAND, currentProcess, argument1); if (!threadObject) { SYSCALL_RETURN(ES_ERROR_INSUFFICIENT_RESOURCES, false); } thread.tid = threadObject->id; - thread.handle = currentProcess->handleTable.OpenHandle(threadObject, 0, KERNEL_OBJECT_THREAD); SYSCALL_WRITE(argument2, &thread, sizeof(EsThreadInformation)); @@ -849,7 +848,7 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_WAIT) { } currentThread->terminatableState = THREAD_USER_BLOCK_REQUEST; - waitReturnValue = scheduler.WaitEvents(events, waitObjectCount); + waitReturnValue = KEventWaitMultiple(events, waitObjectCount); currentThread->terminatableState = THREAD_IN_SYSCALL; if (waitReturnValue == argument1) { @@ -1082,7 +1081,7 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_WINDOW_GET_BOUNDS) { SYSCALL_IMPLEMENT(ES_SYSCALL_PROCESS_PAUSE) { SYSCALL_HANDLE(argument0, KERNEL_OBJECT_PROCESS, process, Process); - scheduler.PauseProcess(process, (bool) argument1); + ProcessPause(process, (bool) argument1); SYSCALL_RETURN(ES_SUCCESS, false); } @@ -1303,7 +1302,7 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_PROCESSOR_COUNT) { SYSCALL_IMPLEMENT(ES_SYSCALL_PROCESS_OPEN) { SYSCALL_PERMISSION(ES_PERMISSION_PROCESS_OPEN); - Process *process = scheduler.OpenProcess(argument0); + Process *process = ProcessOpen(argument0); if (process) { SYSCALL_RETURN(currentProcess->handleTable.OpenHandle(process, 0, KERNEL_OBJECT_PROCESS), false); @@ -1755,7 +1754,7 @@ uintptr_t DoSyscall(EsSyscallType index, uintptr_t argument0, uintptr_t argument reason.duringSystemCall = index; KernelLog(LOG_ERROR, "Syscall", "syscall failure", "Process crashed during system call [%x, %x, %x, %x, %x]\n", index, argument0, argument1, argument2, argument3); - scheduler.CrashProcess(currentProcess, &reason); + ProcessCrash(currentProcess, &reason); } } diff --git a/shared/strings.cpp b/shared/strings.cpp index bf15a22..f05f723 100644 --- a/shared/strings.cpp +++ b/shared/strings.cpp @@ -90,8 +90,8 @@ DEFINE_INTERFACE_STRING(CommonUnitMilliseconds, " ms"); // Desktop. DEFINE_INTERFACE_STRING(DesktopNewTabTitle, "New Tab"); -DEFINE_INTERFACE_STRING(DesktopShutdownTitle, "Shutdown"); -DEFINE_INTERFACE_STRING(DesktopShutdownAction, "Shutdown"); +DEFINE_INTERFACE_STRING(DesktopShutdownTitle, "Shut Down"); +DEFINE_INTERFACE_STRING(DesktopShutdownAction, "Shut down"); DEFINE_INTERFACE_STRING(DesktopRestartAction, "Restart"); DEFINE_INTERFACE_STRING(DesktopForceQuit, "Force quit"); DEFINE_INTERFACE_STRING(DesktopCrashedApplication, "The application has crashed. If you're a developer, more information is available in System Monitor.");