* Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2003-2005, Axel DΓΆrfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <pthread.h>
#include "pthread_private.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syscalls.h>
#include <user_mutex_defs.h>
#include <time_private.h>
#define MUTEX_TYPE_BITS 0x0000000f
#define MUTEX_TYPE(mutex) ((mutex)->flags & MUTEX_TYPE_BITS)
static const pthread_mutexattr pthread_mutexattr_default = {
PTHREAD_MUTEX_DEFAULT,
false
};
int
pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* _attr)
{
const pthread_mutexattr* attr = _attr != NULL
? *_attr : &pthread_mutexattr_default;
mutex->lock = 0;
mutex->owner = -1;
mutex->owner_count = 0;
mutex->flags = attr->type | (attr->process_shared ? MUTEX_FLAG_SHARED : 0);
return 0;
}
int
pthread_mutex_destroy(pthread_mutex_t* mutex)
{
return 0;
}
status_t
__pthread_mutex_lock(pthread_mutex_t* mutex, uint32 flags, bigtime_t timeout)
{
thread_id thisThread = find_thread(NULL);
if (mutex->owner == thisThread) {
if (MUTEX_TYPE(mutex) == PTHREAD_MUTEX_RECURSIVE) {
if (mutex->owner_count == INT32_MAX)
return EAGAIN;
mutex->owner_count++;
return 0;
}
if (MUTEX_TYPE(mutex) == PTHREAD_MUTEX_ERRORCHECK
|| MUTEX_TYPE(mutex) == PTHREAD_MUTEX_DEFAULT) {
return timeout < 0 ? EBUSY : EDEADLK;
}
}
const int32 oldValue = atomic_test_and_set((int32*)&mutex->lock, B_USER_MUTEX_LOCKED, 0);
if (oldValue != 0) {
if (timeout < 0)
return EBUSY;
if ((mutex->flags & MUTEX_FLAG_SHARED) != 0)
flags |= B_USER_MUTEX_SHARED;
status_t error;
do {
error = _kern_mutex_lock((int32*)&mutex->lock, NULL, flags, timeout);
} while (error == B_INTERRUPTED);
if (error != B_OK)
return error;
}
assert(mutex->owner == -1);
mutex->owner = thisThread;
mutex->owner_count = 1;
return 0;
}
int
pthread_mutex_lock(pthread_mutex_t* mutex)
{
return __pthread_mutex_lock(mutex, 0, B_INFINITE_TIMEOUT);
}
int
pthread_mutex_trylock(pthread_mutex_t* mutex)
{
return __pthread_mutex_lock(mutex, B_ABSOLUTE_REAL_TIME_TIMEOUT, -1);
}
int
pthread_mutex_clocklock(pthread_mutex_t* mutex, clockid_t clock_id,
const struct timespec* abstime)
{
bigtime_t timeout = 0;
bool invalidTime = false;
if (abstime == NULL || !timespec_to_bigtime(*abstime, timeout))
invalidTime = true;
uint32 flags = 0;
switch (clock_id) {
case CLOCK_REALTIME:
flags = B_ABSOLUTE_REAL_TIME_TIMEOUT;
break;
case CLOCK_MONOTONIC:
flags = B_ABSOLUTE_TIMEOUT;
break;
default:
invalidTime = true;
break;
}
status_t status = __pthread_mutex_lock(mutex, flags, timeout);
if (status != B_OK && invalidTime)
return EINVAL;
return status;
}
int
pthread_mutex_timedlock(pthread_mutex_t* mutex, const struct timespec* abstime)
{
return pthread_mutex_clocklock(mutex, CLOCK_REALTIME, abstime);
}
int
pthread_mutex_unlock(pthread_mutex_t* mutex)
{
if (mutex->owner != find_thread(NULL))
return EPERM;
if (MUTEX_TYPE(mutex) == PTHREAD_MUTEX_RECURSIVE
&& --mutex->owner_count > 0) {
return 0;
}
mutex->owner = -1;
int32 oldValue = atomic_and((int32*)&mutex->lock,
~(int32)B_USER_MUTEX_LOCKED);
if ((oldValue & B_USER_MUTEX_WAITING) != 0) {
_kern_mutex_unblock((int32*)&mutex->lock,
(mutex->flags & MUTEX_FLAG_SHARED) ? B_USER_MUTEX_SHARED : 0);
}
if (MUTEX_TYPE(mutex) == PTHREAD_MUTEX_ERRORCHECK
|| MUTEX_TYPE(mutex) == PTHREAD_MUTEX_DEFAULT) {
if ((oldValue & B_USER_MUTEX_LOCKED) == 0)
return EPERM;
}
return 0;
}
int
pthread_mutex_getprioceiling(const pthread_mutex_t* mutex, int* _prioCeiling)
{
if (mutex == NULL || _prioCeiling == NULL)
return EINVAL;
*_prioCeiling = 0;
return 0;
}
int
pthread_mutex_setprioceiling(pthread_mutex_t* mutex, int prioCeiling,
int* _oldCeiling)
{
if (mutex == NULL)
return EINVAL;
return EPERM;
}