Restructured and rewritten entire codebase

This commit is contained in:
Alex
2023-10-09 01:16:24 +03:00
parent 446a571018
commit 889e1522a3
484 changed files with 15683 additions and 14032 deletions

277
tasking/ipc.cpp Normal file
View File

@ -0,0 +1,277 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <ipc.hpp>
#include <task.hpp>
#include "../kernel.h"
#include "../ipc.h"
namespace InterProcessCommunication
{
void IPC::Fork(IPC *Parent)
{
std::vector<IPCHandle *> ParentHandles = Parent->GetHandles();
foreach (auto Hnd in ParentHandles)
{
debug("Forking IPC with ID %d", Hnd->ID);
IPCHandle *NewHnd = (IPCHandle *)vma->RequestPages(TO_PAGES(sizeof(IPCHandle) + 1));
memcpy(NewHnd, Hnd, sizeof(IPCHandle));
NewHnd->Node = fs->Create(Hnd->Node->Name, vfs::NodeType::FILE, IPCNode);
Handles.push_back(NewHnd);
}
}
IPCHandle *IPC::Create(IPCType Type, char UniqueToken[16])
{
UNUSED(Type);
SmartLock(this->IPCLock);
IPCHandle *Hnd = (IPCHandle *)vma->RequestPages(TO_PAGES(sizeof(IPCHandle) + 1));
Hnd->ID = NextID++;
Hnd->Node = fs->Create(UniqueToken, vfs::NodeType::FILE, IPCNode);
Hnd->Buffer = nullptr;
Hnd->Length = 0;
Hnd->Listening = false;
Handles.push_back(Hnd);
debug("Created IPC with ID %d", Hnd->ID);
return Hnd;
}
IPCErrorCode IPC::Destroy(IPCID ID)
{
SmartLock(this->IPCLock);
forItr(itr, Handles)
{
if ((*itr)->ID == ID)
{
fs->Delete((*itr)->Node);
vma->FreePages((*itr), TO_PAGES(sizeof(IPCHandle) + 1));
Handles.erase(itr);
debug("Destroyed IPC with ID %d", ID);
return IPCSuccess;
}
}
debug("Failed to destroy IPC with ID %d", ID);
return IPCIDNotFound;
}
IPCErrorCode IPC::Allocate(IPCID ID, long Size)
{
SmartLock(this->IPCLock);
if (Size < 0)
return IPCError;
foreach (auto Hnd in Handles)
{
if (Hnd->ID == ID)
{
if (Hnd->Buffer != nullptr || Hnd->Length != 0)
return IPCAlreadyAllocated;
Hnd->Buffer = (uint8_t *)vma->RequestPages(TO_PAGES(Size + 1));
Hnd->Length = Size;
return IPCSuccess;
}
}
return IPCIDNotFound;
}
IPCErrorCode IPC::Deallocate(IPCID ID)
{
SmartLock(this->IPCLock);
foreach (auto Hnd in Handles)
{
if (Hnd->ID == ID)
{
if (Hnd->Buffer == nullptr || Hnd->Length == 0)
return IPCNotAllocated;
vma->FreePages(Hnd->Buffer, TO_PAGES(Hnd->Length + 1));
Hnd->Buffer = nullptr;
Hnd->Length = 0;
return IPCSuccess;
}
}
return IPCIDNotFound;
}
IPCErrorCode IPC::Read(IPCID ID, void *Buffer, long Size)
{
SmartLock(this->IPCLock);
if (Size < 0)
return IPCError;
foreach (auto Hnd in Handles)
{
if (Hnd->ID == ID)
{
if (Hnd->Listening)
{
debug("IPC %d is listening", ID);
return IPCNotListening;
}
if (Hnd->Length < Size)
{
debug("IPC %d is too small", ID);
return IPCError;
}
debug("IPC %d reading %d bytes", ID, Size);
memcpy(Buffer, Hnd->Buffer, Size);
debug("IPC read %d bytes", Size);
return IPCSuccess;
}
}
debug("IPC %d not found", ID);
return IPCIDNotFound;
}
IPCErrorCode IPC::Write(IPCID ID, void *Buffer, long Size)
{
SmartLock(this->IPCLock);
if (Size < 0)
{
debug("IPC %d is too small", ID);
return IPCError;
}
foreach (auto Hnd in Handles)
{
if (Hnd->ID == ID)
{
if (!Hnd->Listening)
{
debug("IPC %d is NOT listening", ID);
return IPCNotListening;
}
if (Hnd->Length < Size)
{
debug("IPC %d is too small", ID);
return IPCError;
}
debug("IPC %d writing %d bytes", ID, Size);
memcpy(Hnd->Buffer, Buffer, Size);
Hnd->Listening = false;
debug("IPC %d wrote %d bytes and now is %s", ID, Size, Hnd->Listening ? "listening" : "ready");
return IPCSuccess;
}
}
debug("IPC %d not found", ID);
return IPCIDNotFound;
}
IPCErrorCode IPC::Listen(IPCID ID, bool Listen)
{
foreach (auto Hnd in Handles)
{
if (Hnd->ID == ID)
{
Hnd->Listening = Listen;
debug("IPC %d is now set to %s", ID, Listen ? "listening" : "ready");
return IPCSuccess;
}
}
debug("IPC %d not found", ID);
return IPCIDNotFound;
}
IPCErrorCode IPC::Wait(IPCID ID)
{
foreach (auto Hnd in Handles)
{
if (Hnd->ID == ID)
{
if (!CPU::Interrupts())
warn("Interrupts are disabled. This may cause a kernel hang.");
debug("Waiting for IPC %d (now %s)", ID, Hnd->Listening ? "listening" : "ready");
while (Hnd->Listening)
TaskManager->Yield();
debug("IPC %d is ready", ID);
return IPCSuccess;
}
}
debug("IPC %d not found", ID);
return IPCIDNotFound;
}
IPCHandle *IPC::SearchByToken(char UniqueToken[16])
{
foreach (auto Hnd in Handles)
{
if (strcmp(Hnd->Node->Name, UniqueToken) == 0)
{
debug("Found IPC with token %s", UniqueToken);
return Hnd;
}
}
debug("Failed to find IPC with token %s", UniqueToken);
return nullptr;
}
int IPC::HandleSyscall(long Command, long Type, int ID, int Flags, void *Buffer, size_t Size)
{
switch (Command)
{
case IPC_CREATE:
{
char UniqueToken[16];
if (Buffer != nullptr)
strcpy(UniqueToken, (char *)Buffer);
else
snprintf(UniqueToken, 16, "IPC_%d", ID);
IPCHandle *Hnd = this->Create((IPCType)Type, UniqueToken);
this->Allocate(Hnd->ID, Size ? Size : PAGE_SIZE);
return Hnd->ID;
}
case IPC_READ:
return this->Read(ID, Buffer, Size);
case IPC_WRITE:
return TaskManager->GetProcessByID(Flags)->IPC->Write(ID, Buffer, Size);
case IPC_DELETE:
{
this->Deallocate(ID);
return this->Destroy(ID);
}
case IPC_WAIT:
return this->Wait(ID);
case IPC_LISTEN:
return this->Listen(ID, Flags);
default:
return IPCInvalidCommand;
}
return IPCError;
}
IPC::IPC(void *Process)
{
Tasking::PCB *pcb = (Tasking::PCB *)Process;
this->Process = Process;
this->vma = new Memory::VirtualMemoryArea(pcb->PageTable);
IPCNode = fs->Create("ipc", vfs::NodeType::DIRECTORY,
pcb->ProcessDirectory);
}
IPC::~IPC()
{
fs->Delete(IPCNode, true);
delete this->vma, this->vma = nullptr;
}
}

267
tasking/process.cpp Normal file
View File

