/******************************************************************************* Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. 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 . thread.cpp Describes a thread belonging to a process. *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Sortix { Thread::Thread() { id = 0; // TODO: Make a thread id. process = NULL; prevsibling = NULL; nextsibling = NULL; schedulerlistprev = NULL; schedulerlistnext = NULL; state = NONE; memset(®isters, 0, sizeof(registers)); fsbase = 0; gsbase = 0; kernelstackpos = 0; kernelstacksize = 0; kernelstackmalloced = false; pledged_destruction = false; 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; sigemptyset(&signal_pending); sigemptyset(&signal_mask); memset(&signal_stack, 0, sizeof(signal_stack)); signal_stack.ss_flags = SS_DISABLE; } 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; } extern "C" void BootstrapKernelThread(void* user, ThreadEntry entry) { entry(user); kthread_exit(); } Thread* CreateKernelThread(Process* process, CPU::InterruptRegisters* regs, unsigned long fsbase, unsigned long gsbase) { #if defined(__x86_64__) if ( fsbase >> 48 != 0x0000 && fsbase >> 48 != 0xFFFF ) return errno = EINVAL, (Thread*) NULL; if ( gsbase >> 48 != 0x0000 && gsbase >> 48 != 0xFFFF ) return errno = EINVAL, (Thread*) NULL; #endif assert(process && regs && process->addrspace); Thread* thread = new Thread; if ( !thread ) return NULL; thread->addrspace = process->addrspace; thread->SaveRegisters(regs); thread->fsbase = fsbase; thread->gsbase = gsbase; 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 = 8 * 1024UL; if ( !stacksize ) stacksize = DEFAULT_KERNEL_STACK_SIZE; uint8_t* stack = new uint8_t[stacksize]; if ( !stack ) return NULL; CPU::InterruptRegisters regs; SetupKernelThreadRegs(®s, entry, user, (addr_t) stack, stacksize); Thread* thread = CreateKernelThread(process, ®s, 0, 0); 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, 0, 0); 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; } static int sys_exit_thread(int requested_exit_code, int flags, const struct exit_thread* user_extended) { if ( flags & ~(EXIT_THREAD_ONLY_IF_OTHERS | EXIT_THREAD_UNMAP | EXIT_THREAD_ZERO | EXIT_THREAD_TLS_UNMAP | EXIT_THREAD_PROCESS | EXIT_THREAD_DUMP_CORE) ) return errno = EINVAL, -1; if ( (flags & EXIT_THREAD_ONLY_IF_OTHERS) && (flags & EXIT_THREAD_PROCESS) ) return errno = EINVAL, -1; Thread* thread = CurrentThread(); Process* process = CurrentProcess(); struct exit_thread extended; if ( !user_extended ) memset(&extended, 0, sizeof(extended)); else if ( !CopyFromUser(&extended, user_extended, sizeof(extended)) ) return -1; extended.unmap_size = Page::AlignUp(extended.unmap_size); kthread_mutex_lock(&thread->process->threadlock); bool is_others = false; for ( Thread* iter = thread->process->firstthread; !is_others && iter; iter = iter->nextsibling ) { if ( iter == thread ) continue; if ( iter->pledged_destruction ) continue; if ( iter->terminated ) continue; is_others = true; } if ( !(flags & EXIT_THREAD_ONLY_IF_OTHERS) || is_others ) thread->pledged_destruction = true; bool are_threads_exiting = false; if ( (flags & EXIT_THREAD_PROCESS) || !is_others ) process->threads_exiting = true; else if ( process->threads_exiting ) are_threads_exiting = true; kthread_mutex_unlock(&thread->process->threadlock); // Self-destruct if another thread began exiting the process. if ( are_threads_exiting ) kthread_exit(); if ( (flags & EXIT_THREAD_ONLY_IF_OTHERS) && !is_others ) return errno = ESRCH, -1; if ( flags & EXIT_THREAD_UNMAP && Page::IsAligned((uintptr_t) extended.unmap_from) && extended.unmap_size ) { ScopedLock lock(&process->segment_lock); Memory::UnmapMemory(process, (uintptr_t) extended.unmap_from, extended.unmap_size); Memory::Flush(); // TODO: The segment is not actually removed! } if ( flags & EXIT_THREAD_TLS_UNMAP && Page::IsAligned((uintptr_t) extended.tls_unmap_from) && extended.tls_unmap_size ) { ScopedLock lock(&process->segment_lock); Memory::UnmapMemory(process, (uintptr_t) extended.tls_unmap_from, extended.tls_unmap_size); Memory::Flush(); } if ( flags & EXIT_THREAD_ZERO ) ZeroUser(extended.zero_from, extended.zero_size); if ( !is_others ) { // Validate the requested exit code such that the process can't exit // with an impossible exit status or that it wasn't actually terminated. int the_nature = WNATURE(requested_exit_code); int the_status = WEXITSTATUS(requested_exit_code); int the_signal = WTERMSIG(requested_exit_code); if ( the_nature == WNATURE_EXITED ) the_signal = 0; else if ( the_nature == WNATURE_SIGNALED ) { if ( the_signal == 0 /* null signal */ || the_signal == SIGSTOP || the_signal == SIGTSTP || the_signal == SIGTTIN || the_signal == SIGTTOU || the_signal == SIGCONT ) the_signal = SIGKILL; the_status = 128 + the_signal; } else { the_nature = WNATURE_SIGNALED; the_signal = SIGKILL; } requested_exit_code = WCONSTRUCT(the_nature, the_status, the_signal); thread->process->ExitWithCode(requested_exit_code); } kthread_exit(); } void Thread::Init() { Syscall::Register(SYSCALL_EXIT_THREAD, (void*) sys_exit_thread); } } // namespace Sortix