blob: 64b14877b8076b969e66544a3fe7ea5602f8f2d7 [file] [log] [blame]
/*
* 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);
}