cleanup scheduler

This commit is contained in:
nakst 2021-11-13 15:18:37 +00:00
parent 52e35a7636
commit 2436959c84
13 changed files with 288 additions and 323 deletions

View File

@ -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:;

View File

@ -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");

View File

@ -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));

View File

@ -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;
}

View File

@ -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.

View File

@ -1677,7 +1677,7 @@ void MMBalanceThread() {
// For every memory region...
MMSpace *space = process->vmm;
scheduler.SetTemporaryAddressSpace(space);
ThreadSetTemporaryAddressSpace(space);
KMutexAcquire(&space->reserveMutex);
LinkedItem<MMRegion> *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);
}
{

View File

@ -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() {

View File

@ -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;

View File

@ -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;

View File

@ -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<Thread> *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<Thread> activeThreads[THREAD_PRIORITY_COUNT];
LinkedList<Thread> pausedThreads;
LinkedList<KTimer> activeTimers;
KMutex allThreadsMutex; // For accessing the allThreads list.
KMutex allProcessesMutex; // For accessing the allProcesses list.
KSpinlock asyncTaskSpinlock; // For accessing the per-CPU asyncTaskList.
LinkedList<Thread> allThreads;
LinkedList<Process> 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> *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> *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<Process> *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<Process> *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() {

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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.");