/* Copyright (C) 2014 Stony Brook University This file is part of Graphene Library OS. Graphene Library OS is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Graphene Library OS 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ /* * db_threading.c * * This file contain APIs to create, exit and yield a thread. */ #include "pal_defs.h" #include "pal_freebsd_defs.h" #include "pal.h" #include "pal_internal.h" #include "pal_freebsd.h" #include "pal_error.h" #include "pal_debug.h" #include "api.h" #include #include #include #include #include #include #include #include #define THREAD_STACK_SIZE (pal_state.alloc_align) /* _DkThreadCreate for internal use. Create an internal thread inside the current process. The arguments callback and param specify the starting function and parameters */ int _DkThreadCreate (PAL_HANDLE * handle, int (*callback) (void *), const void * param) { void * child_stack = NULL; if (_DkVirtualMemoryAlloc(&child_stack, THREAD_STACK_SIZE, 0, PAL_PROT_READ|PAL_PROT_WRITE) < 0) return -PAL_ERROR_NOMEM; // move child_stack to the top of stack. child_stack += THREAD_STACK_SIZE; // align child_stack to 16 child_stack = ALIGN_DOWN_PTR(child_stack, 16); flags &= PAL_THREAD_MASK; assert(!flags); //FreeBSD does not support any more flags for rfork! int ret = rfork_thread( RFPROC|RFNOWAIT|RFSIGSHARE|RFMEM, child_stack, callback, (void *)param); if (IS_ERR(ret)) return -PAL_ERROR_DENIED; PAL_HANDLE hdl = malloc(HANDLE_SIZE(thread)); SET_HANDLE_TYPE(hdl, thread); hdl->thread.tid = ret; *handle = hdl; return 0; } int _DkThreadDelayExecution (unsigned long * duration) { struct timespec sleeptime; struct timespec remainingtime; const long VERY_LONG_TIME_IN_US = 1000000L * 60 * 60 * 24 * 365 * 128; if (*duration > VERY_LONG_TIME_IN_US) { /* avoid overflow with time_t */ sleeptime.tv_sec = VERY_LONG_TIME_IN_US / 1000000; sleeptime.tv_nsec = 0; } else { sleeptime.tv_sec = *duration / 1000000; sleeptime.tv_nsec = (*duration - sleeptime.tv_sec * 1000000) * 1000; } int ret = INLINE_SYSCALL(nanosleep, 2, &sleeptime, &remainingtime); if (IS_ERR(ret)) { PAL_NUM remaining = remainingtime.tv_sec * 1000000 + remainingtime.tv_nsec / 1000; *duration -= remaining; return -PAL_ERROR_INTERRUPTED; } return 0; } /* PAL call DkThreadYieldExecution. Yield the execution of the current thread. */ void _DkThreadYieldExecution (void) { INLINE_SYSCALL(sched_yield, 0); } /* _DkThreadExit for internal use: Thread exiting */ noreturn void _DkThreadExit(int* clear_child_tid) { if (clear_child_tid) { /* thread is ready to exit, must inform LibOS by setting *clear_child_tid to 0; * async helper thread in LibOS is waiting on this to wake up parent */ __atomic_store_n(clear_child_tid, 0, __ATOMIC_RELAXED); } INLINE_SYSCALL(exit, 1, 0); } int _DkThreadResume (PAL_HANDLE threadHandle) { int ret = INLINE_SYSCALL(kill, 2, threadHandle->thread.tid, SIGCONT); if (IS_ERR(ret)) return -PAL_ERROR_DENIED; return 0; } struct handle_ops thread_ops = { /* nothing */ };