Also got rid of trailing white space. That corrupted .git/. Big ass-commit because of recovered .git directory.
288 lines
6.9 KiB
C++
288 lines
6.9 KiB
C++
/******************************************************************************
|
|
|
|
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
|
|
|
This file is part of Sortix.
|
|
|
|
Sortix 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.
|
|
|
|
Sortix 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 Sortix. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
thread.cpp
|
|
Describes a thread belonging to a process.
|
|
|
|
******************************************************************************/
|
|
|
|
#include <sortix/kernel/platform.h>
|
|
#include <libmaxsi/error.h>
|
|
#include <libmaxsi/memory.h>
|
|
#include "event.h"
|
|
#include "process.h"
|
|
#include "thread.h"
|
|
#include "scheduler.h"
|
|
#include <sortix/kernel/memorymanagement.h>
|
|
#include "time.h"
|
|
#include "syscall.h"
|
|
|
|
using namespace Maxsi;
|
|
|
|
namespace Sortix
|
|
{
|
|
Thread::Thread()
|
|
{
|
|
id = 0; // TODO: Make a thread id.
|
|
process = NULL;
|
|
prevsibling = NULL;
|
|
nextsibling = NULL;
|
|
event = NULL;
|
|
eventnextwaiting = NULL;
|
|
sleepuntil = 0;
|
|
nextsleepingthread = NULL;
|
|
schedulerlistprev = NULL;
|
|
schedulerlistnext = NULL;
|
|
state = NONE;
|
|
Maxsi::Memory::Set(®isters, 0, sizeof(registers));
|
|
ready = false;
|
|
scfunc = NULL;
|
|
currentsignal = NULL;
|
|
sighandler = NULL;
|
|
pidbackup = -1;
|
|
addrspacebackup = 0UL;
|
|
terminated = false;
|
|
ResetCallbacks();
|
|
}
|
|
|
|
Thread::Thread(const Thread* forkfrom)
|
|
{
|
|
id = forkfrom->id;
|
|
process = NULL;
|
|
prevsibling = NULL;
|
|
nextsibling = NULL;
|
|
state = forkfrom->state;
|
|
event = NULL;
|
|
eventnextwaiting = NULL;
|
|
sleepuntil = forkfrom->sleepuntil;
|
|
Maxsi::Memory::Copy(®isters, &forkfrom->registers, sizeof(registers));
|
|
ready = false;
|
|
stackpos = forkfrom->stackpos;
|
|
stacksize = forkfrom->stacksize;
|
|
nextsleepingthread = NULL;
|
|
schedulerlistprev = NULL;
|
|
schedulerlistnext = NULL;
|
|
scfunc = NULL;
|
|
sighandler = forkfrom->sighandler;
|
|
pidbackup = -1;
|
|
addrspacebackup = 0UL;
|
|
terminated = false;
|
|
ResetCallbacks();
|
|
}
|
|
|
|
void Thread::ResetCallbacks()
|
|
{
|
|
onchildprocessexit = NULL;
|
|
}
|
|
|
|
Thread::~Thread()
|
|
{
|
|
ASSERT(CurrentProcess() == process);
|
|
ASSERT(nextsleepingthread == NULL);
|
|
|
|
if ( event ) { event->Unregister(this); }
|
|
|
|
// Delete information about signals being processed.
|
|
while ( currentsignal )
|
|
{
|
|
Signal* todelete = currentsignal;
|
|
currentsignal = currentsignal->nextsignal;
|
|
delete todelete;
|
|
}
|
|
|
|
Memory::UnmapRangeUser(stackpos, stacksize);
|
|
|
|
terminated = true;
|
|
}
|
|
|
|
Thread* Thread::Fork()
|
|
{
|
|
ASSERT(ready);
|
|
|
|
Signal* clonesignal = NULL;
|
|
if ( currentsignal )
|
|
{
|
|
clonesignal = currentsignal->Fork();
|
|
if ( !clonesignal ) { return NULL; }
|
|
}
|
|
|
|
Thread* clone = new Thread(this);
|
|
if ( !clone )
|
|
{
|
|
while ( clonesignal )
|
|
{
|
|
Signal* todelete = clonesignal;
|
|
clonesignal = clonesignal->nextsignal;
|
|
delete todelete;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
clone->currentsignal = clonesignal;
|
|
|
|
return clone;
|
|
}
|
|
|
|
void CreateThreadCPU(Thread* thread, addr_t entry);
|
|
|
|
Thread* CreateThread(addr_t entry, size_t stacksize)
|
|
{
|
|
Process* process = CurrentProcess();
|
|
|
|
if ( stacksize == 0 ) { stacksize = process->DefaultStackSize(); }
|
|
|
|
// TODO: Find some unused virtual address space of the needed size
|
|
// somewhere in the current process.
|
|
addr_t stackpos = process->AllocVirtualAddr(stacksize);
|
|
if ( !stackpos ) { return NULL; }
|
|
|
|
if ( !Memory::MapRangeUser(stackpos, stacksize) )
|
|
{
|
|
// TODO: Free the reserved virtual memory area.
|
|
return NULL;
|
|
}
|
|
|
|
Thread* thread = new Thread();
|
|
if ( !thread )
|
|
{
|
|
Memory::UnmapRangeUser(stackpos, stacksize);
|
|
// TODO: Free the reserved virtual memory area.
|
|
return NULL;
|
|
}
|
|
|
|
thread->stackpos = stackpos;
|
|
thread->stacksize = stacksize;
|
|
|
|
// Set up the thread state registers.
|
|
CreateThreadCPU(thread, entry);
|
|
|
|
// Create the family tree.
|
|
thread->process = process;
|
|
Thread* firsty = process->firstthread;
|
|
if ( firsty ) { firsty->prevsibling = thread; }
|
|
thread->nextsibling = firsty;
|
|
process->firstthread = thread;
|
|
|
|
thread->Ready();
|
|
|
|
Scheduler::SetThreadState(thread, Thread::State::RUNNABLE);
|
|
|
|
return thread;
|
|
}
|
|
|
|
void Thread::Ready()
|
|
{
|
|
if ( ready ) { return; }
|
|
ready = true;
|
|
|
|
this->pidbackup = process->pid;
|
|
this->addrspacebackup = process->addrspace;
|
|
|
|
if ( Time::MicrosecondsSinceBoot() < sleepuntil )
|
|
{
|
|
uintmax_t howlong = sleepuntil - Time::MicrosecondsSinceBoot();
|
|
Scheduler::PutThreadToSleep(this, howlong);
|
|
}
|
|
else if ( state == State::RUNNABLE )
|
|
{
|
|
state = State::NONE; // Since we are in no linked list.
|
|
Scheduler::SetThreadState(this, State::RUNNABLE);
|
|
}
|
|
}
|
|
|
|
void Thread::HandleSignal(CPU::InterruptRegisters* regs)
|
|
{
|
|
Signal* override = signalqueue.Pop(currentsignal);
|
|
if ( !override ) { return; }
|
|
if ( !sighandler ) { delete override; return; }
|
|
|
|
if ( override->signum == SIGKILL )
|
|
{
|
|
|
|
}
|
|
|
|
|
|
override->nextsignal = currentsignal;
|
|
Maxsi::Memory::Copy(&override->regs, regs, sizeof(override->regs));
|
|
currentsignal = override;
|
|
|
|
HandleSignalCPU(regs);
|
|
}
|
|
|
|
void SysSigReturn()
|
|
{
|
|
Thread* thread = CurrentThread();
|
|
if ( !thread->currentsignal ) { return; }
|
|
|
|
CPU::InterruptRegisters* dest = Syscall::InterruptRegs();
|
|
CPU::InterruptRegisters* src = &thread->currentsignal->regs;
|
|
|
|
Maxsi::Memory::Copy(dest, src, sizeof(CPU::InterruptRegisters));
|
|
thread->currentsignal = thread->currentsignal->nextsignal;
|
|
Syscall::AsIs();
|
|
}
|
|
|
|
void SysRegisterSignalHandler(sighandler_t sighandler)
|
|
{
|
|
CurrentThread()->sighandler = sighandler;
|
|
}
|
|
|
|
int SysKill(pid_t pid, int signum)
|
|
{
|
|
if ( signum < 0 || 128 <= signum ) { Error::Set(EINVAL); return -1; }
|
|
|
|
// Protect the system idle process.
|
|
if ( pid == 0 ) { Error::Set(EPERM); return -1; }
|
|
|
|
Process* process = Process::Get(pid);
|
|
if ( !process ) { Error::Set(ESRCH); return -1; }
|
|
|
|
// TODO: Protect init?
|
|
// TODO: Check for permission.
|
|
// TODO: Check for zombies.
|
|
|
|
Thread* currentthread = CurrentThread();
|
|
Thread* thread = NULL;
|
|
if ( currentthread->process->pid == pid ) { thread = currentthread; }
|
|
if ( !thread ) { thread = process->firstthread; }
|
|
if ( !thread ) { Error::Set(ESRCH); return -1; /* TODO: Zombie? */ }
|
|
|
|
// TODO: If thread is not runnable, wake it and runs its handler?
|
|
if ( !thread->signalqueue.Push(signum) )
|
|
{
|
|
// TODO: Possibly kill the process?
|
|
}
|
|
|
|
if ( thread == currentthread )
|
|
{
|
|
Syscall::SyscallRegs()->result = 0;
|
|
thread->HandleSignal(Syscall::InterruptRegs());
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void Thread::Init()
|
|
{
|
|
Syscall::Register(SYSCALL_KILL, (void*) SysKill);
|
|
Syscall::Register(SYSCALL_REGISTER_SIGNAL_HANDLER, (void*) SysRegisterSignalHandler);
|
|
Syscall::Register(SYSCALL_SIGRETURN, (void*) SysSigReturn);
|
|
}
|
|
}
|