Kernel/LibC: Add basic support for pthread_{create,exit}
This commit is contained in:
		
							parent
							
								
									788f5429e1
								
							
						
					
					
						commit
						c1618e2b5d
					
				|  | @ -182,6 +182,9 @@ namespace Kernel | |||
| 		BAN::ErrorOr<long> sys_sigpending(sigset_t* set); | ||||
| 		BAN::ErrorOr<long> sys_sigprocmask(int how, const sigset_t* set, sigset_t* oset); | ||||
| 
 | ||||
| 		BAN::ErrorOr<long> sys_pthread_create(const pthread_attr_t* __restrict attr, void (*entry)(void*), void* arg); | ||||
| 		BAN::ErrorOr<long> sys_pthread_exit(void* value); | ||||
| 
 | ||||
| 		BAN::ErrorOr<long> sys_tcgetpgrp(int fd); | ||||
| 		BAN::ErrorOr<long> sys_tcsetpgrp(int fd, pid_t pgid); | ||||
| 
 | ||||
|  | @ -290,6 +293,13 @@ namespace Kernel | |||
| 
 | ||||
| 		BAN::Vector<Thread*> m_threads; | ||||
| 
 | ||||
| 		struct pthread_info_t | ||||
| 		{ | ||||
| 			Thread* thread; | ||||
| 			void* value; | ||||
| 		}; | ||||
| 		BAN::Vector<pthread_info_t> m_exited_pthreads; | ||||
| 
 | ||||
| 		uint64_t m_alarm_interval_ns { 0 }; | ||||
| 		uint64_t m_alarm_wake_time_ns { 0 }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -38,6 +38,8 @@ namespace Kernel | |||
| 		static BAN::ErrorOr<Thread*> create_userspace(Process*, PageTable&); | ||||
| 		~Thread(); | ||||
| 
 | ||||
| 		BAN::ErrorOr<Thread*> pthread_create(entry_t, void*); | ||||
| 
 | ||||
| 		BAN::ErrorOr<Thread*> clone(Process*, uintptr_t sp, uintptr_t ip); | ||||
| 		void setup_exec(); | ||||
| 		void setup_process_cleanup(); | ||||
|  |  | |||
|  | @ -185,6 +185,8 @@ namespace Kernel | |||
| 		process->m_userspace_info.envp = nullptr; | ||||
| 
 | ||||
| 		auto* thread = MUST(Thread::create_userspace(process, process->page_table())); | ||||
| 		thread->setup_exec(); | ||||
| 
 | ||||
| 		process->add_thread(thread); | ||||
| 		process->register_to_scheduler(); | ||||
| 		return process; | ||||
|  | @ -208,6 +210,7 @@ namespace Kernel | |||
| 	Process::~Process() | ||||
| 	{ | ||||
| 		ASSERT(m_threads.empty()); | ||||
| 		ASSERT(m_exited_pthreads.empty()); | ||||
| 		ASSERT(m_mapped_regions.empty()); | ||||
| 		ASSERT(!m_page_table); | ||||
| 	} | ||||
|  | @ -238,6 +241,8 @@ namespace Kernel | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		m_exited_pthreads.clear(); | ||||
| 
 | ||||
| 		ProcFileSystem::get().on_process_delete(*this); | ||||
| 
 | ||||
