mirror of https://gitlab.com/nakst/essence
cleanup scheduler
This commit is contained in:
parent
52e35a7636
commit
2436959c84
|
@ -477,7 +477,7 @@ extern "C" void InterruptHandler(InterruptContext *context) {
|
||||||
EsMemoryZero(&crashReason, sizeof(EsCrashReason));
|
EsMemoryZero(&crashReason, sizeof(EsCrashReason));
|
||||||
crashReason.errorCode = ES_FATAL_ERROR_PROCESSOR_EXCEPTION;
|
crashReason.errorCode = ES_FATAL_ERROR_PROCESSOR_EXCEPTION;
|
||||||
crashReason.duringSystemCall = (EsSyscallType) -1;
|
crashReason.duringSystemCall = (EsSyscallType) -1;
|
||||||
scheduler.CrashProcess(currentThread->process, &crashReason);
|
ProcessCrash(currentThread->process, &crashReason);
|
||||||
|
|
||||||
resolved:;
|
resolved:;
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ ES_EXTERN_C ACPI_STATUS AcpiOsExecute(ACPI_EXECUTE_TYPE type, ACPI_OSD_EXEC_CALL
|
||||||
event->function = function;
|
event->function = function;
|
||||||
event->context = context;
|
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) {
|
if (acpiEventCount == 256) {
|
||||||
KernelPanic("AcpiOsExecute - Exceeded maximum event count, 256.\n");
|
KernelPanic("AcpiOsExecute - Exceeded maximum event count, 256.\n");
|
||||||
|
|
|
@ -196,7 +196,7 @@ bool Controller::Transmit(void *dataVirtual, uintptr_t dataPhysical, size_t data
|
||||||
void Controller::DispatchThread() {
|
void Controller::DispatchThread() {
|
||||||
while (true) {
|
while (true) {
|
||||||
KEvent *events[] = { &receiveEvent };
|
KEvent *events[] = { &receiveEvent };
|
||||||
KWaitEvents(events, 1);
|
KEventWaitMultiple(events, 1);
|
||||||
|
|
||||||
NetInterfaceSetConnected(this, RD_REGISTER_STATUS() & (1 << 1));
|
NetInterfaceSetConnected(this, RD_REGISTER_STATUS() & (1 << 1));
|
||||||
|
|
||||||
|
|
|
@ -1214,12 +1214,12 @@ void CCWriteBehindThread() {
|
||||||
// Wait for a reason to want to write behind.
|
// Wait for a reason to want to write behind.
|
||||||
// - The CC_WAIT_FOR_WRITE_BEHIND timer expires.
|
// - The CC_WAIT_FOR_WRITE_BEHIND timer expires.
|
||||||
// - The number of available page frames is low (pmm.availableLow).
|
// - 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).
|
// - The modified list is getting full (activeSectionManager.modifiedGettingFull).
|
||||||
KTimer timer = {};
|
KTimer timer = {};
|
||||||
KTimerSet(&timer, CC_WAIT_FOR_WRITE_BEHIND - lastWriteMs);
|
KTimerSet(&timer, CC_WAIT_FOR_WRITE_BEHIND - lastWriteMs);
|
||||||
KEvent *events[] = { &timer.event, &pmm.availableLow, &scheduler.killedEvent, &activeSectionManager.modifiedGettingFull };
|
KEvent *events[] = { &timer.event, &pmm.availableLow, &scheduler.allProcessesTerminatedEvent, &activeSectionManager.modifiedGettingFull };
|
||||||
scheduler.WaitEvents(events, sizeof(events) / sizeof(events[0]));
|
KEventWaitMultiple(events, sizeof(events) / sizeof(events[0]));
|
||||||
KTimerRemove(&timer);
|
KTimerRemove(&timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1251,7 +1251,7 @@ void CCInitialise() {
|
||||||
activeSectionManager.sectionCount);
|
activeSectionManager.sectionCount);
|
||||||
|
|
||||||
KEventSet(&activeSectionManager.modifiedNonFull);
|
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;
|
activeSectionManager.writeBackThread->isPageGenerator = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
|
|
||||||
void KernelInitialise() {
|
void KernelInitialise() {
|
||||||
kernelProcess = scheduler.SpawnProcess(PROCESS_KERNEL); // Spawn the kernel process.
|
kernelProcess = ProcessSpawn(PROCESS_KERNEL); // Spawn the kernel process.
|
||||||
MMInitialise(); // Initialise the memory manager.
|
MMInitialise(); // Initialise the memory manager.
|
||||||
KThreadCreate("KernelMain", KernelMain); // Create the KernelMain thread.
|
KThreadCreate("KernelMain", KernelMain); // Create the KernelMain thread.
|
||||||
ArchInitialise(); // Start processors and initialise CPULocalStorage.
|
ArchInitialise(); // Start processors and initialise CPULocalStorage.
|
||||||
|
@ -22,11 +22,11 @@ void KernelInitialise() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelMain(uintptr_t) {
|
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.
|
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.
|
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.
|
FSShutdown(); // Flush file cache and unmount filesystems.
|
||||||
DriversShutdown(); // Inform drivers of shutdown.
|
DriversShutdown(); // Inform drivers of shutdown.
|
||||||
ArchShutdown(); // Power off or restart the computer.
|
ArchShutdown(); // Power off or restart the computer.
|
||||||
|
|
|
@ -1677,7 +1677,7 @@ void MMBalanceThread() {
|
||||||
// For every memory region...
|
// For every memory region...
|
||||||
|
|
||||||
MMSpace *space = process->vmm;
|
MMSpace *space = process->vmm;
|
||||||
scheduler.SetTemporaryAddressSpace(space);
|
ThreadSetTemporaryAddressSpace(space);
|
||||||
KMutexAcquire(&space->reserveMutex);
|
KMutexAcquire(&space->reserveMutex);
|
||||||
LinkedItem<MMRegion> *item = pmm.nextRegionToBalance ? &pmm.nextRegionToBalance->itemNonGuard : space->usedRegionsNonGuard.firstItem;
|
LinkedItem<MMRegion> *item = pmm.nextRegionToBalance ? &pmm.nextRegionToBalance->itemNonGuard : space->usedRegionsNonGuard.firstItem;
|
||||||
|
|
||||||
|
@ -1735,7 +1735,7 @@ void MMBalanceThread() {
|
||||||
}
|
}
|
||||||
|
|
||||||
KMutexRelease(&space->reserveMutex);
|
KMutexRelease(&space->reserveMutex);
|
||||||
scheduler.SetTemporaryAddressSpace(nullptr);
|
ThreadSetTemporaryAddressSpace(nullptr);
|
||||||
CloseHandleToObject(process, KERNEL_OBJECT_PROCESS);
|
CloseHandleToObject(process, KERNEL_OBJECT_PROCESS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2298,9 +2298,9 @@ void MMInitialise() {
|
||||||
|
|
||||||
pmm.zeroPageEvent.autoReset = true;
|
pmm.zeroPageEvent.autoReset = true;
|
||||||
MMCommit(PHYSICAL_MEMORY_MANIPULATION_REGION_PAGES * K_PAGE_SIZE, true);
|
MMCommit(PHYSICAL_MEMORY_MANIPULATION_REGION_PAGES * K_PAGE_SIZE, true);
|
||||||
pmm.zeroPageThread = scheduler.SpawnThread("MMZero", (uintptr_t) MMZeroPageThread, 0, SPAWN_THREAD_LOW_PRIORITY);
|
pmm.zeroPageThread = ThreadSpawn("MMZero", (uintptr_t) MMZeroPageThread, 0, SPAWN_THREAD_LOW_PRIORITY);
|
||||||
scheduler.SpawnThread("MMBalance", (uintptr_t) MMBalanceThread, 0, ES_FLAGS_DEFAULT)->isPageGenerator = true;
|
ThreadSpawn("MMBalance", (uintptr_t) MMBalanceThread, 0, ES_FLAGS_DEFAULT)->isPageGenerator = true;
|
||||||
scheduler.SpawnThread("MMObjTrim", (uintptr_t) MMObjectCacheTrimThread, 0, ES_FLAGS_DEFAULT);
|
ThreadSpawn("MMObjTrim", (uintptr_t) MMObjectCacheTrimThread, 0, ES_FLAGS_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
bool KEventSet(KEvent *event, bool maybeAlreadySet = false);
|
||||||
void KEventReset(KEvent *event);
|
void KEventReset(KEvent *event);
|
||||||
bool KEventPoll(KEvent *event); // TODO Remove this! Currently it is only used by KAudioFillBuffersFromMixer.
|
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.
|
struct KWriterLock { // One writer or many readers.
|
||||||
K_PRIVATE
|
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.
|
extern "C" void KThreadTerminate(); // Terminates the current thread. Kernel threads can only be terminated by themselves.
|
||||||
void KYield();
|
void KYield();
|
||||||
|
|
||||||
uintptr_t KWaitEvents(KEvent **events, size_t count);
|
uintptr_t KEventWaitMultiple(KEvent **events, size_t count);
|
||||||
|
|
||||||
struct KWorkGroup {
|
struct KWorkGroup {
|
||||||
inline void Initialise() {
|
inline void Initialise() {
|
||||||
|
|
|
@ -219,7 +219,7 @@ void CloseHandleToObject(void *object, KernelObjectType type, uint32_t flags) {
|
||||||
if (previous == 0) {
|
if (previous == 0) {
|
||||||
KernelPanic("CloseHandleToProcess - All handles to process %x have been closed.\n", process);
|
KernelPanic("CloseHandleToProcess - All handles to process %x have been closed.\n", process);
|
||||||
} else if (previous == 1) {
|
} else if (previous == 1) {
|
||||||
scheduler.RemoveProcess(process);
|
ProcessRemove(process);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
@ -230,7 +230,7 @@ void CloseHandleToObject(void *object, KernelObjectType type, uint32_t flags) {
|
||||||
if (previous == 0) {
|
if (previous == 0) {
|
||||||
KernelPanic("CloseHandleToObject - All handles to thread %x have been closed.\n", thread);
|
KernelPanic("CloseHandleToObject - All handles to thread %x have been closed.\n", thread);
|
||||||
} else if (previous == 1) {
|
} else if (previous == 1) {
|
||||||
scheduler.RemoveThread(thread);
|
ThreadRemove(thread);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
|
|
@ -463,7 +463,7 @@ namespace POSIX {
|
||||||
// EsPrint("vfork->\n");
|
// EsPrint("vfork->\n");
|
||||||
|
|
||||||
// Allocate the process.
|
// Allocate the process.
|
||||||
Process *forkProcess = currentThread->posixData->forkProcess = scheduler.SpawnProcess();
|
Process *forkProcess = currentThread->posixData->forkProcess = ProcessSpawn(PROCESS_NORMAL);
|
||||||
forkProcess->pgid = currentProcess->pgid;
|
forkProcess->pgid = currentProcess->pgid;
|
||||||
|
|
||||||
// Clone our FDs.
|
// Clone our FDs.
|
||||||
|
@ -518,7 +518,7 @@ namespace POSIX {
|
||||||
|
|
||||||
// Start the process.
|
// Start the process.
|
||||||
|
|
||||||
if (!process->Start(path, syscall.arguments[1])) {
|
if (!ProcessStart(process, path, syscall.arguments[1])) {
|
||||||
EsHeapFree(path, 0, K_FIXED);
|
EsHeapFree(path, 0, K_FIXED);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
@ -591,7 +591,7 @@ namespace POSIX {
|
||||||
if (processHandle) {
|
if (processHandle) {
|
||||||
return processHandle;
|
return processHandle;
|
||||||
} else {
|
} else {
|
||||||
scheduler.TerminateProcess(currentProcess, syscall.arguments[0]);
|
ProcessTerminate(currentProcess, syscall.arguments[0]);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,37 @@
|
||||||
// TODO Simplify or remove asynchronous task thread semantics.
|
// TODO Simplify or remove asynchronous task thread semantics.
|
||||||
// TODO Break up or remove dispatchSpinlock.
|
// 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
|
#ifndef IMPLEMENTATION
|
||||||
|
|
||||||
#define THREAD_PRIORITY_NORMAL (0) // Lower value = higher priority.
|
#define THREAD_PRIORITY_NORMAL (0) // Lower value = higher priority.
|
||||||
|
@ -109,9 +140,6 @@ enum ProcessType {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Process {
|
struct Process {
|
||||||
bool StartWithNode(KNode *node);
|
|
||||||
bool Start(char *imagePath, size_t imagePathLength);
|
|
||||||
|
|
||||||
MMSpace *vmm;
|
MMSpace *vmm;
|
||||||
HandleTable handleTable;
|
HandleTable handleTable;
|
||||||
MessageQueue messageQueue;
|
MessageQueue messageQueue;
|
||||||
|
@ -139,7 +167,7 @@ struct Process {
|
||||||
|
|
||||||
// Termination:
|
// Termination:
|
||||||
bool allThreadsTerminated;
|
bool allThreadsTerminated;
|
||||||
bool preventNewThreads; // Set by TerminateProcess.
|
bool preventNewThreads; // Set by ProcessTerminate.
|
||||||
int exitStatus; // TODO Remove this.
|
int exitStatus; // TODO Remove this.
|
||||||
KEvent killedEvent;
|
KEvent killedEvent;
|
||||||
|
|
||||||
|
@ -163,106 +191,37 @@ struct Scheduler {
|
||||||
// External API:
|
// External API:
|
||||||
|
|
||||||
void Yield(InterruptContext *context);
|
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 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 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 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 NotifyObject(LinkedList<Thread> *blockedThreads, bool unblockAll, Thread *previousMutexOwner = nullptr);
|
||||||
void UnblockThread(Thread *unblockedThread, Thread *previousMutexOwner = nullptr);
|
void UnblockThread(Thread *unblockedThread, Thread *previousMutexOwner = nullptr);
|
||||||
|
|
||||||
Thread *PickThread(CPULocalStorage *local); // Pick the next thread to execute.
|
Thread *PickThread(CPULocalStorage *local); // Pick the next thread to execute.
|
||||||
int8_t GetThreadEffectivePriority(Thread *thread);
|
int8_t GetThreadEffectivePriority(Thread *thread);
|
||||||
|
|
||||||
// Variables:
|
// Variables:
|
||||||
|
|
||||||
KSpinlock dispatchSpinlock; // For accessing synchronisation objects, thread states, scheduling lists, etc. TODO Break this up!
|
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 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> activeThreads[THREAD_PRIORITY_COUNT];
|
||||||
LinkedList<Thread> pausedThreads;
|
LinkedList<Thread> pausedThreads;
|
||||||
LinkedList<KTimer> activeTimers;
|
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<Thread> allThreads;
|
||||||
LinkedList<Process> allProcesses;
|
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;
|
volatile bool started, panic, shutdown;
|
||||||
|
|
||||||
uint64_t timeMs;
|
uint64_t timeMs;
|
||||||
|
|
||||||
EsObjectID nextThreadID;
|
|
||||||
EsObjectID nextProcessID;
|
|
||||||
EsObjectID nextProcessorID;
|
|
||||||
|
|
||||||
#ifdef DEBUG_BUILD
|
#ifdef DEBUG_BUILD
|
||||||
EsThreadEventLogEntry *volatile threadEventLog;
|
EsThreadEventLogEntry *volatile threadEventLog;
|
||||||
volatile uintptr_t threadEventLogPosition;
|
volatile uintptr_t threadEventLogPosition;
|
||||||
|
@ -270,6 +229,21 @@ struct Scheduler {
|
||||||
#endif
|
#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;
|
||||||
Process *kernelProcess = &_kernelProcess;
|
Process *kernelProcess = &_kernelProcess;
|
||||||
Process *desktopProcess;
|
Process *desktopProcess;
|
||||||
|
@ -379,7 +353,7 @@ void Scheduler::MaybeUpdateActiveList(Thread *thread) {
|
||||||
activeThreads[effectivePriority].InsertStart(&thread->item);
|
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;
|
bool userland = flags & SPAWN_THREAD_USERLAND;
|
||||||
Thread *parentThread = GetCurrentThread();
|
Thread *parentThread = GetCurrentThread();
|
||||||
|
|
||||||
|
@ -388,7 +362,7 @@ Thread *Scheduler::SpawnThread(const char *cName, uintptr_t startAddress, uintpt
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userland && process == kernelProcess) {
|
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
|
// 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);
|
KMutexAcquire(&process->threadsMutex);
|
||||||
EsDefer(KMutexRelease(&process->threadsMutex));
|
EsDefer(KMutexRelease(&process->threadsMutex));
|
||||||
|
|
||||||
if (shutdown && userland) return nullptr;
|
if (process->preventNewThreads) {
|
||||||
if (process->preventNewThreads) return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
Thread *thread = (Thread *) threadPool.Add(sizeof(Thread));
|
Thread *thread = (Thread *) scheduler.threadPool.Add(sizeof(Thread));
|
||||||
if (!thread) return nullptr;
|
if (!thread) return nullptr;
|
||||||
KernelLog(LOG_INFO, "Scheduler", "spawn thread", "Created thread, %x to start at %x\n", thread, startAddress);
|
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;
|
if (!stack) goto fail;
|
||||||
skipStackAllocation:;
|
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.
|
// This is synchronized under the threadsMutex.
|
||||||
thread->paused = (parentThread && process == parentThread->process && parentThread->paused) || (flags & SPAWN_THREAD_PAUSED);
|
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->userStackCommit = userStackCommit;
|
||||||
thread->terminatableState = userland ? THREAD_TERMINATABLE : THREAD_IN_SYSCALL;
|
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->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->process = process;
|
||||||
thread->item.thisItem = thread;
|
thread->item.thisItem = thread;
|
||||||
thread->allItem.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);
|
process->threads.InsertEnd(&thread->processItem);
|
||||||
|
|
||||||
KMutexAcquire(&allThreadsMutex);
|
KMutexAcquire(&scheduler.allThreadsMutex);
|
||||||
allThreads.InsertStart(&thread->allItem);
|
scheduler.allThreads.InsertStart(&thread->allItem);
|
||||||
KMutexRelease(&allThreadsMutex);
|
KMutexRelease(&scheduler.allThreadsMutex);
|
||||||
|
|
||||||
// Each thread owns a handles to the owner process.
|
// Each thread owns a handles to the owner process.
|
||||||
// This makes sure the process isn't destroyed before all its threads have been destroyed.
|
// 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) {
|
if (thread->type == THREAD_NORMAL) {
|
||||||
// Add the thread to the start of the active thread list to make sure that it runs immediately.
|
// Add the thread to the start of the active thread list to make sure that it runs immediately.
|
||||||
KSpinlockAcquire(&dispatchSpinlock);
|
KSpinlockAcquire(&scheduler.dispatchSpinlock);
|
||||||
AddActiveThread(thread, true);
|
scheduler.AddActiveThread(thread, true);
|
||||||
KSpinlockRelease(&dispatchSpinlock);
|
KSpinlockRelease(&scheduler.dispatchSpinlock);
|
||||||
} else {
|
} else {
|
||||||
// Idle and asynchronous task threads don't need to be added to a scheduling list.
|
// 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:;
|
fail:;
|
||||||
if (stack) MMFree(process->vmm, (void *) stack);
|
if (stack) MMFree(process->vmm, (void *) stack);
|
||||||
if (kernelStack) MMFree(kernelMMSpace, (void *) kernelStack);
|
if (kernelStack) MMFree(kernelMMSpace, (void *) kernelStack);
|
||||||
threadPool.Remove(thread);
|
scheduler.threadPool.Remove(thread);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void KillProcess(Process *process) {
|
void ProcessKill(Process *process) {
|
||||||
// This function should only be called by KillThread, when the last thread in the process exits.
|
// 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.
|
// 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) {
|
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);
|
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();
|
process->handleTable.Destroy();
|
||||||
|
|
||||||
// Destroy the virtual memory space.
|
// 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!
|
// This must be destroyed after the handle table!
|
||||||
MMSpaceDestroy(process->vmm);
|
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);
|
Thread *thread = EsContainerOf(Thread, killAsyncTask, task);
|
||||||
scheduler.SetTemporaryAddressSpace(thread->process->vmm);
|
ThreadSetTemporaryAddressSpace(thread->process->vmm);
|
||||||
|
|
||||||
KMutexAcquire(&scheduler.allThreadsMutex);
|
KMutexAcquire(&scheduler.allThreadsMutex);
|
||||||
scheduler.allThreads.Remove(&thread->allItem);
|
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);
|
"Killing thread (ID %d, %d remain in process %d) %x...\n", thread->id, thread->process->threads.count, thread->process->id, thread);
|
||||||
|
|
||||||
if (lastThread) {
|
if (lastThread) {
|
||||||
KillProcess(thread->process);
|
ProcessKill(thread->process);
|
||||||
}
|
}
|
||||||
|
|
||||||
MMFree(kernelMMSpace, (void *) thread->kernelStackBase);
|
MMFree(kernelMMSpace, (void *) thread->kernelStackBase);
|
||||||
|
@ -603,7 +578,7 @@ void KillThread(KAsyncTask *task) {
|
||||||
CloseHandleToObject(thread, KERNEL_OBJECT_THREAD);
|
CloseHandleToObject(thread, KERNEL_OBJECT_THREAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::TerminateProcess(Process *process, int status) {
|
void ProcessTerminate(Process *process, int status) {
|
||||||
KMutexAcquire(&process->threadsMutex);
|
KMutexAcquire(&process->threadsMutex);
|
||||||
|
|
||||||
KernelLog(LOG_INFO, "Scheduler", "terminate process", "Terminating process %d '%z' with status %i...\n",
|
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;
|
thread = thread->nextItem;
|
||||||
|
|
||||||
if (threadObject != currentThread) {
|
if (threadObject != currentThread) {
|
||||||
TerminateThread(threadObject);
|
ThreadTerminate(threadObject);
|
||||||
} else if (isCurrentProcess) {
|
} else if (isCurrentProcess) {
|
||||||
foundCurrentThread = true;
|
foundCurrentThread = true;
|
||||||
} else {
|
} 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);
|
KMutexRelease(&process->threadsMutex);
|
||||||
|
|
||||||
if (!foundCurrentThread && isCurrentProcess) {
|
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) {
|
} else if (isCurrentProcess) {
|
||||||
// This doesn't return.
|
// This doesn't return.
|
||||||
TerminateThread(currentThread);
|
ThreadTerminate(currentThread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::TerminateThread(Thread *thread) {
|
void ThreadTerminate(Thread *thread) {
|
||||||
// Overview:
|
// Overview:
|
||||||
// Set terminating true, and paused false.
|
// Set terminating true, and paused false.
|
||||||
// Is this the current thread?
|
// 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.
|
// We cannot return to the previous function as it expects to be killed.
|
||||||
ProcessorFakeTimerInterrupt();
|
ProcessorFakeTimerInterrupt();
|
||||||
KernelPanic("Scheduler::TerminateThread - ProcessorFakeTimerInterrupt returned.\n");
|
KernelPanic("Scheduler::ThreadTerminate - ProcessorFakeTimerInterrupt returned.\n");
|
||||||
} else {
|
} else {
|
||||||
if (thread->terminatableState == THREAD_TERMINATABLE) {
|
if (thread->terminatableState == THREAD_TERMINATABLE) {
|
||||||
// We're in user code..
|
// We're in user code..
|
||||||
|
@ -688,13 +663,13 @@ void Scheduler::TerminateThread(Thread *thread) {
|
||||||
// is pre-empted, it will be terminated.
|
// is pre-empted, it will be terminated.
|
||||||
} else {
|
} else {
|
||||||
if (thread->state != THREAD_ACTIVE) {
|
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.
|
// The thread is terminatable and it isn't executing.
|
||||||
// Remove it from its queue, and then remove the thread.
|
// Remove it from its queue, and then remove the thread.
|
||||||
thread->item.RemoveFromList();
|
thread->item.RemoveFromList();
|
||||||
KRegisterAsyncTask(&thread->killAsyncTask, KillThread);
|
KRegisterAsyncTask(&thread->killAsyncTask, ThreadKill);
|
||||||
yield = true;
|
yield = true;
|
||||||
}
|
}
|
||||||
} else if (thread->terminatableState == THREAD_USER_BLOCK_REQUEST) {
|
} else if (thread->terminatableState == THREAD_USER_BLOCK_REQUEST) {
|
||||||
|
@ -709,7 +684,7 @@ void Scheduler::TerminateThread(Thread *thread) {
|
||||||
// Unblock the thread.
|
// Unblock the thread.
|
||||||
// See comment above.
|
// See comment above.
|
||||||
if (thread->state == THREAD_WAITING_MUTEX || thread->state == THREAD_WAITING_EVENT) {
|
if (thread->state == THREAD_WAITING_MUTEX || thread->state == THREAD_WAITING_EVENT) {
|
||||||
UnblockThread(thread);
|
scheduler.UnblockThread(thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -725,7 +700,7 @@ void Scheduler::TerminateThread(Thread *thread) {
|
||||||
if (yield) ProcessorFakeTimerInterrupt(); // Process the asynchronous task.
|
if (yield) ProcessorFakeTimerInterrupt(); // Process the asynchronous task.
|
||||||
}
|
}
|
||||||
|
|
||||||
void NewProcess() {
|
void ProcessLoadExecutable() {
|
||||||
Process *thisProcess = GetCurrentThread()->process;
|
Process *thisProcess = GetCurrentThread()->process;
|
||||||
|
|
||||||
KernelLog(LOG_INFO, "Scheduler", "new process",
|
KernelLog(LOG_INFO, "Scheduler", "new process",
|
||||||
|
@ -795,7 +770,7 @@ void NewProcess() {
|
||||||
if (thisProcess->creationFlags & ES_PROCESS_CREATE_PAUSED) threadFlags |= SPAWN_THREAD_PAUSED;
|
if (thisProcess->creationFlags & ES_PROCESS_CREATE_PAUSED) threadFlags |= SPAWN_THREAD_PAUSED;
|
||||||
|
|
||||||
thisProcess->executableState = ES_PROCESS_EXECUTABLE_LOADED;
|
thisProcess->executableState = ES_PROCESS_EXECUTABLE_LOADED;
|
||||||
thisProcess->executableMainThread = scheduler.SpawnThread("MainThread", api.startAddress,
|
thisProcess->executableMainThread = ThreadSpawn("MainThread", api.startAddress,
|
||||||
(uintptr_t) startupInformation, threadFlags, thisProcess);
|
(uintptr_t) startupInformation, threadFlags, thisProcess);
|
||||||
|
|
||||||
if (!thisProcess->executableMainThread) {
|
if (!thisProcess->executableMainThread) {
|
||||||
|
@ -805,7 +780,7 @@ void NewProcess() {
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
if (thisProcess->type != PROCESS_NORMAL) {
|
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;
|
thisProcess->executableState = ES_PROCESS_EXECUTABLE_FAILED_TO_LOAD;
|
||||||
|
@ -814,37 +789,17 @@ void NewProcess() {
|
||||||
KEventSet(&thisProcess->executableLoadAttemptComplete);
|
KEventSet(&thisProcess->executableLoadAttemptComplete);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Process::Start(char *imagePath, size_t imagePathLength) {
|
bool ProcessStartWithNode(Process *process, KNode *node) {
|
||||||
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) {
|
|
||||||
// Make sure nobody has tried to start the process.
|
// Make sure nobody has tried to start the process.
|
||||||
|
|
||||||
KSpinlockAcquire(&scheduler.dispatchSpinlock);
|
KSpinlockAcquire(&scheduler.dispatchSpinlock);
|
||||||
|
|
||||||
if (executableStartRequest) {
|
if (process->executableStartRequest) {
|
||||||
KSpinlockRelease(&scheduler.dispatchSpinlock);
|
KSpinlockRelease(&scheduler.dispatchSpinlock);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
executableStartRequest = true;
|
process->executableStartRequest = true;
|
||||||
|
|
||||||
KSpinlockRelease(&scheduler.dispatchSpinlock);
|
KSpinlockRelease(&scheduler.dispatchSpinlock);
|
||||||
|
|
||||||
|
@ -853,45 +808,45 @@ bool Process::StartWithNode(KNode *node) {
|
||||||
KWriterLockTake(&node->writerLock, K_LOCK_SHARED);
|
KWriterLockTake(&node->writerLock, K_LOCK_SHARED);
|
||||||
size_t byteCount = node->directoryEntry->item.key.longKeyBytes;
|
size_t byteCount = node->directoryEntry->item.key.longKeyBytes;
|
||||||
if (byteCount > ES_SNAPSHOT_MAX_PROCESS_NAME_LENGTH) byteCount = ES_SNAPSHOT_MAX_PROCESS_NAME_LENGTH;
|
if (byteCount > ES_SNAPSHOT_MAX_PROCESS_NAME_LENGTH) byteCount = ES_SNAPSHOT_MAX_PROCESS_NAME_LENGTH;
|
||||||
EsMemoryCopy(cExecutableName, node->directoryEntry->item.key.longKey, byteCount);
|
EsMemoryCopy(process->cExecutableName, node->directoryEntry->item.key.longKey, byteCount);
|
||||||
cExecutableName[byteCount] = 0;
|
process->cExecutableName[byteCount] = 0;
|
||||||
KWriterLockReturn(&node->writerLock, K_LOCK_SHARED);
|
KWriterLockReturn(&node->writerLock, K_LOCK_SHARED);
|
||||||
|
|
||||||
// Initialise the memory space.
|
// Initialise the memory space.
|
||||||
|
|
||||||
bool success = MMSpaceInitialise(vmm);
|
bool success = MMSpaceInitialise(process->vmm);
|
||||||
if (!success) return false;
|
if (!success) return false;
|
||||||
|
|
||||||
// NOTE If you change these flags, make sure to update the flags when the handle is closed!
|
// 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)) {
|
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.activeProcessCount, 1);
|
||||||
__sync_fetch_and_add(&scheduler.blockShutdownProcessCount, 1);
|
__sync_fetch_and_add(&scheduler.blockShutdownProcessCount, 1);
|
||||||
|
|
||||||
// Add the process to the list of all processes,
|
// Add the process to the list of all processes,
|
||||||
// and spawn the kernel thread to load the executable.
|
// and spawn the kernel thread to load the executable.
|
||||||
// This is synchronized under allProcessesMutex so that the process can't be terminated or paused
|
// 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);
|
KMutexAcquire(&scheduler.allProcessesMutex);
|
||||||
scheduler.allProcesses.InsertEnd(&allItem);
|
scheduler.allProcesses.InsertEnd(&process->allItem);
|
||||||
Thread *newProcessThread = scheduler.SpawnThread("NewProcess", (uintptr_t) NewProcess, 0, ES_FLAGS_DEFAULT, this);
|
Thread *loadExecutableThread = ThreadSpawn("ExecLoad", (uintptr_t) ProcessLoadExecutable, 0, ES_FLAGS_DEFAULT, process);
|
||||||
KMutexRelease(&scheduler.allProcessesMutex);
|
KMutexRelease(&scheduler.allProcessesMutex);
|
||||||
|
|
||||||
if (!newProcessThread) {
|
if (!loadExecutableThread) {
|
||||||
CloseHandleToObject(this, KERNEL_OBJECT_PROCESS);
|
CloseHandleToObject(process, KERNEL_OBJECT_PROCESS);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the executable to be loaded.
|
// Wait for the executable to be loaded.
|
||||||
|
|
||||||
CloseHandleToObject(newProcessThread, KERNEL_OBJECT_THREAD);
|
CloseHandleToObject(loadExecutableThread, KERNEL_OBJECT_THREAD);
|
||||||
KEventWait(&executableLoadAttemptComplete, ES_WAIT_NO_TIMEOUT);
|
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");
|
KernelLog(LOG_ERROR, "Scheduler", "executable load failure", "Executable failed to load.\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -899,23 +854,43 @@ bool Process::StartWithNode(KNode *node) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Process *Scheduler::SpawnProcess(ProcessType processType) {
|
bool ProcessStart(Process *process, char *imagePath, size_t imagePathLength) {
|
||||||
if (shutdown) return nullptr;
|
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) {
|
if (!process) {
|
||||||
return nullptr;
|
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) {
|
if (!process->vmm) {
|
||||||
processPool.Remove(process);
|
scheduler.processPool.Remove(process);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
process->id = __sync_fetch_and_add(&nextProcessID, 1);
|
process->id = __sync_fetch_and_add(&scheduler.nextProcessID, 1);
|
||||||
process->vmm->referenceCount = 1;
|
process->vmm->referenceCount = 1;
|
||||||
process->allItem.thisItem = process;
|
process->allItem.thisItem = process;
|
||||||
process->handles = 1;
|
process->handles = 1;
|
||||||
|
@ -931,15 +906,15 @@ Process *Scheduler::SpawnProcess(ProcessType processType) {
|
||||||
return process;
|
return process;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::SetTemporaryAddressSpace(MMSpace *space) {
|
void ThreadSetTemporaryAddressSpace(MMSpace *space) {
|
||||||
KSpinlockAcquire(&dispatchSpinlock);
|
KSpinlockAcquire(&scheduler.dispatchSpinlock);
|
||||||
Thread *thread = GetCurrentThread();
|
Thread *thread = GetCurrentThread();
|
||||||
MMSpace *oldSpace = thread->temporaryAddressSpace ?: kernelMMSpace;
|
MMSpace *oldSpace = thread->temporaryAddressSpace ?: kernelMMSpace;
|
||||||
thread->temporaryAddressSpace = space;
|
thread->temporaryAddressSpace = space;
|
||||||
MMSpace *newSpace = space ?: kernelMMSpace;
|
MMSpace *newSpace = space ?: kernelMMSpace;
|
||||||
MMSpaceOpenReference(newSpace);
|
MMSpaceOpenReference(newSpace);
|
||||||
ProcessorSetAddressSpace(&newSpace->data);
|
ProcessorSetAddressSpace(&newSpace->data);
|
||||||
KSpinlockRelease(&dispatchSpinlock);
|
KSpinlockRelease(&scheduler.dispatchSpinlock);
|
||||||
MMSpaceCloseReference(oldSpace);
|
MMSpaceCloseReference(oldSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -959,15 +934,15 @@ void AsyncTaskThread() {
|
||||||
item->Remove();
|
item->Remove();
|
||||||
KSpinlockRelease(&scheduler.asyncTaskSpinlock);
|
KSpinlockRelease(&scheduler.asyncTaskSpinlock);
|
||||||
callback(task); // This may cause the task to be deallocated.
|
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;
|
local->inAsyncTask = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::CreateProcessorThreads(CPULocalStorage *local) {
|
void Scheduler::CreateProcessorThreads(CPULocalStorage *local) {
|
||||||
local->asyncTaskThread = SpawnThread("AsyncTasks", (uintptr_t) AsyncTaskThread, 0, SPAWN_THREAD_ASYNC_TASK);
|
local->asyncTaskThread = ThreadSpawn("AsyncTasks", (uintptr_t) AsyncTaskThread, 0, SPAWN_THREAD_ASYNC_TASK);
|
||||||
local->currentThread = local->idleThread = SpawnThread("Idle", 0, 0, SPAWN_THREAD_IDLE);
|
local->currentThread = local->idleThread = ThreadSpawn("Idle", 0, 0, SPAWN_THREAD_IDLE);
|
||||||
local->processorID = __sync_fetch_and_add(&nextProcessorID, 1);
|
local->processorID = __sync_fetch_and_add(&nextProcessorID, 1);
|
||||||
|
|
||||||
if (local->processorID >= K_MAX_PROCESSORS) {
|
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);
|
KernelLog(LOG_INFO, "Scheduler", "remove process", "Removing process %d.\n", process->id);
|
||||||
|
|
||||||
if (process->executableNode) {
|
if (process->executableNode) {
|
||||||
|
@ -997,11 +972,11 @@ void Scheduler::RemoveProcess(Process *process) {
|
||||||
scheduler.processPool.Remove(process);
|
scheduler.processPool.Remove(process);
|
||||||
|
|
||||||
if (1 == __sync_fetch_and_sub(&scheduler.blockShutdownProcessCount, 1)) {
|
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);
|
KernelLog(LOG_INFO, "Scheduler", "remove thread", "Removing thread %d.\n", thread->id);
|
||||||
|
|
||||||
// The last handle to the thread has been closed,
|
// The last handle to the thread has been closed,
|
||||||
|
@ -1021,17 +996,75 @@ void Scheduler::RemoveThread(Thread *thread) {
|
||||||
scheduler.threadPool.Remove(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) {
|
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) {
|
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) {
|
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);
|
KMutexAcquire(&process->crashMutex);
|
||||||
|
@ -1047,7 +1080,7 @@ void Scheduler::CrashProcess(Process *process, EsCrashReason *crashReason) {
|
||||||
|
|
||||||
EsMemoryCopy(&process->crashReason, crashReason, sizeof(EsCrashReason));
|
EsMemoryCopy(&process->crashReason, crashReason, sizeof(EsCrashReason));
|
||||||
|
|
||||||
if (!shutdown) {
|
if (!scheduler.shutdown) {
|
||||||
_EsMessageWithObject m;
|
_EsMessageWithObject m;
|
||||||
EsMemoryZero(&m, sizeof(m));
|
EsMemoryZero(&m, sizeof(m));
|
||||||
m.message.type = ES_MSG_APPLICATION_CRASH;
|
m.message.type = ES_MSG_APPLICATION_CRASH;
|
||||||
|
@ -1059,65 +1092,7 @@ void Scheduler::CrashProcess(Process *process, EsCrashReason *crashReason) {
|
||||||
KMutexRelease(&process->crashMutex);
|
KMutexRelease(&process->crashMutex);
|
||||||
|
|
||||||
// TODO Shouldn't this be done before sending the desktop message?
|
// TODO Shouldn't this be done before sending the desktop message?
|
||||||
scheduler.PauseProcess(GetCurrentThread()->process, false);
|
ProcessPause(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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread *Scheduler::PickThread(CPULocalStorage *local) {
|
Thread *Scheduler::PickThread(CPULocalStorage *local) {
|
||||||
|
@ -1205,7 +1180,7 @@ void Scheduler::Yield(InterruptContext *context) {
|
||||||
if (killThread) {
|
if (killThread) {
|
||||||
local->currentThread->state = THREAD_TERMINATED;
|
local->currentThread->state = THREAD_TERMINATED;
|
||||||
KernelLog(LOG_INFO, "Scheduler", "terminate yielded thread", "Terminated yielded thread %x\n", local->currentThread);
|
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.
|
// 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");
|
KernelPanic("Scheduler::Yield - DoContextSwitch unexpectedly returned.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::Shutdown() {
|
void ProcessTerminateAll() {
|
||||||
scheduler.shutdown = true;
|
scheduler.shutdown = true;
|
||||||
|
|
||||||
// Close our handle to the desktop process.
|
// Close our handle to the desktop process.
|
||||||
CloseHandleToObject(desktopProcess->executableMainThread, KERNEL_OBJECT_THREAD);
|
CloseHandleToObject(desktopProcess->executableMainThread, KERNEL_OBJECT_THREAD);
|
||||||
CloseHandleToObject(desktopProcess, KERNEL_OBJECT_PROCESS);
|
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) {
|
while (true) {
|
||||||
KMutexAcquire(&allProcessesMutex);
|
KMutexAcquire(&scheduler.allProcessesMutex);
|
||||||
Process *process = allProcesses.firstItem->thisItem;
|
Process *process = scheduler.allProcesses.firstItem->thisItem;
|
||||||
|
|
||||||
while (process && (process->preventNewThreads || process == kernelProcess)) {
|
while (process && (process->preventNewThreads || process == kernelProcess)) {
|
||||||
LinkedItem<Process> *item = process->allItem.nextItem;
|
LinkedItem<Process> *item = process->allItem.nextItem;
|
||||||
process = item ? item->thisItem : nullptr;
|
process = item ? item->thisItem : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
KMutexRelease(&allProcessesMutex);
|
KMutexRelease(&scheduler.allProcessesMutex);
|
||||||
if (!process) break;
|
if (!process) break;
|
||||||
|
|
||||||
TerminateProcess(process, -1);
|
ProcessTerminate(process, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
KEventWait(&killedEvent);
|
KEventWait(&scheduler.allProcessesTerminatedEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
Process *Scheduler::OpenProcess(uint64_t id) {
|
Process *ProcessOpen(uint64_t id) {
|
||||||
KMutexAcquire(&allProcessesMutex);
|
KMutexAcquire(&scheduler.allProcessesMutex);
|
||||||
LinkedItem<Process> *item = scheduler.allProcesses.firstItem;
|
LinkedItem<Process> *item = scheduler.allProcesses.firstItem;
|
||||||
Process *result = nullptr;
|
Process *result = nullptr;
|
||||||
|
|
||||||
|
@ -1340,16 +1315,16 @@ Process *Scheduler::OpenProcess(uint64_t id) {
|
||||||
item = item->nextItem;
|
item = item->nextItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
KMutexRelease(&allProcessesMutex);
|
KMutexRelease(&scheduler.allProcessesMutex);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KThreadCreate(const char *cName, void (*startAddress)(uintptr_t), uintptr_t argument) {
|
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() {
|
void KThreadTerminate() {
|
||||||
scheduler.TerminateThread(GetCurrentThread());
|
ThreadTerminate(GetCurrentThread());
|
||||||
}
|
}
|
||||||
|
|
||||||
void KYield() {
|
void KYield() {
|
||||||
|
|
|
@ -140,10 +140,34 @@ bool KMutexAcquire(KMutex *mutex) {
|
||||||
__sync_synchronize();
|
__sync_synchronize();
|
||||||
|
|
||||||
if (GetLocalStorage() && GetLocalStorage()->schedulerReady) {
|
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,
|
// Instead of spinning on the lock,
|
||||||
// let's tell the scheduler to not schedule this thread
|
// let's tell the scheduler to not schedule this thread
|
||||||
// until it's released.
|
// 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) {
|
if (currentThread->terminating && currentThread->terminatableState == THREAD_USER_BLOCK_REQUEST) {
|
||||||
// We didn't acquire the mutex because the thread is terminating.
|
// 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;
|
events[0] = _this;
|
||||||
|
|
||||||
if (timeoutMs == (uint64_t) ES_WAIT_NO_TIMEOUT) {
|
if (timeoutMs == (uint64_t) ES_WAIT_NO_TIMEOUT) {
|
||||||
int index = scheduler.WaitEvents(events, 1);
|
int index = KEventWaitMultiple(events, 1);
|
||||||
return index == 0;
|
return index == 0;
|
||||||
} else {
|
} else {
|
||||||
KTimer timer = {};
|
KTimer timer = {};
|
||||||
KTimerSet(&timer, timeoutMs);
|
KTimerSet(&timer, timeoutMs);
|
||||||
events[1] = &timer.event;
|
events[1] = &timer.event;
|
||||||
int index = scheduler.WaitEvents(events, 2);
|
int index = KEventWaitMultiple(events, 2);
|
||||||
KTimerRemove(&timer);
|
KTimerRemove(&timer);
|
||||||
return index == 0;
|
return index == 0;
|
||||||
}
|
}
|
||||||
|
@ -624,42 +648,13 @@ void KTimerRemove(KTimer *timer) {
|
||||||
KSpinlockRelease(&scheduler.activeTimersSpinlock);
|
KSpinlockRelease(&scheduler.activeTimersSpinlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::WaitMutex(KMutex *mutex) {
|
uintptr_t KEventWaitMultiple(KEvent **events, size_t count) {
|
||||||
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) {
|
|
||||||
if (count > ES_MAX_WAIT_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) {
|
} else if (!count) {
|
||||||
KernelPanic("Scheduler::WaitEvents - Count is 0.\n");
|
KernelPanic("KEventWaitMultiple - Count is 0.\n");
|
||||||
} else if (!ProcessorAreInterruptsEnabled()) {
|
} else if (!ProcessorAreInterruptsEnabled()) {
|
||||||
KernelPanic("Scheduler::WaitEvents - Interrupts disabled.\n");
|
KernelPanic("KEventWaitMultiple - Interrupts disabled.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread *thread = GetCurrentThread();
|
Thread *thread = GetCurrentThread();
|
||||||
|
@ -703,10 +698,6 @@ uintptr_t Scheduler::WaitEvents(KEvent **events, size_t count) {
|
||||||
return -1; // Exited from termination.
|
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) {
|
void Scheduler::UnblockThread(Thread *unblockedThread, Thread *previousMutexOwner) {
|
||||||
KSpinlockAssertLocked(&dispatchSpinlock);
|
KSpinlockAssertLocked(&dispatchSpinlock);
|
||||||
|
|
||||||
|
|
|
@ -176,7 +176,7 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_PROCESS_CREATE) {
|
||||||
// Create the process.
|
// Create the process.
|
||||||
|
|
||||||
if (error == ES_SUCCESS) {
|
if (error == ES_SUCCESS) {
|
||||||
Process *process = scheduler.SpawnProcess();
|
Process *process = ProcessSpawn(PROCESS_NORMAL);
|
||||||
|
|
||||||
if (!process) {
|
if (!process) {
|
||||||
error = ES_ERROR_INSUFFICIENT_RESOURCES;
|
error = ES_ERROR_INSUFFICIENT_RESOURCES;
|
||||||
|
@ -198,7 +198,7 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_PROCESS_CREATE) {
|
||||||
|
|
||||||
// Start the process.
|
// Start the process.
|
||||||
|
|
||||||
if (error != ES_SUCCESS || !process->StartWithNode(executableObject)) {
|
if (error != ES_SUCCESS || !ProcessStartWithNode(process, executableObject)) {
|
||||||
error = ES_ERROR_UNKNOWN; // TODO.
|
error = ES_ERROR_UNKNOWN; // TODO.
|
||||||
CloseHandleToObject(process, KERNEL_OBJECT_PROCESS);
|
CloseHandleToObject(process, KERNEL_OBJECT_PROCESS);
|
||||||
} else {
|
} else {
|
||||||
|
@ -498,10 +498,10 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_THREAD_TERMINATE) {
|
||||||
{
|
{
|
||||||
SYSCALL_HANDLE(argument0, KERNEL_OBJECT_THREAD, thread, Thread);
|
SYSCALL_HANDLE(argument0, KERNEL_OBJECT_THREAD, thread, Thread);
|
||||||
if (thread == currentThread) self = true;
|
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);
|
SYSCALL_RETURN(ES_SUCCESS, false);
|
||||||
}
|
}
|
||||||
|
@ -514,10 +514,10 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_PROCESS_TERMINATE) {
|
||||||
{
|
{
|
||||||
SYSCALL_HANDLE(argument0, KERNEL_OBJECT_PROCESS, process, Process);
|
SYSCALL_HANDLE(argument0, KERNEL_OBJECT_PROCESS, process, Process);
|
||||||
if (process == currentProcess) self = true;
|
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);
|
SYSCALL_RETURN(ES_SUCCESS, false);
|
||||||
}
|
}
|
||||||
|
@ -525,14 +525,13 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_PROCESS_TERMINATE) {
|
||||||
SYSCALL_IMPLEMENT(ES_SYSCALL_THREAD_CREATE) {
|
SYSCALL_IMPLEMENT(ES_SYSCALL_THREAD_CREATE) {
|
||||||
EsThreadInformation thread;
|
EsThreadInformation thread;
|
||||||
EsMemoryZero(&thread, sizeof(EsThreadInformation));
|
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) {
|
if (!threadObject) {
|
||||||
SYSCALL_RETURN(ES_ERROR_INSUFFICIENT_RESOURCES, false);
|
SYSCALL_RETURN(ES_ERROR_INSUFFICIENT_RESOURCES, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
thread.tid = threadObject->id;
|
thread.tid = threadObject->id;
|
||||||
|
|
||||||
thread.handle = currentProcess->handleTable.OpenHandle(threadObject, 0, KERNEL_OBJECT_THREAD);
|
thread.handle = currentProcess->handleTable.OpenHandle(threadObject, 0, KERNEL_OBJECT_THREAD);
|
||||||
|
|
||||||
SYSCALL_WRITE(argument2, &thread, sizeof(EsThreadInformation));
|
SYSCALL_WRITE(argument2, &thread, sizeof(EsThreadInformation));
|
||||||
|
@ -849,7 +848,7 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_WAIT) {
|
||||||
}
|
}
|
||||||
|
|
||||||
currentThread->terminatableState = THREAD_USER_BLOCK_REQUEST;
|
currentThread->terminatableState = THREAD_USER_BLOCK_REQUEST;
|
||||||
waitReturnValue = scheduler.WaitEvents(events, waitObjectCount);
|
waitReturnValue = KEventWaitMultiple(events, waitObjectCount);
|
||||||
currentThread->terminatableState = THREAD_IN_SYSCALL;
|
currentThread->terminatableState = THREAD_IN_SYSCALL;
|
||||||
|
|
||||||
if (waitReturnValue == argument1) {
|
if (waitReturnValue == argument1) {
|
||||||
|
@ -1082,7 +1081,7 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_WINDOW_GET_BOUNDS) {
|
||||||
|
|
||||||
SYSCALL_IMPLEMENT(ES_SYSCALL_PROCESS_PAUSE) {
|
SYSCALL_IMPLEMENT(ES_SYSCALL_PROCESS_PAUSE) {
|
||||||
SYSCALL_HANDLE(argument0, KERNEL_OBJECT_PROCESS, process, Process);
|
SYSCALL_HANDLE(argument0, KERNEL_OBJECT_PROCESS, process, Process);
|
||||||
scheduler.PauseProcess(process, (bool) argument1);
|
ProcessPause(process, (bool) argument1);
|
||||||
SYSCALL_RETURN(ES_SUCCESS, false);
|
SYSCALL_RETURN(ES_SUCCESS, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1303,7 +1302,7 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_PROCESSOR_COUNT) {
|
||||||
SYSCALL_IMPLEMENT(ES_SYSCALL_PROCESS_OPEN) {
|
SYSCALL_IMPLEMENT(ES_SYSCALL_PROCESS_OPEN) {
|
||||||
SYSCALL_PERMISSION(ES_PERMISSION_PROCESS_OPEN);
|
SYSCALL_PERMISSION(ES_PERMISSION_PROCESS_OPEN);
|
||||||
|
|
||||||
Process *process = scheduler.OpenProcess(argument0);
|
Process *process = ProcessOpen(argument0);
|
||||||
|
|
||||||
if (process) {
|
if (process) {
|
||||||
SYSCALL_RETURN(currentProcess->handleTable.OpenHandle(process, 0, KERNEL_OBJECT_PROCESS), false);
|
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;
|
reason.duringSystemCall = index;
|
||||||
KernelLog(LOG_ERROR, "Syscall", "syscall failure",
|
KernelLog(LOG_ERROR, "Syscall", "syscall failure",
|
||||||
"Process crashed during system call [%x, %x, %x, %x, %x]\n", index, argument0, argument1, argument2, argument3);
|
"Process crashed during system call [%x, %x, %x, %x, %x]\n", index, argument0, argument1, argument2, argument3);
|
||||||
scheduler.CrashProcess(currentProcess, &reason);
|
ProcessCrash(currentProcess, &reason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,8 +90,8 @@ DEFINE_INTERFACE_STRING(CommonUnitMilliseconds, " ms");
|
||||||
// Desktop.
|
// Desktop.
|
||||||
|
|
||||||
DEFINE_INTERFACE_STRING(DesktopNewTabTitle, "New Tab");
|
DEFINE_INTERFACE_STRING(DesktopNewTabTitle, "New Tab");
|
||||||
DEFINE_INTERFACE_STRING(DesktopShutdownTitle, "Shutdown");
|
DEFINE_INTERFACE_STRING(DesktopShutdownTitle, "Shut Down");
|
||||||
DEFINE_INTERFACE_STRING(DesktopShutdownAction, "Shutdown");
|
DEFINE_INTERFACE_STRING(DesktopShutdownAction, "Shut down");
|
||||||
DEFINE_INTERFACE_STRING(DesktopRestartAction, "Restart");
|
DEFINE_INTERFACE_STRING(DesktopRestartAction, "Restart");
|
||||||
DEFINE_INTERFACE_STRING(DesktopForceQuit, "Force quit");
|
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.");
|
DEFINE_INTERFACE_STRING(DesktopCrashedApplication, "The application has crashed. If you're a developer, more information is available in System Monitor.");
|
||||||
|
|
Loading…
Reference in New Issue