/* * Copyright (C) 2019 Intel Corporation. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "bh_thread.h" #include "bh_assert.h" #include "bh_log.h" #include "bh_memory.h" #include #include #include static bool is_thread_sys_inited = false; static korp_mutex thread_list_lock; static pthread_key_t thread_local_storage_key[BH_MAX_TLS_NUM]; int _vm_thread_sys_init() { unsigned i, j; int ret; if (is_thread_sys_inited) return 0; for (i = 0; i < BH_MAX_TLS_NUM; i++) { ret = pthread_key_create(&thread_local_storage_key[i], NULL); if (ret) goto fail; } ret = vm_mutex_init(&thread_list_lock); if (ret) goto fail; is_thread_sys_inited = true; return 0; fail: for (j = 0; j < i; j++) pthread_key_delete(thread_local_storage_key[j]); return -1; } void vm_thread_sys_destroy(void) { if (is_thread_sys_inited) { unsigned i; for (i = 0; i < BH_MAX_TLS_NUM; i++) pthread_key_delete(thread_local_storage_key[i]); vm_mutex_destroy(&thread_list_lock); is_thread_sys_inited = false; } } typedef struct { thread_start_routine_t start; void* stack; int stack_size; void* arg; } thread_wrapper_arg; static void *vm_thread_wrapper(void *arg) { thread_wrapper_arg * targ = arg; LOG_VERBOSE("THREAD CREATE 0x%08x\n", &targ); targ->stack = (void *) ((unsigned int) (&arg) & ~0xfff); _vm_tls_put(1, targ); targ->start(targ->arg); bh_free(targ); _vm_tls_put(1, NULL); return NULL; } int _vm_thread_create_with_prio(korp_tid *tid, thread_start_routine_t start, void *arg, unsigned int stack_size, int prio) { pthread_attr_t tattr; thread_wrapper_arg *targ; bh_assert(stack_size > 0); bh_assert(tid); bh_assert(start); *tid = INVALID_THREAD_ID; pthread_attr_init(&tattr); pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE); if (pthread_attr_setstacksize(&tattr, stack_size) != 0) { bh_debug("Invalid thread stack size %u. Min stack size on Linux = %u", stack_size, PTHREAD_STACK_MIN); pthread_attr_destroy(&tattr); return BHT_ERROR; } targ = (thread_wrapper_arg*) bh_malloc(sizeof(*targ)); if (!targ) { pthread_attr_destroy(&tattr); return BHT_ERROR; } targ->start = start; targ->arg = arg; targ->stack_size = stack_size; if (pthread_create(tid, &tattr, vm_thread_wrapper, targ) != 0) { pthread_attr_destroy(&tattr); bh_free(targ); return BHT_ERROR; } pthread_attr_destroy(&tattr); return BHT_OK; } int _vm_thread_create(korp_tid *tid, thread_start_routine_t start, void *arg, unsigned int stack_size) { return _vm_thread_create_with_prio(tid, start, arg, stack_size, BH_THREAD_DEFAULT_PRIORITY); } korp_tid _vm_self_thread() { return (korp_tid) pthread_self(); } void vm_thread_exit(void * code) { bh_free(_vm_tls_get(1)); _vm_tls_put(1, NULL); pthread_exit(code); } void *_vm_tls_get(unsigned idx) { bh_assert(idx < BH_MAX_TLS_NUM); return pthread_getspecific(thread_local_storage_key[idx]); } int _vm_tls_put(unsigned idx, void * tls) { bh_assert(idx < BH_MAX_TLS_NUM); pthread_setspecific(thread_local_storage_key[idx], tls); return BHT_OK; } int _vm_mutex_init(korp_mutex *mutex) { return pthread_mutex_init(mutex, NULL) == 0 ? BHT_OK : BHT_ERROR; } int _vm_recursive_mutex_init(korp_mutex *mutex) { int ret; pthread_mutexattr_t mattr; bh_assert(mutex); ret = pthread_mutexattr_init(&mattr); if (ret) return BHT_ERROR; pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE_NP); ret = pthread_mutex_init(mutex, &mattr); pthread_mutexattr_destroy(&mattr); return ret == 0 ? BHT_OK : BHT_ERROR; } int _vm_mutex_destroy(korp_mutex *mutex) { int ret; bh_assert(mutex); ret = pthread_mutex_destroy(mutex); return ret == 0 ? BHT_OK : BHT_ERROR; } /* Returned error (EINVAL, EAGAIN and EDEADLK) from locking the mutex indicates some logic error present in the program somewhere. Don't try to recover error for an existing unknown error.*/ void vm_mutex_lock(korp_mutex *mutex) { int ret; bh_assert(mutex); ret = pthread_mutex_lock(mutex); if (0 != ret) { printf("vm mutex lock failed (ret=%d)!\n", ret); exit(-1); } } int vm_mutex_trylock(korp_mutex *mutex) { int ret; bh_assert(mutex); ret = pthread_mutex_trylock(mutex); return ret == 0 ? BHT_OK : BHT_ERROR; } /* Returned error (EINVAL, EAGAIN and EPERM) from unlocking the mutex indicates some logic error present in the program somewhere. Don't try to recover error for an existing unknown error.*/ void vm_mutex_unlock(korp_mutex *mutex) { int ret; bh_assert(mutex); ret = pthread_mutex_unlock(mutex); if (0 != ret) { printf("vm mutex unlock failed (ret=%d)!\n", ret); exit(-1); } } int _vm_sem_init(korp_sem* sem, unsigned int c) { int ret; bh_assert(sem); ret = sem_init(sem, 0, c); return ret == 0 ? BHT_OK : BHT_ERROR; } int _vm_sem_destroy(korp_sem *sem) { int ret; bh_assert(sem); ret = sem_destroy(sem); return ret == 0 ? BHT_OK : BHT_ERROR; } int _vm_sem_wait(korp_sem *sem) { int ret; bh_assert(sem); ret = sem_wait(sem); return ret == 0 ? BHT_OK : BHT_ERROR; } int _vm_sem_reltimedwait(korp_sem *sem, int mills) { int ret = BHT_OK; struct timespec timeout; const int mills_per_sec = 1000; const int mills_to_nsec = 1E6; bh_assert(sem); if (mills == BHT_WAIT_FOREVER) { ret = sem_wait(sem); } else { timeout.tv_sec = mills / mills_per_sec; timeout.tv_nsec = (mills % mills_per_sec) * mills_to_nsec; timeout.tv_sec += time(NULL); ret = sem_timedwait(sem, &timeout); } if (ret != BHT_OK) { if (errno == BHT_TIMEDOUT) { ret = BHT_TIMEDOUT; errno = 0; } else { bh_debug("Faliure happens when timed wait is called"); bh_assert(0); } } return ret; } int _vm_sem_post(korp_sem *sem) { bh_assert(sem); return sem_post(sem) == 0 ? BHT_OK : BHT_ERROR; } int _vm_cond_init(korp_cond *cond) { bh_assert(cond); if (pthread_cond_init(cond, NULL) != BHT_OK) return BHT_ERROR; return BHT_OK; } int _vm_cond_destroy(korp_cond *cond) { bh_assert(cond); if (pthread_cond_destroy(cond) != BHT_OK) return BHT_ERROR; return BHT_OK; } int _vm_cond_wait(korp_cond *cond, korp_mutex *mutex) { bh_assert(cond); bh_assert(mutex); if (pthread_cond_wait(cond, mutex) != BHT_OK) return BHT_ERROR; return BHT_OK; } static void msec_nsec_to_abstime(struct timespec *ts, int64 msec, int32 nsec) { struct timeval tv; gettimeofday(&tv, NULL); ts->tv_sec = tv.tv_sec + msec / 1000; ts->tv_nsec = tv.tv_usec * 1000 + (msec % 1000) * 1000000 + nsec; if (ts->tv_nsec >= 1000000000L) { ts->tv_sec++; ts->tv_nsec -= 1000000000L; } } int _vm_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, int mills) { int ret; struct timespec abstime; if (mills == BHT_WAIT_FOREVER) ret = pthread_cond_wait(cond, mutex); else { msec_nsec_to_abstime(&abstime, mills, 0); ret = pthread_cond_timedwait(cond, mutex, &abstime); } if (ret != BHT_OK && ret != BHT_TIMEDOUT) return BHT_ERROR; return BHT_OK; } int _vm_cond_signal(korp_cond *cond) { bh_assert(cond); if (pthread_cond_signal(cond) != BHT_OK) return BHT_ERROR; return BHT_OK; } int _vm_cond_broadcast(korp_cond *cond) { bh_assert(cond); if (pthread_cond_broadcast(cond) != BHT_OK) return BHT_ERROR; return BHT_OK; } int _vm_thread_cancel(korp_tid thread) { return pthread_cancel(thread); } int _vm_thread_join(korp_tid thread, void **value_ptr, int mills) { return pthread_join(thread, value_ptr); } int _vm_thread_detach(korp_tid thread) { return pthread_detach(thread); }