Update sortix/thread.cpp to current coding conventions.

This commit is contained in:
Jonas 'Sortie' Termansen 2013-07-27 12:56:40 +02:00
parent d5a7901b25
commit da94e0fb62
1 changed files with 290 additions and 279 deletions

View File

@ -22,288 +22,299 @@
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/memorymanagement.h>
#include <sortix/kernel/syscall.h>
#include <sortix/kernel/interrupt.h>
#include <sortix/kernel/time.h>
#include <sortix/kernel/scheduler.h>
#include <sortix/kernel/process.h>
#include <sortix/kernel/thread.h>
#include <sortix/mman.h>
#include <sortix/signal.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
namespace Sortix
#include <sortix/mman.h>
#include <sortix/signal.h>
#include <sortix/kernel/platform.h>
#include <sortix/kernel/interrupt.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/memorymanagement.h>
#include <sortix/kernel/process.h>
#include <sortix/kernel/scheduler.h>
#include <sortix/kernel/syscall.h>
#include <sortix/kernel/thread.h>
#include <sortix/kernel/time.h>
namespace Sortix {
Thread::Thread()
{
Thread::Thread()
{
id = 0; // TODO: Make a thread id.
process = NULL;
prevsibling = NULL;
nextsibling = NULL;
schedulerlistprev = NULL;
schedulerlistnext = NULL;
state = NONE;
memset(&registers, 0, sizeof(registers));
stackpos = 0;
stacksize = 0;
kernelstackpos = 0;
kernelstacksize = 0;
kernelstackmalloced = false;
currentsignal = 0;
siglevel = 0;
sighandler = NULL;
terminated = false;
fpuinitialized = false;
// If malloc isn't 16-byte aligned, then we can't rely on offsets in
// our own class, so we'll just fix ourselves nicely up.
unsigned long fpuaddr = ((unsigned long) fpuenv+16UL) & ~(16UL-1UL);
fpuenvaligned = (uint8_t*) fpuaddr;
}
Thread::~Thread()
{
if ( process )
process->OnThreadDestruction(this);
assert(CurrentThread() != this);
if ( kernelstackmalloced )
delete[] (uint8_t*) kernelstackpos;
terminated = true;
}
addr_t Thread::SwitchAddressSpace(addr_t newaddrspace)
{
bool wasenabled = Interrupt::SetEnabled(false);
addr_t result = addrspace;
addrspace = newaddrspace;
Memory::SwitchAddressSpace(newaddrspace);
Interrupt::SetEnabled(wasenabled);
return result;
}
// Last chance to clean up user-space things before this thread dies.
void Thread::LastPrayer()
{
Memory::UnmapRange(stackpos, stacksize);
Memory::Flush();
}
extern "C" void BootstrapKernelThread(void* user, ThreadEntry entry)
{
entry(user);
kthread_exit();
}
Thread* CreateKernelThread(Process* process, CPU::InterruptRegisters* regs)
{
assert(process && regs && process->addrspace);
Thread* thread = new Thread;
if ( !thread ) { return NULL; }
thread->addrspace = process->addrspace;
thread->SaveRegisters(regs);
kthread_mutex_lock(&process->threadlock);
// Create the family tree.
thread->process = process;
Thread* firsty = process->firstthread;
if ( firsty ) { firsty->prevsibling = thread; }
thread->nextsibling = firsty;
process->firstthread = thread;
kthread_mutex_unlock(&process->threadlock);
return thread;
}
Thread* CreateKernelThread(Process* process, ThreadEntry entry, void* user,
size_t stacksize)
{
const size_t DEFAULT_KERNEL_STACK_SIZE = 64*8192UL;
if ( !stacksize ) { stacksize = DEFAULT_KERNEL_STACK_SIZE; }
uint8_t* stack = new uint8_t[stacksize];
if ( !stack ) { return NULL; }
CPU::InterruptRegisters regs;
SetupKernelThreadRegs(&regs, entry, user, (addr_t) stack, stacksize);
Thread* thread = CreateKernelThread(process, &regs);
if ( !thread ) { delete[] stack; }
thread->kernelstackpos = (addr_t) stack;
thread->kernelstacksize = stacksize;
thread->kernelstackmalloced = true;
return thread;
}
Thread* CreateKernelThread(ThreadEntry entry, void* user, size_t stacksize)
{
return CreateKernelThread(CurrentProcess(), entry, user, stacksize);
}
void StartKernelThread(Thread* thread)
{
Scheduler::SetThreadState(thread, ThreadState::RUNNABLE);
}
Thread* RunKernelThread(Process* process, CPU::InterruptRegisters* regs)
{
Thread* thread = CreateKernelThread(process, regs);
if ( !thread ) { return NULL; }
StartKernelThread(thread);
return thread;
}
Thread* RunKernelThread(Process* process, ThreadEntry entry, void* user,
size_t stacksize)
{
Thread* thread = CreateKernelThread(process, entry, user, stacksize);
if ( !thread ) { return NULL; }
StartKernelThread(thread);
return thread;
}
Thread* RunKernelThread(ThreadEntry entry, void* user, size_t stacksize)
{
Thread* thread = CreateKernelThread(entry, user, stacksize);
if ( !thread ) { return NULL; }
StartKernelThread(thread);
return thread;
}
void Thread::HandleSignal(CPU::InterruptRegisters* regs)
{
int signum = signalqueue.Pop(currentsignal);
regs->signal_pending = 0;
if ( !signum )
return;
if ( !sighandler )
return;
if ( SIG_NUM_LEVELS <= siglevel )
return;
// Signals can't return to kernel mode because the kernel stack may have
// been overwritten by a system call during the signal handler. Correct
// the return state so it returns to userspace and not the kernel.
if ( !regs->InUserspace() )
HandleSignalFixupRegsCPU(regs);
if ( signum == SIGKILL )
{
// We need to run the OnSigKill method here with interrupts enabled
// and on our own stack. But this method this may have been called
// from the scheduler on any stack, so we need to do a little
// bootstrap and switch to our own stack.
GotoOnSigKill(regs);
return;
}
int level = siglevel++;
signums[level] = currentsignal = signum;
memcpy(sigregs + level, regs, sizeof(*regs));
HandleSignalCPU(regs);
}
void Thread::HandleSigreturn(CPU::InterruptRegisters* regs)
{
if ( !siglevel )
return;
siglevel--;
currentsignal = siglevel ? signums[siglevel-1] : 0;
memcpy(regs, sigregs + siglevel, sizeof(*regs));
regs->signal_pending = 0;
// Check if a more important signal is pending.
HandleSignal(regs);
}
extern "C" void Thread__OnSigKill(Thread* thread)
{
thread->OnSigKill();
}
void Thread::OnSigKill()
{
LastPrayer();
kthread_exit();
}
int SysRegisterSignalHandler(sighandler_t sighandler)
{
CurrentThread()->sighandler = sighandler;
return 0;
}
void Thread::SetHavePendingSignals()
{
// TODO: This doesn't really work if Interrupt::IsCPUInterrupted()!
if ( CurrentThread() == this )
asm_signal_is_pending = 1;
else
registers.signal_pending = 1;
}
bool Thread::DeliverSignal(int signum)
{
if ( signum <= 0 || 128 <= signum ) { errno = EINVAL; return false; }
bool wasenabled = Interrupt::SetEnabled(false);
signalqueue.Push(signum);
SetHavePendingSignals();
Interrupt::SetEnabled(wasenabled);
return true;
}
int SysKill(pid_t pid, int signum)
{
// Protect the system idle process.
if ( !pid ) { errno = EPERM; return -1; }
// TODO: Implement that pid == -1 means all processes!
bool process_group = pid < 0 ? (pid = -pid, true) : false;
// If we kill our own process, deliver the signal to this thread.
Thread* currentthread = CurrentThread();
if ( currentthread->process->pid == pid )
return currentthread->DeliverSignal(signum) ? 0 : -1;
// TODO: Race condition: The process could be deleted while we use it.
Process* process = Process::Get(pid);
if ( !process ) { errno = ESRCH; return -1; }
// TODO: Protect init?
// TODO: Check for permission.
// TODO: Check for zombies.
return process_group ?
process->DeliverGroupSignal(signum) ? 0 : -1 :
process->DeliverSignal(signum) ? 0 : -1;
}
int SysRaise(int signum)
{
return CurrentThread()->DeliverSignal(signum) ? 0 : -1;
}
void Thread::Init()
{
Syscall::Register(SYSCALL_KILL, (void*) SysKill);
Syscall::Register(SYSCALL_RAISE, (void*) SysRaise);
Syscall::Register(SYSCALL_REGISTER_SIGNAL_HANDLER, (void*) SysRegisterSignalHandler);
}
id = 0; // TODO: Make a thread id.
process = NULL;
prevsibling = NULL;
nextsibling = NULL;
schedulerlistprev = NULL;
schedulerlistnext = NULL;
state = NONE;
memset(&registers, 0, sizeof(registers));
stackpos = 0;
stacksize = 0;
kernelstackpos = 0;
kernelstacksize = 0;
kernelstackmalloced = false;
currentsignal = 0;
siglevel = 0;
sighandler = NULL;
terminated = false;
fpuinitialized = false;
// If malloc isn't 16-byte aligned, then we can't rely on offsets in
// our own class, so we'll just fix ourselves nicely up.
unsigned long fpuaddr = ((unsigned long) fpuenv+16UL) & ~(16UL-1UL);
fpuenvaligned = (uint8_t*) fpuaddr;
}
Thread::~Thread()
{
if ( process )
process->OnThreadDestruction(this);
assert(CurrentThread() != this);
if ( kernelstackmalloced )
delete[] (uint8_t*) kernelstackpos;
terminated = true;
}
addr_t Thread::SwitchAddressSpace(addr_t newaddrspace)
{
bool wasenabled = Interrupt::SetEnabled(false);
addr_t result = addrspace;
addrspace = newaddrspace;
Memory::SwitchAddressSpace(newaddrspace);
Interrupt::SetEnabled(wasenabled);
return result;
}
// Last chance to clean up user-space things before this thread dies.
void Thread::LastPrayer()
{
Memory::UnmapRange(stackpos, stacksize);
Memory::Flush();
}
extern "C" void BootstrapKernelThread(void* user, ThreadEntry entry)
{
entry(user);
kthread_exit();
}
Thread* CreateKernelThread(Process* process, CPU::InterruptRegisters* regs)
{
assert(process && regs && process->addrspace);
Thread* thread = new Thread;
if ( !thread )
return NULL;
thread->addrspace = process->addrspace;
thread->SaveRegisters(regs);
kthread_mutex_lock(&process->threadlock);
// Create the family tree.
thread->process = process;
Thread* firsty = process->firstthread;
if ( firsty )
firsty->prevsibling = thread;
thread->nextsibling = firsty;
process->firstthread = thread;
kthread_mutex_unlock(&process->threadlock);
return thread;
}
Thread* CreateKernelThread(Process* process, ThreadEntry entry, void* user,
size_t stacksize)
{
const size_t DEFAULT_KERNEL_STACK_SIZE = 512 * 1024UL;
if ( !stacksize )
stacksize = DEFAULT_KERNEL_STACK_SIZE;
uint8_t* stack = new uint8_t[stacksize];
if ( !stack )
return NULL;
CPU::InterruptRegisters regs;
SetupKernelThreadRegs(&regs, entry, user, (addr_t) stack, stacksize);
Thread* thread = CreateKernelThread(process, &regs);
if ( !thread ) { delete[] stack; return NULL; }
thread->kernelstackpos = (addr_t) stack;
thread->kernelstacksize = stacksize;
thread->kernelstackmalloced = true;
return thread;
}
Thread* CreateKernelThread(ThreadEntry entry, void* user, size_t stacksize)
{
return CreateKernelThread(CurrentProcess(), entry, user, stacksize);
}
void StartKernelThread(Thread* thread)
{
Scheduler::SetThreadState(thread, ThreadState::RUNNABLE);
}
Thread* RunKernelThread(Process* process, CPU::InterruptRegisters* regs)
{
Thread* thread = CreateKernelThread(process, regs);
if ( !thread )
return NULL;
StartKernelThread(thread);
return thread;
}
Thread* RunKernelThread(Process* process, ThreadEntry entry, void* user,
size_t stacksize)
{
Thread* thread = CreateKernelThread(process, entry, user, stacksize);
if ( !thread )
return NULL;
StartKernelThread(thread);
return thread;
}
Thread* RunKernelThread(ThreadEntry entry, void* user, size_t stacksize)
{
Thread* thread = CreateKernelThread(entry, user, stacksize);
if ( !thread )
return NULL;
StartKernelThread(thread);
return thread;
}
void Thread::HandleSignal(CPU::InterruptRegisters* regs)
{
int signum = signalqueue.Pop(currentsignal);
regs->signal_pending = 0;
if ( !signum )
return;
if ( !sighandler )
return;
if ( SIG_NUM_LEVELS <= siglevel )
return;
// Signals can't return to kernel mode because the kernel stack may have
// been overwritten by a system call during the signal handler. Correct
// the return state so it returns to userspace and not the kernel.
if ( !regs->InUserspace() )
HandleSignalFixupRegsCPU(regs);
if ( signum == SIGKILL )
{
// We need to run the OnSigKill method here with interrupts enabled
// and on our own stack. But this method this may have been called
// from the scheduler on any stack, so we need to do a little
// bootstrap and switch to our own stack.
GotoOnSigKill(regs);
return;
}
int level = siglevel++;
signums[level] = currentsignal = signum;
memcpy(sigregs + level, regs, sizeof(*regs));
HandleSignalCPU(regs);
}
void Thread::HandleSigreturn(CPU::InterruptRegisters* regs)
{
if ( !siglevel )
return;
siglevel--;
currentsignal = siglevel ? signums[siglevel-1] : 0;
memcpy(regs, sigregs + siglevel, sizeof(*regs));
regs->signal_pending = 0;
// Check if a more important signal is pending.
HandleSignal(regs);
}
extern "C" void Thread__OnSigKill(Thread* thread)
{
thread->OnSigKill();
}
void Thread::OnSigKill()
{
LastPrayer();
kthread_exit();
}
void Thread::SetHavePendingSignals()
{
// TODO: This doesn't really work if Interrupt::IsCPUInterrupted()!
if ( CurrentThread() == this )
asm_signal_is_pending = 1;
else
registers.signal_pending = 1;
}
bool Thread::DeliverSignal(int signum)
{
if ( signum <= 0 || 128 <= signum )
return errno = EINVAL, false;
bool wasenabled = Interrupt::SetEnabled(false);
signalqueue.Push(signum);
SetHavePendingSignals();
Interrupt::SetEnabled(wasenabled);
return true;
}
static int sys_kill(pid_t pid, int signum)
{
// Protect the system idle process.
if ( !pid )
return errno = EPERM, -1;
// TODO: Implement that pid == -1 means all processes!
bool process_group = pid < 0 ? (pid = -pid, true) : false;
// If we kill our own process, deliver the signal to this thread.
Thread* currentthread = CurrentThread();
if ( currentthread->process->pid == pid )
return currentthread->DeliverSignal(signum) ? 0 : -1;
// TODO: Race condition: The process could be deleted while we use it.
Process* process = Process::Get(pid);
if ( !process )
return errno = ESRCH, -1;
// TODO: Protect init?
// TODO: Check for permission.
// TODO: Check for zombies.
return process_group ?
process->DeliverGroupSignal(signum) ? 0 : -1 :
process->DeliverSignal(signum) ? 0 : -1;
}
static int sys_raise(int signum)
{
return CurrentThread()->DeliverSignal(signum) ? 0 : -1;
}
static int sys_register_signal_handler(sighandler_t sighandler)
{
CurrentThread()->sighandler = sighandler;
return 0;
}
void Thread::Init()
{
Syscall::Register(SYSCALL_KILL, (void*) sys_kill);
Syscall::Register(SYSCALL_RAISE, (void*) sys_raise);
Syscall::Register(SYSCALL_REGISTER_SIGNAL_HANDLER, (void*) sys_register_signal_handler);
}
} // namespace Sortix