| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 <decaf/internal/util/concurrent/PlatformThread.h> |
| |
| #include <decaf/lang/exceptions/RuntimeException.h> |
| #include <decaf/util/concurrent/TimeUnit.h> |
| #include <memory> |
| |
| #if HAVE_ERRNO_H |
| #include <errno.h> |
| #endif |
| #if HAVE_LIMITS_H |
| #include <limits.h> |
| #endif |
| #if HAVE_PTHREAD_H |
| #include <pthread.h> |
| #endif |
| #if HAVE_SIGNAL_H |
| #include <signal.h> |
| #endif |
| #if HAVE_STRING_H |
| #include <string.h> |
| #endif |
| #if HAVE_SCHED_H |
| #include <sched.h> |
| #endif |
| #if HAVE_SYS_TIME_H |
| #include <sys/time.h> |
| #endif |
| #if HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #if HAVE_TIME_H |
| #include <time.h> |
| #endif |
| |
| using namespace decaf; |
| using namespace decaf::lang; |
| using namespace decaf::lang::exceptions; |
| using namespace decaf::util; |
| using namespace decaf::util::concurrent; |
| using namespace decaf::internal; |
| using namespace decaf::internal::util; |
| using namespace decaf::internal::util::concurrent; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::createMutex(decaf_mutex_t* mutex) { |
| |
| *mutex = new pthread_mutex_t; |
| |
| if( pthread_mutex_init(*mutex, NULL) != 0 ) { |
| throw RuntimeException( |
| __FILE__, __LINE__, "Failed to create OS Mutex object." ); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::lockMutex(decaf_mutex_t mutex) { |
| |
| if (pthread_mutex_lock(mutex) != 0) { |
| throw RuntimeException( |
| __FILE__, __LINE__, "Failed to Lock OS Mutex" ); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| bool PlatformThread::tryLockMutex(decaf_mutex_t mutex) { |
| return pthread_mutex_trylock(mutex) == 0 ? true : false; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::unlockMutex(decaf_mutex_t mutex) { |
| |
| if (pthread_mutex_unlock(mutex) != 0) { |
| throw RuntimeException( |
| __FILE__, __LINE__, "Failed to unlock OS Mutex" ); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::destroyMutex(decaf_mutex_t mutex) { |
| pthread_mutex_destroy(mutex); |
| delete mutex; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::createRWMutex(decaf_rwmutex_t* mutex) { |
| |
| *mutex = new pthread_rwlock_t; |
| |
| if( pthread_rwlock_init(*mutex, NULL) != 0 ) { |
| throw RuntimeException( |
| __FILE__, __LINE__, "Failed to create OS Mutex object." ); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::readerLockMutex(decaf_rwmutex_t mutex) { |
| |
| if (pthread_rwlock_rdlock(mutex) != 0) { |
| throw RuntimeException( |
| __FILE__, __LINE__, "Failed to Lock OS RW Mutex" ); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::writerLockMutex(decaf_rwmutex_t mutex) { |
| |
| if (pthread_rwlock_wrlock(mutex) != 0) { |
| throw RuntimeException( |
| __FILE__, __LINE__, "Failed to Lock OS RW Mutex" ); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| bool PlatformThread::tryReaderLockMutex(decaf_rwmutex_t mutex) { |
| return pthread_rwlock_tryrdlock(mutex) == 0 ? true : false; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| bool PlatformThread::tryWriterLockMutex(decaf_rwmutex_t mutex) { |
| return pthread_rwlock_trywrlock(mutex) == 0 ? true : false; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::unlockRWMutex(decaf_rwmutex_t mutex) { |
| |
| if (pthread_rwlock_unlock(mutex) != 0) { |
| throw RuntimeException( |
| __FILE__, __LINE__, "Failed to Unlock OS RW Mutex" ); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::destroyRWMutex(decaf_rwmutex_t mutex) { |
| pthread_rwlock_destroy(mutex); |
| delete mutex; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::createCondition(decaf_condition_t* condition) { |
| |
| *condition = new pthread_cond_t; |
| |
| if (pthread_cond_init(*condition, NULL) != 0) { |
| throw RuntimeException( |
| __FILE__, __LINE__, "Failed to initialize OS Condition object."); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::notify(decaf_condition_t condition) { |
| pthread_cond_signal(condition); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::notifyAll(decaf_condition_t condition) { |
| pthread_cond_broadcast(condition); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::waitOnCondition(decaf_condition_t condition, decaf_mutex_t mutex) { |
| pthread_cond_wait(condition, mutex); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| bool PlatformThread::waitOnCondition(decaf_condition_t condition, decaf_mutex_t mutex, |
| long long mills, int nanos) { |
| |
| struct timeval tv; |
| gettimeofday( &tv, NULL ); |
| long long timeNow = TimeUnit::SECONDS.toNanos( tv.tv_sec ) + |
| TimeUnit::MICROSECONDS.toNanos( tv.tv_usec ); |
| |
| // Convert delay to nanoseconds and add it to now. |
| long long delay = TimeUnit::MILLISECONDS.toNanos( mills ) + nanos + timeNow; |
| |
| struct timespec abstime; |
| abstime.tv_sec = TimeUnit::NANOSECONDS.toSeconds( delay ); |
| abstime.tv_nsec = delay % 1000000000; |
| |
| if (pthread_cond_timedwait(condition, mutex, &abstime) == ETIMEDOUT) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::interruptibleWaitOnCondition(decaf_condition_t condition, decaf_mutex_t mutex, |
| CompletionCondition& complete) { |
| |
| do { |
| |
| pthread_cond_wait(condition, mutex); |
| |
| if (complete()) { |
| break; |
| } |
| |
| } while(true); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| bool PlatformThread::interruptibleWaitOnCondition(decaf_condition_t condition, decaf_mutex_t mutex, |
| long long mills, int nanos, CompletionCondition& complete) { |
| |
| struct timeval tv; |
| gettimeofday( &tv, NULL ); |
| long long timeNow = TimeUnit::SECONDS.toNanos( tv.tv_sec ) + |
| TimeUnit::MICROSECONDS.toNanos( tv.tv_usec ); |
| |
| // Convert delay to nanoseconds and add it to now. |
| long long delay = TimeUnit::MILLISECONDS.toNanos( mills ) + nanos + timeNow; |
| |
| struct timespec abstime; |
| abstime.tv_sec = TimeUnit::NANOSECONDS.toSeconds( delay ); |
| abstime.tv_nsec = delay % 1000000000; |
| |
| bool result = false; |
| |
| do { |
| |
| if (pthread_cond_timedwait(condition, mutex, &abstime) == ETIMEDOUT) { |
| |
| // interruption events take precedence over timeout. |
| if (complete(true)) { |
| break; |
| } |
| |
| result = true; |
| break; |
| } |
| |
| // check if spurious wake or intentional. |
| if (complete(false)) { |
| break; |
| } |
| |
| } while(true); |
| |
| return result; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::destroyCondition(decaf_condition_t condition) { |
| pthread_cond_destroy(condition); |
| delete condition; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::initPriorityMapping(int maxPriority, std::vector<int>& mapping) { |
| |
| int max = sched_get_priority_max(SCHED_OTHER); |
| int min = sched_get_priority_min(SCHED_OTHER); |
| |
| if (max == min) { |
| // get this thread's priority and use that for all threads. |
| struct sched_param schedParam; |
| int currPolicy; |
| pthread_getschedparam(pthread_self(), &currPolicy, &schedParam); |
| max = schedParam.sched_priority; |
| min = max; |
| } |
| |
| mapping.clear(); |
| mapping.resize(maxPriority + 1); |
| |
| int tmpmax = max * 1024; |
| int tmpmin = min * 1024; |
| int mid = (tmpmin + tmpmax) / 2; |
| int midrange = maxPriority / 2; |
| |
| mapping[0] = min; |
| |
| int delta = (mid - tmpmin) / midrange; |
| for(int i = 1; i < midrange; i++) { |
| mapping[midrange - i] = (mid - delta * i) / 1024; |
| } |
| |
| int tailcount = maxPriority - midrange; |
| delta = (tmpmax - mid) / tailcount; |
| |
| for(int i = 0; i < tailcount; i++) { |
| mapping[midrange + i] = (mid + delta * i) / 1024; |
| } |
| |
| mapping[maxPriority] = max; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::createNewThread(decaf_thread_t* handle, threadMainMethod threadMain, void* threadArg, |
| int priority, long long stackSize, long long* threadId) { |
| pthread_attr_t attributes; |
| int schedResult; |
| int schedPolicy; |
| |
| schedResult = pthread_attr_init(&attributes); |
| if (schedResult != 0) { |
| throw RuntimeException(__FILE__, __LINE__, |
| "Failed to initialize thread attribute, error value is: %d", schedResult); |
| } |
| |
| schedResult = pthread_attr_getschedpolicy(&attributes, &schedPolicy); |
| if (schedResult != 0) { |
| throw RuntimeException(__FILE__, __LINE__, |
| "Failed to get thread scheduling policy, error value is: %d.", schedResult); |
| } |
| |
| // only set the priority if it's a policy that allows setting of the priority. |
| if (SCHED_FIFO == schedPolicy || SCHED_RR == schedPolicy) { |
| struct sched_param schedData; |
| |
| schedData.sched_priority = priority; |
| schedResult = pthread_attr_setschedparam(&attributes, &schedData); |
| if (schedResult != 0) { |
| throw RuntimeException(__FILE__, __LINE__, |
| "Failed to set new Thread priority to " |
| "value: %d, error value is: %d.", |
| schedData.sched_priority, schedResult); |
| } |
| } |
| |
| if (stackSize != -1) { |
| |
| #ifdef LINUX |
| if (stackSize < PTHREAD_STACK_MIN) { |
| stackSize = PTHREAD_STACK_MIN; |
| } |
| #else |
| if (stackSize < PLATFORM_MIN_STACK_SIZE) { |
| stackSize = PLATFORM_MIN_STACK_SIZE; |
| } |
| #endif |
| |
| if (pthread_attr_setstacksize(&attributes, (size_t)stackSize) == EINVAL) { |
| throw RuntimeException( |
| __FILE__, __LINE__, |
| "Failed to create new Thread due to invalid stack size setting: %d.", |
| stackSize); |
| } |
| } |
| |
| int result = pthread_create(handle, &attributes, threadMain, threadArg); |
| *threadId = (long long)(&handle); |
| pthread_attr_destroy(&attributes); |
| |
| if( result != 0 ) { |
| throw RuntimeException( |
| __FILE__, __LINE__, "Failed to create new Thread." ); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::detachThread(decaf_thread_t handle) { |
| pthread_detach(handle); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::detachOSThread(decaf_thread_t handle DECAF_UNUSED) { |
| // Nothing to do on Linux. |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::joinThread(decaf_thread_t handle) { |
| void* theReturn = 0; |
| pthread_join(handle, &theReturn); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::exitThread() { |
| int result = 0; |
| pthread_exit(&result); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| decaf_thread_t PlatformThread::getCurrentThread() { |
| return pthread_self(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| decaf_thread_t PlatformThread::getSafeOSThreadHandle() { |
| return pthread_self(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| long long PlatformThread::getCurrentThreadId() { |
| |
| // pthread_t can be an int or struct or who knows so we use the address |
| // of the value returned which seems to remain fixed. |
| return (long long)&(pthread_self); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| int PlatformThread::getPriority(decaf_thread_t thread) { |
| struct sched_param schedParam; |
| int policy = SCHED_OTHER; |
| pthread_getschedparam(thread, &policy, &schedParam); |
| return schedParam.sched_priority; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::setPriority(decaf_thread_t thread, int priority) { |
| struct sched_param schedParam; |
| schedParam.sched_priority = priority; |
| pthread_setschedparam(thread, SCHED_OTHER, &schedParam); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| long long PlatformThread::getStackSize(decaf_thread_t thread DECAF_UNUSED) { |
| |
| pthread_attr_t attributes; |
| pthread_attr_init( &attributes ); |
| size_t stackSize; |
| |
| if (pthread_attr_getstacksize(&attributes, &stackSize) == EINVAL) { |
| pthread_attr_destroy( &attributes ); |
| throw RuntimeException( |
| __FILE__, __LINE__, |
| "Failed to get stack size setting."); |
| } |
| |
| pthread_attr_destroy( &attributes ); |
| |
| return (long long)stackSize; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::setStackSize(decaf_thread_t thread DECAF_UNUSED, long long stackSize) { |
| |
| pthread_attr_t attributes; |
| pthread_attr_init( &attributes ); |
| |
| #ifdef LINUX |
| if (stackSize < PTHREAD_STACK_MIN) { |
| stackSize = PTHREAD_STACK_MIN; |
| } |
| #endif |
| |
| if (pthread_attr_setstacksize(&attributes, (size_t)stackSize) == EINVAL) { |
| pthread_attr_destroy( &attributes ); |
| throw RuntimeException( |
| __FILE__, __LINE__, |
| "Failed to set stack size setting: %d.", stackSize); |
| } |
| |
| pthread_attr_destroy( &attributes ); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::yeild() { |
| |
| #ifdef HAVE_PTHREAD_YIELD |
| pthread_yield(); |
| #else |
| #ifdef HAVE_SCHED_YIELD |
| sched_yield(); |
| #endif |
| #endif |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::createTlsKey(decaf_tls_key* tlsKey) { |
| pthread_key_create(tlsKey, NULL); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::destroyTlsKey(decaf_tls_key tlsKey) { |
| pthread_key_delete(tlsKey); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void* PlatformThread::getTlsValue(decaf_tls_key tlsKey) { |
| return pthread_getspecific(tlsKey); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void PlatformThread::setTlsValue(decaf_tls_key tlsKey, void* value) { |
| pthread_setspecific(tlsKey, value); |
| } |