@ -0,0 +1,267 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <task.hpp>
#include <dumper.hpp>
#include <convert.h>
#include <lock.hpp>
#include <printf.h>
#include <smp.hpp>
#include <io.h>
#include "../kernel.h"
#if defined(a64)
#include "../arch/amd64/cpu/apic.hpp"
#include "../arch/amd64/cpu/gdt.hpp"
#elif defined(a32)
#include "../arch/i386/cpu/apic.hpp"
#elif defined(aa64)
#endif
// #define DEBUG_TASKING 1
#ifdef DEBUG_TASKING
#define tskdbg(m, ...) \
debug(m, ##__VA_ARGS__); \
__sync
#else
#define tskdbg(m, ...)
#endif
using namespace InterProcessCommunication;
using namespace vfs;
using vfs::NodeType;
namespace Tasking
{
void PCB::Rename(const char *name)
{
assert(name != nullptr);
assert(strlen(name) > 0);
trace("Renaming thread %s to %s",
this->Name, name);
if (this->Name)
{
this->AllocatedMemory -= strlen(this->Name) + 1;
delete[] this->Name;
}
this->Name = new char[strlen(name) + 1];
this->AllocatedMemory += strlen(name) + 1;
strcpy((char *)this->Name, name);
}
void PCB::SetWorkingDirectory(vfs::Node *node)
{
trace("Setting working directory of process %s to %#lx (%s)",
this->Name, node, node->Name);
CurrentWorkingDirectory = node;
}
size_t PCB::GetSize()
{
size_t ret = this->AllocatedMemory;
ret += this->vma->GetAllocatedMemorySize();
for (size_t i = 0; i < this->Threads.size(); i++)
ret += sizeof(TCB);
for (size_t i = 0; i < this->Children.size(); i++)
ret += sizeof(PCB);
return ret;
}
PCB::PCB(Task *ctx, PCB *Parent, const char *Name,
TaskExecutionMode ExecutionMode, void *Image,
bool DoNotCreatePageTable,
uint16_t UserID, uint16_t GroupID)
{
debug("+ %#lx", this);
assert(ctx != nullptr);
assert(Name != nullptr);
assert(strlen(Name) > 0);
assert(ExecutionMode >= _ExecuteModeMin);
assert(ExecutionMode <= _ExecuteModeMax);
this->ctx = ctx;
this->ID = ctx->NextPID++;
if (this->Name) /* Prevent memory leak */
delete[] this->Name;
this->Name = new char[strlen(Name) + 1];
strcpy((char *)this->Name, Name);
this->ExitCode = KILL_CRASH;
/* Check parent */
if (Parent == nullptr)
this->Parent = ctx->GetCurrentProcess();
else
this->Parent = Parent;
/* Set uid & gid */
if (this->Parent &&
UserID == UINT16_MAX &&
GroupID == UINT16_MAX)
{
UserID = this->Parent->Security.Real.UserID;
GroupID = this->Parent->Security.Real.GroupID;
debug("Inherited uid & gid from parent process %s(%d) with uid %d and gid %d",
this->Parent->Name, this->Parent->ID, UserID, GroupID);
}
this->Security.Real.UserID = UserID;
this->Security.Real.GroupID = GroupID;
this->Security.Effective.UserID = UserID;
this->Security.Effective.GroupID = GroupID;
this->Security.ExecutionMode = ExecutionMode;
switch (ExecutionMode)
{
case TaskExecutionMode::System:
fixme("Mode not supported.");
[[fallthrough]];
case TaskExecutionMode::Kernel:
{
this->Security.IsCritical = true;
break;
}
case TaskExecutionMode::User:
{
break;
}
default:
assert(false);
}
char ProcFSName[12];
sprintf(ProcFSName, "%d", this->ID);
this->ProcessDirectory = fs->Create(ProcFSName, DIRECTORY, ProcFS);
this->FileDescriptors = new FileDescriptorTable(this);
/* If create page table */
if (DoNotCreatePageTable == false)
{
OwnPageTable = true;
size_t PTPgs = TO_PAGES(sizeof(Memory::PageTable) + 1);
this->PageTable = (Memory::PageTable *)KernelAllocator.RequestPages(PTPgs);
memcpy(this->PageTable, KernelPageTable, sizeof(Memory::PageTable));
debug("Process %s(%d) has page table at %#lx",
this->Name, this->ID, this->PageTable);
}
this->vma = new Memory::VirtualMemoryArea(this->PageTable);
this->ProgramBreak = new Memory::ProgramBreak(this->PageTable, this->vma);
this->IPC = new class IPC((void *)this);
if (Image)
{
this->ELFSymbolTable = new SymbolResolver::Symbols((uintptr_t)Image);
this->AllocatedMemory += sizeof(SymbolResolver::Symbols);
}
if (Parent)
Parent->Children.push_back(this);
debug("Process page table: %#lx", this->PageTable);
debug("Created %s process \"%s\"(%d). Parent \"%s\"(%d)",
ExecutionMode == TaskExecutionMode::User ? "user" : "kernel",
this->Name, this->ID,
Parent ? this->Parent->Name : "None",
Parent ? this->Parent->ID : 0);
this->AllocatedMemory += strlen(Name) + 1;
this->AllocatedMemory += sizeof(PCB);
this->AllocatedMemory += sizeof(FileDescriptorTable);
this->AllocatedMemory += FROM_PAGES(TO_PAGES(sizeof(Memory::PageTable) + 1));
this->AllocatedMemory += sizeof(Memory::VirtualMemoryArea);
this->AllocatedMemory += sizeof(Memory::ProgramBreak);
this->AllocatedMemory += sizeof(class IPC);
this->Info.SpawnTime = TimeManager->GetCounter();
ctx->ProcessList.push_back(this);
}
PCB::~PCB()
{
debug("- %#lx", this);
debug("Destroying process \"%s\"(%d)",
this->Name, this->ID);
debug("Removing from process list");
/* Remove us from the process list so we
don't get scheduled anymore */
ctx->ProcessList.erase(std::find(ctx->ProcessList.begin(),
ctx->ProcessList.end(),
this));
debug("Freeing symbol table strings");
if (this->ELFSymbolTable)
delete this->ELFSymbolTable;
debug("Freeing IPC");
delete this->IPC;
debug("Freeing allocated memory");
delete this->ProgramBreak;
delete this->vma;
debug("Closing file descriptors");
delete this->FileDescriptors;
/* If we own the pointer to the
PageTable, we need to free it */
if (this->PageTable && OwnPageTable)
{
debug("Freeing page table");
size_t PTPgs = TO_PAGES(sizeof(Memory::PageTable) + 1);
KernelAllocator.FreePages(this->PageTable, PTPgs);
}
/* Exit all children processes */
foreach (auto pcb in this->Children)
delete pcb;
/* Exit all threads */
foreach (auto tcb in this->Threads)
delete tcb;
debug("Removing /proc/%d", this->ID);
fs->Delete(this->ProcessDirectory, true);
/* Free Name */
delete[] this->Name;
debug("Removing from parent process");
if (this->Parent)
{
std::vector<Tasking::PCB *> &pChild = this->Parent->Children;
pChild.erase(std::find(pChild.begin(),
pChild.end(),
this));
}
}
}

769
tasking/scheduler.cpp Normal file
View File

@ -0,0 +1,769 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <task.hpp>
#include <dumper.hpp>
#include <convert.h>
#include <lock.hpp>
#include <printf.h>
#include <smp.hpp>
#include <io.h>
#include "../kernel.h"
#if defined(a64)
#include "../arch/amd64/cpu/apic.hpp"
#include "../arch/amd64/cpu/gdt.hpp"
#elif defined(a32)
#include "../arch/i386/cpu/apic.hpp"
#include "../arch/i386/cpu/gdt.hpp"
#elif defined(aa64)
#endif
/* FIXME: On screen task manager is corrupting the stack... */
// #define ON_SCREEN_SCHEDULER_TASK_MANAGER 1
// #define DEBUG_SCHEDULER 1
// #define DEBUG_GET_NEXT_AVAILABLE_PROCESS 1
// #define DEBUG_GET_NEXT_AVAILABLE_THREAD 1
// #define DEBUG_FIND_NEW_PROCESS 1
// #define DEBUG_SCHEDULER_SEARCH_PROCESS_THREAD 1
// #define DEBUG_WAKE_UP_THREADS 1
/* Global */
#ifdef DEBUG_SCHEDULER
#define DEBUG_GET_NEXT_AVAILABLE_PROCESS 1
#define DEBUG_GET_NEXT_AVAILABLE_THREAD 1
#define DEBUG_FIND_NEW_PROCESS 1
#define DEBUG_SCHEDULER_SEARCH_PROCESS_THREAD 1
#define DEBUG_WAKE_UP_THREADS 1
#define schedbg(m, ...) \
debug(m, ##__VA_ARGS__); \
__sync
#else
#define schedbg(m, ...)
#endif
/* GetNextAvailableThread */
#ifdef DEBUG_GET_NEXT_AVAILABLE_PROCESS
#define gnap_schedbg(m, ...) \
debug(m, ##__VA_ARGS__); \
__sync
#else
#define gnap_schedbg(m, ...)
#endif
/* GetNextAvailableProcess */
#ifdef DEBUG_GET_NEXT_AVAILABLE_THREAD
#define gnat_schedbg(m, ...) \
debug(m, ##__VA_ARGS__); \
__sync
#else
#define gnat_schedbg(m, ...)
#endif
/* FindNewProcess */
#ifdef DEBUG_FIND_NEW_PROCESS
#define fnp_schedbg(m, ...) \
debug(m, ##__VA_ARGS__); \
__sync
#else
#define fnp_schedbg(m, ...)
#endif
/* SchedulerSearchProcessThread */
#ifdef DEBUG_SCHEDULER_SEARCH_PROCESS_THREAD
#define sspt_schedbg(m, ...) \
debug(m, ##__VA_ARGS__); \
__sync
#else
#define sspt_schedbg(m, ...)
#endif
/* WakeUpThreads */
#ifdef DEBUG_WAKE_UP_THREADS
#define wut_schedbg(m, ...) \
debug(m, ##__VA_ARGS__); \
__sync
#else
#define wut_schedbg(m, ...)
#endif
extern "C" SafeFunction NIF void TaskingScheduler_OneShot(int TimeSlice)
{
if (TimeSlice == 0)
TimeSlice = Tasking::TaskPriority::Normal;
#if defined(a86)
((APIC::Timer *)Interrupts::apicTimer[GetCurrentCPU()->ID])->OneShot(CPU::x86::IRQ16, TimeSlice);
#elif defined(aa64)
#endif
}
namespace Tasking
{
#if defined(a86)
SafeFunction NIF bool Task::FindNewProcess(void *CPUDataPointer)
{
CPUData *CurrentCPU = (CPUData *)CPUDataPointer;
fnp_schedbg("%d processes", ProcessList.size());
#ifdef DEBUG_FIND_NEW_PROCESS
foreach (auto process in ProcessList)
fnp_schedbg("Process %d %s", process->ID,
process->Name);
#endif
foreach (auto process in ProcessList)
{
if (unlikely(InvalidPCB(process)))
continue;
switch (process->State.load())
{
case TaskState::Ready:
fnp_schedbg("Ready process (%s)%d",
process->Name, process->ID);
break;
default:
fnp_schedbg("Process \"%s\"(%d) status %d",
process->Name, process->ID,
process->State);
/* We don't actually remove the process. RemoveProcess
firstly checks if it's terminated, if not, it will
loop through Threads and call RemoveThread on
terminated threads. */
RemoveProcess(process);
continue;
}
foreach (auto thread in process->Threads)
{
if (unlikely(InvalidTCB(thread)))
continue;
if (thread->State.load() != TaskState::Ready)
continue;
if (thread->Info.Affinity[CurrentCPU->ID] == false)
continue;
CurrentCPU->CurrentProcess = process;
CurrentCPU->CurrentThread = thread;
return true;
}
}
fnp_schedbg("No process to run.");
return false;
}
SafeFunction NIF bool Task::GetNextAvailableThread(void *CPUDataPointer)
{
CPUData *CurrentCPU = (CPUData *)CPUDataPointer;
size_t ThreadsSize = CurrentCPU->CurrentProcess->Threads.size();
for (size_t i = 0; i < ThreadsSize; i++)
{
if (CurrentCPU->CurrentProcess->Threads[i] == CurrentCPU->CurrentThread.load())
{
size_t TempIndex = i;
RetryAnotherThread:
if (TempIndex + 1 >= ThreadsSize)
break;
TCB *nextThread = CurrentCPU->CurrentProcess->Threads[TempIndex + 1];
if (unlikely(InvalidTCB(nextThread)))
{
if (TempIndex > ThreadsSize)
break;
TempIndex++;
gnat_schedbg("Thread %#lx is invalid", nextThread);
goto RetryAnotherThread;
}
gnat_schedbg("\"%s\"(%d) and next thread is \"%s\"(%d)",
CurrentCPU->CurrentProcess->Threads[i]->Name,
CurrentCPU->CurrentProcess->Threads[i]->ID,
nextThread->Name, nextThread->ID);
if (nextThread->State.load() != TaskState::Ready)
{
gnat_schedbg("Thread %d is not ready", nextThread->ID);
TempIndex++;
goto RetryAnotherThread;
}
if (nextThread->Info.Affinity[CurrentCPU->ID] == false)
continue;
CurrentCPU->CurrentThread = nextThread;
gnat_schedbg("[thd 0 -> end] Scheduling thread %d parent of %s->%d Procs %d",
nextThread->ID, nextThread->Parent->Name,
ThreadsSize, ProcessList.size());
return true;
}
#ifdef DEBUG
else
{
gnat_schedbg("Thread %d is not the current one",
CurrentCPU->CurrentProcess->Threads[i]->ID);
}
#endif
}
return false;
}
SafeFunction NIF bool Task::GetNextAvailableProcess(void *CPUDataPointer)
{
CPUData *CurrentCPU = (CPUData *)CPUDataPointer;
bool Skip = true;
foreach (auto process in ProcessList)
{
if (process == CurrentCPU->CurrentProcess.load())
{
Skip = false;
gnap_schedbg("Found current process %#lx", process);
continue;
}
if (Skip)
{
gnap_schedbg("Skipping process %#lx", process);
continue;
}
if (unlikely(InvalidPCB(process)))
{
gnap_schedbg("Invalid process %#lx", process);
continue;
}
if (process->State.load() != TaskState::Ready)
{
gnap_schedbg("Process %d is not ready", process->ID);
continue;
}
foreach (auto thread in process->Threads)
{
if (unlikely(InvalidTCB(thread)))
{
gnap_schedbg("Invalid thread %#lx", thread);
continue;
}
if (thread->State.load() != TaskState::Ready)
{
gnap_schedbg("Thread %d is not ready", thread->ID);
continue;
}
if (thread->Info.Affinity[CurrentCPU->ID] == false)
continue;
CurrentCPU->CurrentProcess = process;
CurrentCPU->CurrentThread = thread;
gnap_schedbg("[cur proc+1 -> first thd] Scheduling thread %d %s->%d (Total Procs %d)",
thread->ID, thread->Name, process->Threads.size(), ProcessList.size());
return true;
}
}
gnap_schedbg("No process to run.");
return false;
}
SafeFunction NIF bool Task::SchedulerSearchProcessThread(void *CPUDataPointer)
{
CPUData *CurrentCPU = (CPUData *)CPUDataPointer;
foreach (auto process in ProcessList)
{
if (unlikely(InvalidPCB(process)))
{
sspt_schedbg("Invalid process %#lx", process);
continue;
}
if (process->State.load() != TaskState::Ready)
{
sspt_schedbg("Process %d is not ready", process->ID);
continue;
}
foreach (auto thread in process->Threads)
{
if (unlikely(InvalidTCB(thread)))
{
sspt_schedbg("Invalid thread %#lx", thread);
continue;
}
if (thread->State.load() != TaskState::Ready)
{
sspt_schedbg("Thread %d is not ready", thread->ID);
continue;
}
if (thread->Info.Affinity[CurrentCPU->ID] == false)
continue;
CurrentCPU->CurrentProcess = process;
CurrentCPU->CurrentThread = thread;
sspt_schedbg("[proc 0 -> end -> first thd] Scheduling thread %d parent of %s->%d (Procs %d)",
thread->ID, thread->Parent->Name, process->Threads.size(), ProcessList.size());
return true;
}
}
return false;
}
SafeFunction NIF void Task::UpdateProcessState()
{
foreach (auto process in ProcessList)
{
if (unlikely(InvalidPCB(process)))
continue;
if (process->State.load() == TaskState::Terminated)
continue;
if (process->Threads.size() == 1)
{
process->State.exchange(process->Threads[0]->State.load());
continue;
}
bool AllThreadsSleeping = true;
foreach (auto thread in process->Threads)
{
if (thread->State.load() != TaskState::Sleeping)
{
AllThreadsSleeping = false;
break;
}
}
if (AllThreadsSleeping)
process->State.store(TaskState::Sleeping);
else if (process->State.load() == TaskState::Sleeping)
process->State.store(TaskState::Ready);
}
}
SafeFunction NIF void Task::WakeUpThreads()
{
foreach (auto process in ProcessList)
{
if (unlikely(InvalidPCB(process)))
continue;
Tasking::TaskState pState = process->State.load();
if (pState != TaskState::Ready &&
pState != TaskState::Sleeping &&
pState != TaskState::Blocked)
continue;
foreach (auto thread in process->Threads)
{
if (unlikely(InvalidTCB(thread)))
continue;
if (likely(thread->State.load() != TaskState::Sleeping))
continue;
/* Check if the thread is ready to wake up. */
if (unlikely(thread->Info.SleepUntil < TimeManager->GetCounter()))
{
if (pState == TaskState::Sleeping)
process->State.store(TaskState::Ready);
thread->State.store(TaskState::Ready);
thread->Info.SleepUntil = 0;
wut_schedbg("Thread \"%s\"(%d) woke up.", thread->Name, thread->ID);
}
else
{
wut_schedbg("Thread \"%s\"(%d) is not ready to wake up. (SleepUntil: %d, Counter: %d)",
thread->Name, thread->ID, thread->Info.SleepUntil, TimeManager->GetCounter());
}
}
}
}
#ifdef ON_SCREEN_SCHEDULER_TASK_MANAGER
int SuccessSource = 0;
int sanity;
const char *Statuses[] = {
"FF0000", /* Unknown */
"AAFF00", /* Ready */
"00AA00", /* Running */
"FFAA00", /* Sleeping */
"FFAA00", /* Blocked */
"FFAA00", /* Stopped */
"FFAA00", /* Waiting */
"FF0088", /* Zombie */
"FF0000", /* Terminated */
};
const char *StatusesSign[] = {
"Unknown",
"Ready",
"Run",
"Sleep",
"Block",
"Wait",
"Zombie",
"Terminated",
};
const char *SuccessSourceStrings[] = {
"Unknown",
"GetNextAvailableThread",
"GetNextAvailableProcess",
"SchedulerSearchProcessThread",
};
SafeFunction NIF void OnScreenTaskManagerUpdate()
{
TimeManager->Sleep(10, Time::Units::Milliseconds);
Video::ScreenBuffer *sb = Display->GetBuffer(0);
for (short i = 0; i < 340; i++)
{
for (short j = 0; j < 600; j++)
{
uint32_t *Pixel = (uint32_t *)((uintptr_t)sb->Buffer + (j * sb->Width + i) * (bInfo.Framebuffer[0].BitsPerPixel / 8));
*Pixel = 0x222222;
}
}
uint32_t tmpX, tmpY;
Display->GetBufferCursor(0, &tmpX, &tmpY);
Display->SetBufferCursor(0, 0, 0);
printf("\eF02C21Task Manager\n");
foreach (auto Proc in TaskManager->GetProcessList())
{
int State = Proc->State;
printf("\e%s-> \eAABBCC%s \e00AAAA%s\n",
Statuses[State], Proc->Name, StatusesSign[State]);
foreach (auto Thd in Proc->Threads)
{
State = Thd->State;
printf(" \e%s-> \eAABBCC%s \e00AAAA%s\n\eAABBCC",
Statuses[State], Thd->Name, StatusesSign[State]);
}
}
register uintptr_t CurrentStackAddress asm("rsp");
printf("Sanity: %d, Stack: %#lx\nSched. Source: %s", sanity++, CurrentStackAddress, SuccessSourceStrings[SuccessSource]);
if (sanity > 1000)
sanity = 0;
Display->SetBufferCursor(0, tmpX, tmpY);
Display->SetBuffer(0);
TimeManager->Sleep(10, Time::Units::Milliseconds);
}
#endif
#ifdef a64
SafeFunction NIF void Task::Schedule(CPU::x64::TrapFrame *Frame)
#else
SafeFunction NIF void Task::Schedule(CPU::x32::TrapFrame *Frame)
#endif
{
if (unlikely(StopScheduler))
{
warn("Scheduler stopped.");
return;
}
bool ProcessNotChanged = false;
/* Restore kernel page table for safety reasons. */
#ifdef a64
CPU::x64::writecr3({.raw = (uint64_t)KernelPageTable});
#else
CPU::x32::writecr3({.raw = (uint32_t)KernelPageTable});
#endif
uint64_t SchedTmpTicks = TimeManager->GetCounter();
this->LastTaskTicks.store(size_t(SchedTmpTicks - this->SchedulerTicks.load()));
CPUData *CurrentCPU = GetCurrentCPU();
this->LastCore.store(CurrentCPU->ID);
schedbg("Scheduler called on CPU %d.", CurrentCPU->ID);
#ifdef DEBUG_SCHEDULER
{
schedbg("================================================================");
schedbg("State: 0-ukn | 1-rdy | 2-run | 3-wait | 4-term");
schedbg("Technical Informations on regs %#lx", Frame->InterruptNumber);
size_t ds;
asmv("mov %%ds, %0"
: "=r"(ds));
schedbg("FS=%#lx GS=%#lx SS=%#lx CS=%#lx DS=%#lx",
CPU::x64::rdmsr(CPU::x64::MSR_FS_BASE), CPU::x64::rdmsr(CPU::x64::MSR_GS_BASE),
Frame->ss, Frame->cs, ds);
schedbg("R8=%#lx R9=%#lx R10=%#lx R11=%#lx",
Frame->r8, Frame->r9, Frame->r10, Frame->r11);
schedbg("R12=%#lx R13=%#lx R14=%#lx R15=%#lx",
Frame->r12, Frame->r13, Frame->r14, Frame->r15);
schedbg("RAX=%#lx RBX=%#lx RCX=%#lx RDX=%#lx",
Frame->rax, Frame->rbx, Frame->rcx, Frame->rdx);
schedbg("RSI=%#lx RDI=%#lx RBP=%#lx RSP=%#lx",
Frame->rsi, Frame->rdi, Frame->rbp, Frame->rsp);
schedbg("RIP=%#lx RFL=%#lx INT=%#lx ERR=%#lx",
Frame->rip, Frame->rflags, Frame->InterruptNumber, Frame->ErrorCode);
schedbg("================================================================");
}
#endif
if (unlikely(InvalidPCB(CurrentCPU->CurrentProcess.load()) ||
InvalidTCB(CurrentCPU->CurrentThread.load())))
{
schedbg("Invalid process or thread. Finding a new one.");
ProcessNotChanged = true;
if (this->FindNewProcess(CurrentCPU))
goto Success;
else
goto Idle;
}
else
{
CurrentCPU->CurrentThread->Registers = *Frame;
CPU::x64::fxsave(&CurrentCPU->CurrentThread->FPU);
#ifdef a64
CurrentCPU->CurrentThread->ShadowGSBase = CPU::x64::rdmsr(CPU::x64::MSR_SHADOW_GS_BASE);
CurrentCPU->CurrentThread->GSBase = CPU::x64::rdmsr(CPU::x64::MSR_GS_BASE);
CurrentCPU->CurrentThread->FSBase = CPU::x64::rdmsr(CPU::x64::MSR_FS_BASE);
#else
CurrentCPU->CurrentThread->ShadowGSBase = uintptr_t(CPU::x32::rdmsr(CPU::x32::MSR_SHADOW_GS_BASE));
CurrentCPU->CurrentThread->GSBase = uintptr_t(CPU::x32::rdmsr(CPU::x32::MSR_GS_BASE));
CurrentCPU->CurrentThread->FSBase = uintptr_t(CPU::x32::rdmsr(CPU::x32::MSR_FS_BASE));
#endif
if (CurrentCPU->CurrentProcess->State.load() == TaskState::Running)
CurrentCPU->CurrentProcess->State.store(TaskState::Ready);
if (CurrentCPU->CurrentThread->State.load() == TaskState::Running)
CurrentCPU->CurrentThread->State.store(TaskState::Ready);
this->UpdateProcessState();
schedbg("Passed UpdateProcessState");
this->WakeUpThreads();
schedbg("Passed WakeUpThreads");
if (this->SchedulerUpdateTrapFrame)
{
this->SchedulerUpdateTrapFrame = false;
goto Success;
}
if (this->GetNextAvailableThread(CurrentCPU))
{
#ifdef ON_SCREEN_SCHEDULER_TASK_MANAGER
SuccessSource = 1;
#endif
ProcessNotChanged = true;
goto Success;
}
schedbg("Passed GetNextAvailableThread");
if (this->GetNextAvailableProcess(CurrentCPU))
{
#ifdef ON_SCREEN_SCHEDULER_TASK_MANAGER
SuccessSource = 2;
#endif
goto Success;
}
schedbg("Passed GetNextAvailableProcess");
if (SchedulerSearchProcessThread(CurrentCPU))
{
#ifdef ON_SCREEN_SCHEDULER_TASK_MANAGER
SuccessSource = 3;
#endif
schedbg("Passed SchedulerSearchProcessThread");
goto Success;
}
else
{
schedbg("SchedulerSearchProcessThread failed. Going idle.");
goto Idle;
}
}
warn("Unwanted reach!");
TaskingScheduler_OneShot(100);
goto End;
Idle:
ProcessNotChanged = true;
CurrentCPU->CurrentProcess = IdleProcess;
CurrentCPU->CurrentThread = IdleThread;
Success:
schedbg("Process \"%s\"(%d) Thread \"%s\"(%d) is now running on CPU %d",
CurrentCPU->CurrentProcess->Name, CurrentCPU->CurrentProcess->ID,
CurrentCPU->CurrentThread->Name, CurrentCPU->CurrentThread->ID, CurrentCPU->ID);
if (!ProcessNotChanged)
UpdateUsage(&CurrentCPU->CurrentProcess->Info,
CurrentCPU->CurrentProcess->Security.ExecutionMode,
CurrentCPU->ID);
UpdateUsage(&CurrentCPU->CurrentThread->Info,
CurrentCPU->CurrentThread->Security.ExecutionMode,
CurrentCPU->ID);
CurrentCPU->CurrentProcess->State.store(TaskState::Running);
CurrentCPU->CurrentThread->State.store(TaskState::Running);
*Frame = CurrentCPU->CurrentThread->Registers;
for (size_t i = 0; i < (sizeof(CurrentCPU->CurrentThread->IPHistory) / sizeof(CurrentCPU->CurrentThread->IPHistory[0])) - 1; i++)
CurrentCPU->CurrentThread->IPHistory[i + 1] = CurrentCPU->CurrentThread->IPHistory[i];
#ifdef a64
CurrentCPU->CurrentThread->IPHistory[0] = Frame->rip;
GlobalDescriptorTable::SetKernelStack((void *)((uintptr_t)CurrentCPU->CurrentThread->Stack->GetStackTop()));
CPU::x64::fxrstor(&CurrentCPU->CurrentThread->FPU);
CPU::x64::wrmsr(CPU::x64::MSR_SHADOW_GS_BASE, CurrentCPU->CurrentThread->ShadowGSBase);
CPU::x64::wrmsr(CPU::x64::MSR_GS_BASE, CurrentCPU->CurrentThread->GSBase);
CPU::x64::wrmsr(CPU::x64::MSR_FS_BASE, CurrentCPU->CurrentThread->FSBase);
#else
CurrentCPU->CurrentThread->IPHistory[0] = Frame->eip;
GlobalDescriptorTable::SetKernelStack((void *)((uintptr_t)CurrentCPU->CurrentThread->Stack->GetStackTop()));
CPU::x32::fxrstor(&CurrentCPU->CurrentThread->FPU);
CPU::x32::wrmsr(CPU::x32::MSR_SHADOW_GS_BASE, CurrentCPU->CurrentThread->ShadowGSBase);
CPU::x32::wrmsr(CPU::x32::MSR_GS_BASE, CurrentCPU->CurrentThread->GSBase);
CPU::x32::wrmsr(CPU::x32::MSR_FS_BASE, CurrentCPU->CurrentThread->FSBase);
#endif
#ifdef ON_SCREEN_SCHEDULER_TASK_MANAGER
OnScreenTaskManagerUpdate();
#endif
switch (CurrentCPU->CurrentProcess->Security.ExecutionMode)
{
case TaskExecutionMode::System:
case TaskExecutionMode::Kernel:
// wrmsr(MSR_SHADOW_GS_BASE, (uint64_t)CurrentCPU->CurrentThread);
break;
case TaskExecutionMode::User:
// wrmsr(MSR_SHADOW_GS_BASE, CurrentCPU->CurrentThread->gs);
break;
default:
error("Unknown trust level %d.",
CurrentCPU->CurrentProcess->Security.ExecutionMode);
break;
}
if (!ProcessNotChanged)
(&CurrentCPU->CurrentProcess->Info)->LastUpdateTime = TimeManager->GetCounter();
(&CurrentCPU->CurrentThread->Info)->LastUpdateTime = TimeManager->GetCounter();
TaskingScheduler_OneShot(CurrentCPU->CurrentThread->Info.Priority);
if (CurrentCPU->CurrentThread->Security.IsDebugEnabled && CurrentCPU->CurrentThread->Security.IsKernelDebugEnabled)
{
#ifdef a64
trace("%s[%ld]: RIP=%#lx RBP=%#lx RSP=%#lx",
CurrentCPU->CurrentThread->Name, CurrentCPU->CurrentThread->ID,
CurrentCPU->CurrentThread->Registers.rip,
CurrentCPU->CurrentThread->Registers.rbp,
CurrentCPU->CurrentThread->Registers.rsp);
#else
trace("%s[%ld]: EIP=%#lx EBP=%#lx ESP=%#lx",
CurrentCPU->CurrentThread->Name, CurrentCPU->CurrentThread->ID,
CurrentCPU->CurrentThread->Registers.eip,
CurrentCPU->CurrentThread->Registers.ebp,
CurrentCPU->CurrentThread->Registers.esp);
#endif
}
schedbg("================================================================");
schedbg("Technical Informations on Thread %s[%ld]:",
CurrentCPU->CurrentThread->Name, CurrentCPU->CurrentThread->ID);
uintptr_t ds;
asmv("mov %%ds, %0"
: "=r"(ds));
schedbg("FS=%#lx GS=%#lx SS=%#lx CS=%#lx DS=%#lx",
CPU::x64::rdmsr(CPU::x64::MSR_FS_BASE), CPU::x64::rdmsr(CPU::x64::MSR_GS_BASE),
Frame->ss, Frame->cs, ds);
schedbg("R8=%#lx R9=%#lx R10=%#lx R11=%#lx",
Frame->r8, Frame->r9, Frame->r10, Frame->r11);
schedbg("R12=%#lx R13=%#lx R14=%#lx R15=%#lx",
Frame->r12, Frame->r13, Frame->r14, Frame->r15);
schedbg("RAX=%#lx RBX=%#lx RCX=%#lx RDX=%#lx",
Frame->rax, Frame->rbx, Frame->rcx, Frame->rdx);
schedbg("RSI=%#lx RDI=%#lx RBP=%#lx RSP=%#lx",
Frame->rsi, Frame->rdi, Frame->rbp, Frame->rsp);
schedbg("RIP=%#lx RFL=%#lx INT=%#lx ERR=%#lx",
Frame->rip, Frame->rflags, Frame->InterruptNumber, Frame->ErrorCode);
schedbg("================================================================");
End:
this->SchedulerTicks.store(size_t(TimeManager->GetCounter() - SchedTmpTicks));
#ifdef a64
CPU::x64::writecr3({.raw = (uint64_t)CurrentCPU->CurrentProcess->PageTable});
#else
CPU::x32::writecr3({.raw = (uint64_t)CurrentCPU->CurrentProcess->PageTable});
#endif
}
#ifdef a64
SafeFunction NIF void Task::OnInterruptReceived(CPU::x64::TrapFrame *Frame)
#else
SafeFunction NIF void Task::OnInterruptReceived(CPU::x32::TrapFrame *Frame)
#endif
{
SmartCriticalSection(SchedulerLock);
this->Schedule(Frame);
}
#elif defined(aa64)
SafeFunction bool Task::FindNewProcess(void *CPUDataPointer)
{
fixme("unimplemented");
}
SafeFunction bool Task::GetNextAvailableThread(void *CPUDataPointer)
{
fixme("unimplemented");
}
SafeFunction bool Task::GetNextAvailableProcess(void *CPUDataPointer)
{
fixme("unimplemented");
}
SafeFunction bool Task::SchedulerSearchProcessThread(void *CPUDataPointer)
{
fixme("unimplemented");
}
SafeFunction void Task::Schedule(CPU::aarch64::TrapFrame *Frame)
{
fixme("unimplemented");
}
SafeFunction void Task::OnInterruptReceived(CPU::aarch64::TrapFrame *Frame) { this->Schedule(Frame); }
#endif
}

464
tasking/task.cpp Normal file
View File

@ -0,0 +1,464 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <task.hpp>
#include <dumper.hpp>
#include <convert.h>
#include <lock.hpp>
#include <printf.h>
#include <smp.hpp>
#include <io.h>
#include "../kernel.h"
#if defined(a64)
#include "../arch/amd64/cpu/apic.hpp"
#include "../arch/amd64/cpu/gdt.hpp"
#elif defined(a32)
#include "../arch/i386/cpu/apic.hpp"
#elif defined(aa64)
#endif
// #define DEBUG_TASKING 1
#ifdef DEBUG_TASKING
#define tskdbg(m, ...) \
debug(m, ##__VA_ARGS__); \
__sync
#else
#define tskdbg(m, ...)
#endif
namespace Tasking
{
#if defined(a86)
__naked __used __no_stack_protector void IdleProcessLoop()
{
asmv("IdleLoop:");
asmv("hlt");
asmv("jmp IdleLoop");
#elif defined(aa64)
__used __no_stack_protector void IdleProcessLoop()
{
asmv("IdleLoop:");
asmv("wfe");
asmv("b IdleLoop");
#endif
}
SafeFunction bool Task::InvalidPCB(PCB *pcb)
{
if (!pcb)
return true;
/* Uninitialized pointers may have uintptr_t max value instead of nullptr. */
if (pcb >= (PCB *)(UINTPTR_MAX - 0x1FFE))
return true;
/* In this section of the memory is reserved by the kernel. */
if (pcb < (PCB *)(0xFFFFF))
return true;
/* Check if it's mapped. */
if (!Memory::Virtual().Check((void *)pcb))
return true;
return false;
}
SafeFunction bool Task::InvalidTCB(TCB *tcb)
{
if (!tcb)
return true;
/* Uninitialized pointers may have uintptr_t max value instead of nullptr. */
if (tcb >= (TCB *)(UINTPTR_MAX - 0x1FFE))
return true;
/* In this section of the memory is reserved by the kernel. */
if (tcb < (TCB *)(0xFFFFF))
return true;
/* Check if it's mapped. */
if (!Memory::Virtual().Check((void *)tcb))
return true;
return false;
}
SafeFunction bool Task::RemoveThread(TCB *Thread)
{
if (Thread->KeepInMemory.load() == true)
return false;
if (Thread->KeepTime > TimeManager->GetCounter())
return false;
debug("Thread \"%s\"(%d) removed from process \"%s\"(%d)",
Thread->Name, Thread->ID, Thread->Parent->Name,
Thread->Parent->ID);
delete Thread;
return true;
}
SafeFunction bool Task::RemoveProcess(PCB *Process)
{
if (unlikely(InvalidPCB(Process)))
return false;
if (Process->State == Terminated)
{
if (Process->KeepInMemory.load() == true)
return false;
if (Process->KeepTime > TimeManager->GetCounter())
return false;
delete Process;
return true;
}
foreach (TCB *Thread in Process->Threads)
{
if (Thread->State == Terminated)
RemoveThread(Thread);
}
return true;
}
SafeFunction void Task::UpdateUsage(TaskInfo *Info, TaskExecutionMode Mode, int Core)
{
UNUSED(Core);
uint64_t CurrentTime = TimeManager->GetCounter();
uint64_t TimePassed = CurrentTime - Info->LastUpdateTime;
// Info->LastUpdateTime = CurrentTime;
if (Mode == TaskExecutionMode::User)
Info->UserTime += TimePassed;
else
Info->KernelTime += TimePassed;
}
PCB *Task::GetCurrentProcess()
{
return GetCurrentCPU()->CurrentProcess.load();
}
TCB *Task::GetCurrentThread()
{
return GetCurrentCPU()->CurrentThread.load();
}
PCB *Task::GetProcessByID(TID ID)
{
SmartLock(TaskingLock);
foreach (auto p in ProcessList)
{
if (p->ID == ID)
return p;
}
return nullptr;
}
TCB *Task::GetThreadByID(TID ID)
{
SmartLock(TaskingLock);
foreach (auto p in ProcessList)
{
foreach (auto t in p->Threads)
{
if (t->ID == ID)
return t;
}
}
return nullptr;
}
void Task::WaitForProcess(PCB *pcb)
{
if (unlikely(InvalidPCB(pcb)))
return;
if (pcb->State == TaskState::UnknownStatus)
return;
debug("Waiting for process \"%s\"(%d)",
pcb->Name, pcb->ID);
while (pcb->State != TaskState::Terminated &&
pcb->State != TaskState::Zombie)
this->Yield();
}
void Task::WaitForThread(TCB *tcb)
{
if (unlikely(InvalidTCB(tcb)))
return;
if (tcb->State == TaskState::UnknownStatus)
return;
debug("Waiting for thread \"%s\"(%d)",
tcb->Name, tcb->ID);
while (tcb->State != TaskState::Terminated &&
tcb->State != TaskState::Zombie)
this->Yield();
}
void Task::WaitForProcessStatus(PCB *pcb, TaskState status)
{
if (unlikely(InvalidPCB(pcb)))
return;
if (pcb->State == TaskState::UnknownStatus)
return;
debug("Waiting for process \"%s\"(%d) to reach status: %d",
pcb->Name, pcb->ID, status);
while (pcb->State != status)
this->Yield();
}
void Task::WaitForThreadStatus(TCB *tcb, TaskState status)
{
if (unlikely(InvalidTCB(tcb)))
return;
if (tcb->State == TaskState::UnknownStatus)
return;
debug("Waiting for thread \"%s\"(%d) to reach status: %d",
tcb->Name, tcb->ID, status);
while (tcb->State != status)
this->Yield();
}
void Task::Sleep(uint64_t Milliseconds, bool NoSwitch)
{
TCB *thread = this->GetCurrentThread();
PCB *process = this->GetCurrentProcess();
thread->State = TaskState::Sleeping;
{
SmartLock(TaskingLock);
if (process->Threads.size() == 1)
process->State = TaskState::Sleeping;
thread->Info.SleepUntil =
TimeManager->CalculateTarget(Milliseconds,
Time::Units::Milliseconds);
}
// #ifdef DEBUG
// uint64_t TicksNow = TimeManager->GetCounter();
// #endif
// debug("Thread \"%s\"(%d) is going to sleep until %llu, current %llu, diff %llu",
// thread->Name, thread->ID, thread->Info.SleepUntil,
// TicksNow, thread->Info.SleepUntil - TicksNow);
if (!NoSwitch)
this->Yield();
}
void Task::SignalShutdown()
{
fixme("SignalShutdown()");
// TODO: Implement this
// This should hang until all processes are terminated
}
void Task::CleanupProcessesThread()
{
thisThread->Rename("Tasking Cleanup");
while (true)
{
this->Sleep(2000);
{
SmartLock(TaskingLock);
foreach (auto Process in ProcessList)
{
if (unlikely(InvalidPCB(Process)))
continue;
RemoveProcess(Process);
}
}
}
}
__no_sanitize("undefined")
TCB *Task::CreateThread(PCB *Parent,
IP EntryPoint,
const char **argv,
const char **envp,
const std::vector<AuxiliaryVector> &auxv,
TaskArchitecture arch,
TaskCompatibility Compatibility,
bool ThreadNotReady)
{
SmartLock(TaskingLock);
return new TCB(this, Parent, EntryPoint,
argv, envp, auxv, arch,
Compatibility, ThreadNotReady);
}
PCB *Task::CreateProcess(PCB *Parent,
const char *Name,
TaskExecutionMode ExecutionMode,
void *Image,
bool DoNotCreatePageTable,
uint16_t UserID,
uint16_t GroupID)
{
SmartLock(TaskingLock);
return new PCB(this, Parent, Name, ExecutionMode,
Image, DoNotCreatePageTable,
UserID, GroupID);
}
Task::Task(const IP EntryPoint) : Interrupts::Handler(16) /* IRQ16 */
{
#if defined(a64)
// Map the IRQ16 to the first CPU.
((APIC::APIC *)Interrupts::apic[0])->RedirectIRQ(0, CPU::x86::IRQ16 - CPU::x86::IRQ0, 1);
#elif defined(a32)
#elif defined(aa64)
#endif
KPrint("Starting Tasking With Instruction Pointer: %p (\e666666%s\eCCCCCC)",
EntryPoint, KernelSymbolTable->GetSymbolFromAddress(EntryPoint));
#if defined(a64)
TaskArchitecture Arch = TaskArchitecture::x64;
#elif defined(a32)
TaskArchitecture Arch = TaskArchitecture::x32;
#elif defined(aa64)
TaskArchitecture Arch = TaskArchitecture::ARM64;
#endif
PCB *kproc = CreateProcess(nullptr, "Kernel", TaskExecutionMode::Kernel);
kproc->ELFSymbolTable = KernelSymbolTable;
TCB *kthrd = CreateThread(kproc, EntryPoint,
nullptr, nullptr,
std::vector<AuxiliaryVector>(), Arch);
kthrd->Rename("Main Thread");
debug("Created Kernel Process: %s and Thread: %s",
kproc->Name, kthrd->Name);
bool MONITORSupported = false;
if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0)
{
CPU::x86::AMD::CPUID0x00000001 cpuid;
cpuid.Get();
MONITORSupported = cpuid.ECX.MONITOR;
}
else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0)
{
CPU::x86::Intel::CPUID0x00000001 cpuid;
cpuid.Get();
MONITORSupported = cpuid.ECX.MONITOR;
}
if (MONITORSupported)
{
trace("CPU has MONITOR/MWAIT support.");
}
if (!CPU::Interrupts(CPU::Check))
{
error("Interrupts are not enabled.");
CPU::Interrupts(CPU::Enable);
}
IdleProcess = CreateProcess(nullptr, (char *)"Idle", TaskExecutionMode::Kernel);
IdleProcess->ELFSymbolTable = KernelSymbolTable;
for (int i = 0; i < SMP::CPUCores; i++)
{
IdleThread = CreateThread(IdleProcess, IP(IdleProcessLoop));
char IdleName[16];
sprintf(IdleName, "Idle Thread %d", i);
IdleThread->Rename(IdleName);
IdleThread->SetPriority(Idle);
for (int j = 0; j < MAX_CPU; j++)
IdleThread->Info.Affinity[j] = false;
IdleThread->Info.Affinity[i] = true;
}
debug("Tasking Started");
#if defined(a86)
if (Interrupts::apicTimer[0])
{
((APIC::Timer *)Interrupts::apicTimer[0])->OneShot(CPU::x86::IRQ16, 100);
/* FIXME: The kernel is not ready for multi-core tasking. */
// for (int i = 1; i < SMP::CPUCores; i++)
// {
// ((APIC::Timer *)Interrupts::apicTimer[i])->OneShot(CPU::x86::IRQ16, 100);
// APIC::InterruptCommandRegisterLow icr;
// icr.Vector = CPU::x86::IRQ16;
// icr.Level = APIC::APICLevel::Assert;
// ((APIC::APIC *)Interrupts::apic[0])->IPI(i, icr);
// }
}
#elif defined(aa64)
#endif
}
Task::~Task()
{
debug("Destructor called");
{
SmartLock(TaskingLock);
foreach (PCB *Process in ProcessList)
{
foreach (TCB *Thread in Process->Threads)
{
if (Thread == GetCurrentCPU()->CurrentThread.load() ||
Thread == CleanupThread)
continue;
this->KillThread(Thread, KILL_SCHEDULER_DESTRUCTION);
}
if (Process == GetCurrentCPU()->CurrentProcess.load())
continue;
this->KillProcess(Process, KILL_SCHEDULER_DESTRUCTION);
}
}
while (ProcessList.size() > 0)
{
trace("Waiting for %d processes to terminate", ProcessList.size());
int NotTerminated = 0;
foreach (PCB *Process in ProcessList)
{
debug("Process %s(%d) is still running (or waiting to be removed status %#lx)",
Process->Name, Process->ID, Process->State);
if (Process->State == TaskState::Terminated)
continue;
NotTerminated++;
}
if (NotTerminated == 1)
break;
TaskingScheduler_OneShot(100);
}
debug("Tasking stopped");
}
}

548
tasking/thread.cpp Normal file
View File

@ -0,0 +1,548 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <task.hpp>
#include <filesystem/ioctl.hpp>
#include <dumper.hpp>
#include <convert.h>
#include <lock.hpp>
#include <printf.h>
#include <smp.hpp>
#include <io.h>
#include "../kernel.h"
#if defined(a64)
#include "../arch/amd64/cpu/apic.hpp"
#include "../arch/amd64/cpu/gdt.hpp"
#elif defined(a32)
#include "../arch/i386/cpu/apic.hpp"
#include "../arch/i386/cpu/gdt.hpp"
#elif defined(aa64)
#endif
// #define DEBUG_TASKING 1
#ifdef DEBUG_TASKING
#define tskdbg(m, ...) \
debug(m, ##__VA_ARGS__); \
__sync
#else
#define tskdbg(m, ...)
#endif
/* For kernel threads only */
void ThreadDoExit()
{
CPUData *CPUData = GetCurrentCPU();
Tasking::TCB *CurrentThread = CPUData->CurrentThread.load();
CurrentThread->State = Tasking::TaskState::Terminated;
debug("\"%s\"(%d) exited with code: %#lx",
CurrentThread->Name,
CurrentThread->ID,
CurrentThread->ExitCode);
CPU::Halt(true);
}
namespace Tasking
{
void TCB::Rename(const char *name)
{
assert(name != nullptr);
assert(strlen(name) > 0);
trace("Renaming thread %s to %s",
this->Name, name);
if (this->Name)
{
this->AllocatedMemory -= strlen(this->Name) + 1;
delete[] this->Name;
}
this->Name = new char[strlen(name) + 1];
this->AllocatedMemory += strlen(name) + 1;
strcpy((char *)this->Name, name);
}
void TCB::SetPriority(TaskPriority priority)
{
assert(priority >= _PriorityMin);
// assert(priority <= _PriorityMax);
trace("Setting priority of thread %s to %d",
this->Name, priority);
Info.Priority = priority;
}
void TCB::SetCritical(bool Critical)
{
trace("Setting criticality of thread %s to %s",
this->Name, Critical ? "true" : "false");
Security.IsCritical = Critical;
}
void TCB::SetDebugMode(bool Enable)
{
trace("Setting debug mode of thread %s to %s",
this->Name, Enable ? "true" : "false");
Security.IsDebugEnabled = Enable;
}
void TCB::SetKernelDebugMode(bool Enable)
{
trace("Setting kernel debug mode of thread %s to %s",
this->Name, Enable ? "true" : "false");
Security.IsKernelDebugEnabled = Enable;
}
size_t TCB::GetSize()
{
size_t ret = this->AllocatedMemory;
ret += this->vma->GetAllocatedMemorySize();
ret += this->Stack->GetSize();
return ret;
}
void TCB::SYSV_ABI_Call(uintptr_t Arg1, uintptr_t Arg2,
uintptr_t Arg3, uintptr_t Arg4,
uintptr_t Arg5, uintptr_t Arg6,
void *Function)
{
#if defined(a64)
this->Registers.rdi = Arg1;
this->Registers.rsi = Arg2;
this->Registers.rdx = Arg3;
this->Registers.rcx = Arg4;
this->Registers.r8 = Arg5;
this->Registers.r9 = Arg6;
if (Function != nullptr)
this->Registers.rip = (uint64_t)Function;
#elif defined(a32)
this->Registers.eax = Arg1;
this->Registers.ebx = Arg2;
this->Registers.ecx = Arg3;
this->Registers.edx = Arg4;
this->Registers.esi = Arg5;
this->Registers.edi = Arg6;
if (Function != nullptr)
this->Registers.eip = (uint32_t)Function;
#else
#warning "SYSV ABI not implemented for this architecture"
#endif
}
__no_sanitize("undefined") void TCB::SetupUserStack_x86_64(const char **argv,
const char **envp,
const std::vector<AuxiliaryVector> &auxv)
{
size_t ArgvSize = 0;
if (argv)
while (argv[ArgvSize] != nullptr)
ArgvSize++;
size_t EnvpSize = 0;
if (envp)
while (envp[EnvpSize] != nullptr)
EnvpSize++;
debug("ArgvSize: %d", ArgvSize);
debug("EnvpSize: %d", EnvpSize);
/* https://articles.manugarg.com/aboutelfauxiliaryvectors.html */
/* https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf#figure.3.9 */
// rsp is the top of the stack
char *Stack = (char *)this->Stack->GetStackPhysicalTop();
// Temporary stack pointer for strings
char *StackStrings = (char *)Stack;
char *StackStringsVirtual = (char *)this->Stack->GetStackTop();
// Store string pointers for later
uintptr_t *ArgvStrings = nullptr;
uintptr_t *EnvpStrings = nullptr;
if (ArgvSize > 0)
ArgvStrings = new uintptr_t[ArgvSize];
if (EnvpSize > 0)
EnvpStrings = new uintptr_t[EnvpSize];
for (size_t i = 0; i < ArgvSize; i++)
{
// Subtract the length of the string and the null terminator
StackStrings -= strlen(argv[i]) + 1;
StackStringsVirtual -= strlen(argv[i]) + 1;
// Store the pointer to the string
ArgvStrings[i] = (uintptr_t)StackStringsVirtual;
// Copy the string to the stack
strcpy(StackStrings, argv[i]);
debug("argv[%d]: %s", i, argv[i]);
}
for (size_t i = 0; i < EnvpSize; i++)
{
// Subtract the length of the string and the null terminator
StackStrings -= strlen(envp[i]) + 1;
StackStringsVirtual -= strlen(envp[i]) + 1;
// Store the pointer to the string
EnvpStrings[i] = (uintptr_t)StackStringsVirtual;
// Copy the string to the stack
strcpy(StackStrings, envp[i]);
debug("envp[%d]: %s", i, envp[i]);
}
// Align the stack to 16 bytes
StackStrings -= (uintptr_t)StackStrings & 0xF;
// Set "Stack" to the new stack pointer
Stack = (char *)StackStrings;
// If argv and envp sizes are odd then we need to align the stack
Stack -= (ArgvSize + EnvpSize) % 2;
// We need 8 bit pointers for the stack from here
uintptr_t *Stack64 = (uintptr_t *)Stack;
// Store the null terminator
Stack64--;
*Stack64 = AT_NULL;
// auxv_array is initialized with auxv elements. If the array is empty then we add a null terminator
std::vector<AuxiliaryVector> auxv_array = auxv;
if (auxv_array.size() == 0)
auxv_array.push_back({.archaux = {.a_type = AT_NULL, .a_un = {.a_val = 0}}});
// Store auxillary vector
foreach (AuxiliaryVector av in auxv_array)
{
// Subtract the size of the auxillary vector
Stack64 -= sizeof(Elf_auxv_t) / sizeof(uintptr_t);
// Store the auxillary vector
POKE(Elf_auxv_t, Stack64) = av.archaux;
// TODO: Store strings to the stack
}
// Store the null terminator
Stack64--;
*Stack64 = AT_NULL;
// Store EnvpStrings[] to the stack
Stack64 -= EnvpSize; // (1 Stack64 = 8 bits; Stack64 = 8 * EnvpSize)
for (size_t i = 0; i < EnvpSize; i++)
{
*(Stack64 + i) = (uintptr_t)EnvpStrings[i];
debug("EnvpStrings[%d]: %#lx",
i, EnvpStrings[i]);
}
// Store the null terminator
Stack64--;
*Stack64 = AT_NULL;
// Store ArgvStrings[] to the stack
Stack64 -= ArgvSize; // (1 Stack64 = 8 bits; Stack64 = 8 * ArgvSize)
for (size_t i = 0; i < ArgvSize; i++)
{
*(Stack64 + i) = (uintptr_t)ArgvStrings[i];
debug("ArgvStrings[%d]: %#lx",
i, ArgvStrings[i]);
}
// Store the argc
Stack64--;
*Stack64 = ArgvSize;
// Set "Stack" to the new stack pointer
Stack = (char *)Stack64;
/* We need the virtual address but because we are in the kernel we can't use the process page table.
So we modify the physical address and store how much we need to subtract to get the virtual address for RSP. */
uintptr_t SubtractStack = (uintptr_t)this->Stack->GetStackPhysicalTop() - (uintptr_t)Stack;
debug("SubtractStack: %#lx", SubtractStack);
// Set the stack pointer to the new stack
uintptr_t StackPointerReg = ((uintptr_t)this->Stack->GetStackTop() - SubtractStack);
#if defined(a64)
this->Registers.rsp = StackPointerReg;
#elif defined(a32)
this->Registers.esp = StackPointerReg;
#elif defined(aa64)
this->Registers.sp = StackPointerReg;
#endif
if (ArgvSize > 0)
delete[] ArgvStrings;
if (EnvpSize > 0)
delete[] EnvpStrings;
#ifdef DEBUG
DumpData("Stack Data", (void *)((uintptr_t)this->Stack->GetStackPhysicalTop() - (uintptr_t)SubtractStack), SubtractStack);
#endif
#if defined(a64)
this->Registers.rdi = (uintptr_t)ArgvSize; // argc
this->Registers.rsi = (uintptr_t)(this->Registers.rsp + 8); // argv
this->Registers.rcx = (uintptr_t)EnvpSize; // envc
this->Registers.rdx = (uintptr_t)(this->Registers.rsp + 8 + (8 * ArgvSize) + 8); // envp
#elif defined(a32)
this->Registers.eax = (uintptr_t)ArgvSize; // argc
this->Registers.ebx = (uintptr_t)(this->Registers.esp + 4); // argv
this->Registers.ecx = (uintptr_t)EnvpSize; // envc
this->Registers.edx = (uintptr_t)(this->Registers.esp + 4 + (4 * ArgvSize) + 4); // envp
#elif defined(aa64)
this->Registers.x0 = (uintptr_t)ArgvSize; // argc
this->Registers.x1 = (uintptr_t)(this->Registers.sp + 8); // argv
this->Registers.x2 = (uintptr_t)EnvpSize; // envc
this->Registers.x3 = (uintptr_t)(this->Registers.sp + 8 + (8 * ArgvSize) + 8); // envp
#endif
}
void TCB::SetupUserStack_x86_32(const char **argv,
const char **envp,
const std::vector<AuxiliaryVector> &auxv)
{
fixme("Not implemented");
}
void TCB::SetupUserStack_aarch64(const char **argv,
const char **envp,
const std::vector<AuxiliaryVector> &auxv)
{
fixme("Not implemented");
}
TCB::TCB(Task *ctx, PCB *Parent, IP EntryPoint,
const char **argv, const char **envp,
const std::vector<AuxiliaryVector> &auxv,
TaskArchitecture Architecture,
TaskCompatibility Compatibility,
bool ThreadNotReady)
{
debug("+ %#lx", this);
assert(ctx != nullptr);
assert(Architecture >= _ArchitectureMin);
assert(Architecture <= _ArchitectureMax);
assert(Compatibility >= _CompatibilityMin);
assert(Compatibility <= _CompatibilityMax);
if (Parent == nullptr)
{
this->Parent = ctx->GetCurrentProcess();
assert(this->Parent != nullptr);
}
else
this->Parent = Parent;
this->ctx = ctx;
this->ID = ctx->NextTID++;
if (this->Name)
delete[] this->Name;
this->Name = new char[strlen(this->Parent->Name) + 1];
strcpy((char *)this->Name, this->Parent->Name);
this->EntryPoint = EntryPoint;
this->ExitCode = KILL_CRASH;
if (ThreadNotReady)
this->State = TaskState::Waiting;
else
this->State = TaskState::Ready;
this->vma = new Memory::VirtualMemoryArea(this->Parent->PageTable);
#if defined(a64)
this->Registers.rip = EntryPoint;
#elif defined(a32)
this->Registers.eip = EntryPoint;
#elif defined(aa64)
this->Registers.pc = EntryPoint;
#endif
switch (this->Parent->Security.ExecutionMode)
{
case TaskExecutionMode::System:
fixme("System mode not supported.");
[[fallthrough]];
case TaskExecutionMode::Kernel:
{
this->Security.IsCritical = true;
this->Stack = new Memory::StackGuard(false, this->vma);
#if defined(a64)
this->ShadowGSBase =
CPU::x64::rdmsr(CPU::x64::MSRID::MSR_SHADOW_GS_BASE);
this->GSBase = CPU::x64::rdmsr(CPU::x64::MSRID::MSR_GS_BASE);
this->FSBase = CPU::x64::rdmsr(CPU::x64::MSRID::MSR_FS_BASE);
this->Registers.cs = GDT_KERNEL_CODE;
this->Registers.ss = GDT_KERNEL_DATA;
this->Registers.rflags.AlwaysOne = 1;
this->Registers.rflags.IF = 1;
this->Registers.rflags.ID = 1;
this->Registers.rsp = ((uintptr_t)this->Stack->GetStackTop());
POKE(uintptr_t, this->Registers.rsp) = (uintptr_t)ThreadDoExit;
#elif defined(a32)
this->Registers.cs = GDT_KERNEL_CODE;
this->Registers.r3_ss = GDT_KERNEL_DATA;
this->Registers.eflags.AlwaysOne = 1;
this->Registers.eflags.IF = 1;
this->Registers.eflags.ID = 1;
this->Registers.esp = ((uintptr_t)this->Stack->GetStackTop());
POKE(uintptr_t, this->Registers.esp) = (uintptr_t)ThreadDoExit;
#elif defined(aa64)
this->Registers.pc = EntryPoint;
this->Registers.sp = ((uintptr_t)this->Stack->GetStackTop());
POKE(uintptr_t, this->Registers.sp) = (uintptr_t)ThreadDoExit;
#endif
break;
}
case TaskExecutionMode::User:
{
this->Stack = new Memory::StackGuard(true, this->vma);
gsTCB *gsT = (gsTCB *)this->vma->RequestPages(TO_PAGES(sizeof(gsTCB)));
gsT->ScPages = TO_PAGES(STACK_SIZE);
gsT->SyscallStackBase = this->vma->RequestPages(gsT->ScPages);
gsT->SyscallStack = (uintptr_t)gsT->SyscallStackBase + STACK_SIZE - 0x10;
debug("New syscall stack created: %#lx (base: %#lx) with gs base at %#lx",
gsT->SyscallStack, gsT->SyscallStackBase, gsT);
gsT->TempStack = 0x0;
gsT->t = this;
#if defined(a64)
this->ShadowGSBase = (uintptr_t)gsT;
this->GSBase = 0;
this->FSBase = 0;
this->Registers.cs = GDT_USER_CODE;
this->Registers.ss = GDT_USER_DATA;
this->Registers.rflags.AlwaysOne = 1;
this->Registers.rflags.IF = 1;
this->Registers.rflags.ID = 1;
/* We need to leave the libc's crt
to make a syscall when the Thread
is exited or we are going to get
an exception. */
this->SetupUserStack_x86_64(argv, envp, auxv);
#elif defined(a32)
this->Registers.cs = GDT_USER_CODE;
this->Registers.r3_ss = GDT_USER_DATA;
this->Registers.eflags.AlwaysOne = 1;
this->Registers.eflags.IF = 1;
this->Registers.eflags.ID = 1;
/* We need to leave the libc's crt
to make a syscall when the Thread
is exited or we are going to get
an exception. */
this->SetupUserStack_x86_32(argv, envp, auxv);
#elif defined(aa64)
this->SetupUserStack_aarch64(argv, envp, auxv);
#endif
#ifdef DEBUG_TASKING
DumpData(this->Name, this->Stack, STACK_SIZE);
#endif
break;
}
default:
assert(false);
}
this->Info.Architecture = Architecture;
this->Info.Compatibility = Compatibility;
this->Security.ExecutionMode =
this->Parent->Security.ExecutionMode;
// TODO: Is really a good idea to use the FPU in kernel mode?
this->FPU.mxcsr = 0b0001111110000000;
this->FPU.mxcsrmask = 0b1111111110111111;
this->FPU.fcw = 0b0000001100111111;
#ifdef DEBUG
#ifdef a64
debug("Thread EntryPoint: %#lx => RIP: %#lx",
this->EntryPoint, this->Registers.rip);
if (this->Parent->Security.ExecutionMode == TaskExecutionMode::User)
debug("Thread stack region is %#lx-%#lx (U) and rsp is %#lx",
this->Stack->GetStackBottom(), this->Stack->GetStackTop(),
this->Registers.rsp);
else
debug("Thread stack region is %#lx-%#lx (K) and rsp is %#lx",
this->Stack->GetStackBottom(), this->Stack->GetStackTop(),
this->Registers.rsp);
#elif defined(a32)
debug("Thread EntryPoint: %#lx => RIP: %#lx",
this->EntryPoint, this->Registers.eip);
if (Parent->Security.ExecutionMode == TaskExecutionMode::User)
debug("Thread stack region is %#lx-%#lx (U) and rsp is %#lx",
this->Stack->GetStackBottom(), this->Stack->GetStackTop(),
this->Registers.esp);
else
debug("Thread stack region is %#lx-%#lx (K) and rsp is %#lx",
this->Stack->GetStackBottom(), this->Stack->GetStackTop(),
this->Registers.esp);
#elif defined(aa64)
#endif
debug("Created %s thread \"%s\"(%d) in process \"%s\"(%d)",
this->Security.ExecutionMode == TaskExecutionMode::User ? "user" : "kernel",
this->Name, this->ID, this->Parent->Name,
this->Parent->ID);
#endif
this->AllocatedMemory += sizeof(TCB);
this->AllocatedMemory += strlen(this->Parent->Name) + 1;
this->AllocatedMemory += sizeof(Memory::StackGuard);
this->Info.SpawnTime = TimeManager->GetCounter();
this->Parent->Threads.push_back(this);
if (this->Parent->Threads.size() == 1 &&
this->Parent->State == Waiting &&
ThreadNotReady == false)
{
this->Parent->State = Ready;
debug("Setting process \"%s\"(%d) to ready",
this->Parent->Name, this->Parent->ID);
}
}
TCB::~TCB()
{
debug("- %#lx", this);
/* Remove us from the process list so we
don't get scheduled anymore */
std::vector<Tasking::TCB *> &Threads = this->Parent->Threads;
Threads.erase(std::find(Threads.begin(),
Threads.end(),
this));
/* Free CPU Stack */
delete this->Stack;
/* Free all allocated memory */
delete this->vma;
/* Free Name */
delete[] this->Name;
}
}