| 		m_process_lock.lock(); | ||||
|  | @ -2079,6 +2084,43 @@ namespace Kernel | |||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<long> Process::sys_pthread_create(const pthread_attr_t* __restrict attr, void (*entry)(void*), void* arg) | ||||
| 	{ | ||||
| 		if (attr != nullptr) | ||||
| 		{ | ||||
| 			dwarnln("pthread attr not supported"); | ||||
| 			return BAN::Error::from_errno(ENOTSUP); | ||||
| 		} | ||||
| 
 | ||||
| 		LockGuard _(m_process_lock); | ||||
| 
 | ||||
| 		auto* new_thread = TRY(Thread::current().pthread_create(entry, arg)); | ||||
| 		MUST(m_threads.push_back(new_thread)); | ||||
| 		MUST(Processor::scheduler().add_thread(new_thread)); | ||||
| 
 | ||||
| 		return new_thread->tid(); | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<long> Process::sys_pthread_exit(void* value) | ||||
| 	{ | ||||
| 		LockGuard _(m_process_lock); | ||||
| 
 | ||||
| 		// main thread cannot call pthread_exit
 | ||||
| 		if (&Thread::current() == m_threads.front()) | ||||
| 			return BAN::Error::from_errno(EINVAL); | ||||
| 
 | ||||
| 		TRY(m_exited_pthreads.emplace_back(&Thread::current(), value)); | ||||
| 		for (auto* thread : m_threads) | ||||
| 		{ | ||||
| 			if (thread != &Thread::current()) | ||||
| 				continue; | ||||
| 			m_process_lock.unlock(); | ||||
| 			thread->on_exit(); | ||||
| 		} | ||||
| 
 | ||||
| 		ASSERT_NOT_REACHED(); | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<long> Process::sys_tcgetpgrp(int fd) | ||||
| 	{ | ||||
| 		LockGuard _(m_process_lock); | ||||
|  |  | |||
|  | @ -117,8 +117,6 @@ namespace Kernel | |||
| 			true | ||||
| 		)); | ||||
| 
 | ||||
| 		thread->setup_exec(); | ||||
| 
 | ||||
| 		thread_deleter.disable(); | ||||
| 
 | ||||
| 		return thread; | ||||
|  | @ -166,6 +164,21 @@ namespace Kernel | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<Thread*> Thread::pthread_create(entry_t entry, void* arg) | ||||
| 	{ | ||||
| 		auto* thread = TRY(create_userspace(m_process, m_process->page_table())); | ||||
| 
 | ||||
| 		memcpy(thread->m_sse_storage, m_sse_storage, sizeof(m_sse_storage)); | ||||
| 
 | ||||
| 		thread->setup_exec_impl( | ||||
| 			reinterpret_cast<uintptr_t>(entry), | ||||
| 			reinterpret_cast<uintptr_t>(arg), | ||||
| 			0, 0, 0 | ||||
| 		); | ||||
| 
 | ||||
| 		return thread; | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<Thread*> Thread::clone(Process* new_process, uintptr_t sp, uintptr_t ip) | ||||
| 	{ | ||||
| 		ASSERT(m_is_userspace); | ||||
|  | @ -189,6 +202,8 @@ namespace Kernel | |||
| 		thread->m_interrupt_stack.sp = sp; | ||||
| 		thread->m_interrupt_stack.ss = 0x10; | ||||
| 
 | ||||
| 		memcpy(thread->m_sse_storage, m_sse_storage, sizeof(m_sse_storage)); | ||||
| 
 | ||||
| #if ARCH(x86_64) | ||||
| 		thread->m_interrupt_registers.rax = 0; | ||||
| #elif ARCH(i686) | ||||
|  | @ -201,23 +216,34 @@ namespace Kernel | |||
| 	} | ||||
| 
 | ||||
| 	void Thread::setup_exec() | ||||
| 	{ | ||||
| 		const auto& userspace_info = process().userspace_info(); | ||||
| 		ASSERT(userspace_info.entry); | ||||
| 
 | ||||
| 		setup_exec_impl( | ||||
| 			userspace_info.entry, | ||||
| 			userspace_info.argc, | ||||
| 			reinterpret_cast<uintptr_t>(userspace_info.argv), | ||||
| 			reinterpret_cast<uintptr_t>(userspace_info.envp), | ||||
| 			userspace_info.file_fd | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	void Thread::setup_exec_impl(uintptr_t entry, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3) | ||||
| 	{ | ||||
| 		ASSERT(is_userspace()); | ||||
| 		m_state = State::NotStarted; | ||||
| 
 | ||||
| 		// Signal mask is inherited
 | ||||
| 
 | ||||
| 		const auto& userspace_info = process().userspace_info(); | ||||
| 		ASSERT(userspace_info.entry); | ||||
| 
 | ||||
| 		// Initialize stack for returning
 | ||||
| 		PageTable::with_fast_page(process().page_table().physical_address_of(kernel_stack_top() - PAGE_SIZE), [&] { | ||||
| 			uintptr_t sp = PageTable::fast_page() + PAGE_SIZE; | ||||
| 			write_to_stack(sp, userspace_info.entry); | ||||
| 			write_to_stack(sp, userspace_info.file_fd); | ||||
| 			write_to_stack(sp, userspace_info.envp); | ||||
| 			write_to_stack(sp, userspace_info.argv); | ||||
| 			write_to_stack(sp, userspace_info.argc); | ||||
| 			write_to_stack(sp, entry); | ||||
| 			write_to_stack(sp, arg3); | ||||
| 			write_to_stack(sp, arg2); | ||||
| 			write_to_stack(sp, arg1); | ||||
| 			write_to_stack(sp, arg0); | ||||
| 		}); | ||||
| 
 | ||||
| 		m_interrupt_stack.ip = reinterpret_cast<vaddr_t>(start_userspace_thread); | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ set(LIBC_SOURCES | |||
| 	netdb.cpp | ||||
| 	poll.cpp | ||||
| 	printf_impl.cpp | ||||
| 	pthread.cpp | ||||
| 	pwd.cpp | ||||
| 	scanf_impl.cpp | ||||
| 	setjmp.cpp | ||||
|  |  | |||
|  | @ -90,6 +90,8 @@ __BEGIN_DECLS | |||
| 	O(SYS_FSYNC,			fsync)			\ | ||||
| 	O(SYS_SYMLINKAT,		symlinkat)		\ | ||||
| 	O(SYS_HARDLINKAT,		hardlinkat)		\ | ||||
| 	O(SYS_PTHREAD_CREATE,	pthread_create)	\ | ||||
| 	O(SYS_PTHREAD_EXIT,		pthread_exit)	\ | ||||
| 
 | ||||
| enum Syscall | ||||
| { | ||||
|  |  | |||
|  | @ -0,0 +1,49 @@ | |||
| #include <BAN/Assert.h> | ||||
| 
 | ||||
| #include <pthread.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/syscall.h> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| struct pthread_trampoline_info_t | ||||
| { | ||||
| 	void* (*start_routine)(void*); | ||||
| 	void* arg; | ||||
| }; | ||||
| 
 | ||||
| static void pthread_trampoline(void* arg) | ||||
| { | ||||
| 	pthread_trampoline_info_t info; | ||||
| 	memcpy(&info, arg, sizeof(pthread_trampoline_info_t)); | ||||
| 	free(arg); | ||||
| 
 | ||||
| 	pthread_exit(info.start_routine(info.arg)); | ||||
| 	ASSERT_NOT_REACHED(); | ||||
| } | ||||
| 
 | ||||
| int pthread_create(pthread_t* __restrict thread, const pthread_attr_t* __restrict attr, void* (*start_routine)(void*), void* __restrict arg) | ||||
| { | ||||
| 	auto* info = static_cast<pthread_trampoline_info_t*>(malloc(sizeof(pthread_trampoline_info_t))); | ||||
| 	if (info == nullptr) | ||||
| 		return -1; | ||||
| 	info->start_routine = start_routine; | ||||
| 	info->arg = arg; | ||||
| 
 | ||||
| 	const auto ret = syscall(SYS_PTHREAD_CREATE, attr, pthread_trampoline, info); | ||||
| 	if (ret == -1) | ||||
| 	{ | ||||
| 		free(info); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (thread) | ||||
| 		*thread = ret; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void pthread_exit(void* value_ptr) | ||||
| { | ||||
| 	syscall(SYS_PTHREAD_EXIT, value_ptr); | ||||
| 	ASSERT_NOT_REACHED(); | ||||
| } | ||||
|  | @ -0,0 +1,8 @@ | |||
| set(SOURCES | ||||
| 	main.cpp | ||||
| ) | ||||
| 
 | ||||
| add_executable(test-pthread ${SOURCES}) | ||||
| banan_link_library(test-pthread libc) | ||||
| 
 | ||||
| install(TARGETS test-pthread OPTIONAL) | ||||
|  | @ -0,0 +1,28 @@ | |||
| #include <stdio.h> | ||||
| #include <pthread.h> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| void* thread_func(void*) | ||||
| { | ||||
| 	printf("hello from thread\n"); | ||||
| 	return nullptr; | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char** argv) | ||||
| { | ||||
| 	pthread_t tid; | ||||
| 
 | ||||
| 	printf("creating thread\n"); | ||||
| 
 | ||||
| 	if (pthread_create(&tid, nullptr, &thread_func, nullptr) == -1) | ||||
| 	{ | ||||
| 		perror("pthread_create"); | ||||
| 		return 1; | ||||
| 	} | ||||
| 
 | ||||
| 	sleep(1); | ||||
| 
 | ||||
| 	printf("exiting\n"); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue