Reimplement unnamed POSIX semaphores using user_mutex

* Fixes sharing semantics, so non-shared semaphores in non-shared
  memory do not become shared after a fork.
* Adds two new system calls: _user_mutex_sem_acquire/release(),
  which reuse the user_mutex address-hashed wait mechanism.
* Named semaphores continue to use traditional sem_id semaphores.
This commit is contained in:
Hamish Morrison 2015-05-11 21:14:52 +01:00
parent 03796a0cef
commit d6d439f3f7
6 changed files with 286 additions and 291 deletions
headers
posix
private
src/system
kernel
libroot/posix

@ -1,5 +1,5 @@
/* /*
* Copyright 2008-2012 Haiku, Inc. * Copyright 2008-2015 Haiku, Inc.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
#ifndef _SEMAPHORE_H_ #ifndef _SEMAPHORE_H_
@ -13,8 +13,12 @@
typedef struct _sem_t { typedef struct _sem_t {
int32_t id; int32_t type;
int32_t _padding[3]; union {
int32_t named_sem_id;
int32_t unnamed_sem;
} u;
int32_t padding[2];
} sem_t; } sem_t;
#define SEM_FAILED ((sem_t*)(long)-1) #define SEM_FAILED ((sem_t*)(long)-1)

@ -20,6 +20,9 @@ status_t _user_mutex_lock(int32* mutex, const char* name, uint32 flags,
status_t _user_mutex_unlock(int32* mutex, uint32 flags); status_t _user_mutex_unlock(int32* mutex, uint32 flags);
status_t _user_mutex_switch_lock(int32* fromMutex, int32* toMutex, status_t _user_mutex_switch_lock(int32* fromMutex, int32* toMutex,
const char* name, uint32 flags, bigtime_t timeout); const char* name, uint32 flags, bigtime_t timeout);
status_t _user_mutex_sem_acquire(int32* sem, const char* name, uint32 flags,
bigtime_t timeout);
status_t _user_mutex_sem_release(int32* sem);
#ifdef __cplusplus #ifdef __cplusplus
} }

@ -79,6 +79,9 @@ extern status_t _kern_mutex_lock(int32* mutex, const char* name,
extern status_t _kern_mutex_unlock(int32* mutex, uint32 flags); extern status_t _kern_mutex_unlock(int32* mutex, uint32 flags);
extern status_t _kern_mutex_switch_lock(int32* fromMutex, int32* toMutex, extern status_t _kern_mutex_switch_lock(int32* fromMutex, int32* toMutex,
const char* name, uint32 flags, bigtime_t timeout); const char* name, uint32 flags, bigtime_t timeout);
extern status_t _kern_mutex_sem_acquire(int32* sem, const char* name,
uint32 flags, bigtime_t timeout);
extern status_t _kern_mutex_sem_release(int32* sem);
/* sem functions */ /* sem functions */
extern sem_id _kern_create_sem(int count, const char *name); extern sem_id _kern_create_sem(int count, const char *name);

@ -1,4 +1,5 @@
/* /*
* Copyright 2015, Hamish Morrison, hamishm53@gmail.com.
* Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@ -99,23 +100,9 @@ remove_user_mutex_entry(UserMutexEntry* entry)
static status_t static status_t
user_mutex_lock_locked(int32* mutex, addr_t physicalAddress, const char* name, user_mutex_wait_locked(int32* mutex, addr_t physicalAddress, const char* name,
uint32 flags, bigtime_t timeout, MutexLocker& locker) uint32 flags, bigtime_t timeout, MutexLocker& locker, bool& lastWaiter)
{ {
// mark the mutex locked + waiting
int32 oldValue = atomic_or(mutex,
B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING);
// The mutex might have been unlocked (or disabled) in the meantime.
if ((oldValue & (B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING)) == 0
|| (oldValue & B_USER_MUTEX_DISABLED) != 0) {
// clear the waiting flag and be done
atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING);
return B_OK;
}
// we have to wait
// add the entry to the table // add the entry to the table
UserMutexEntry entry; UserMutexEntry entry;
entry.address = physicalAddress; entry.address = physicalAddress;
@ -131,18 +118,44 @@ user_mutex_lock_locked(int32* mutex, addr_t physicalAddress, const char* name,
status_t error = waitEntry.Wait(flags, timeout); status_t error = waitEntry.Wait(flags, timeout);
locker.Lock(); locker.Lock();
// dequeue if we weren't woken up if (error != B_OK && entry.locked)
if (!entry.locked && !remove_user_mutex_entry(&entry)) { error = B_OK;
// no one is waiting anymore -- clear the waiting flag
atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING); if (!entry.locked) {
// if nobody woke us up, we have to dequeue ourselves
lastWaiter = !remove_user_mutex_entry(&entry);
} else {
// otherwise the waker has done the work of marking the
// mutex or semaphore uncontended
lastWaiter = false;
} }
if (error != B_OK return error;
&& (entry.locked || (*mutex & B_USER_MUTEX_DISABLED) != 0)) {
// timeout or interrupt, but the mutex was unlocked or disabled in time
error = B_OK;
} }
static status_t
user_mutex_lock_locked(int32* mutex, addr_t physicalAddress,
const char* name, uint32 flags, bigtime_t timeout, MutexLocker& locker)
{
// mark the mutex locked + waiting
int32 oldValue = atomic_or(mutex,
B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING);
if ((oldValue & (B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING)) == 0
|| (oldValue & B_USER_MUTEX_DISABLED) != 0) {
// clear the waiting flag and be done
atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING);
return B_OK;
}
bool lastWaiter;
status_t error = user_mutex_wait_locked(mutex, physicalAddress, name,
flags, timeout, locker, lastWaiter);
if (lastWaiter)
atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING);
return error; return error;
} }
@ -185,6 +198,59 @@ user_mutex_unlock_locked(int32* mutex, addr_t physicalAddress, uint32 flags)
} }
static status_t
user_mutex_sem_acquire_locked(int32* sem, addr_t physicalAddress,
const char* name, uint32 flags, bigtime_t timeout, MutexLocker& locker)
{
// The semaphore may have been released in the meantime, and we also
// need to mark it as contended if it isn't already.
int32 oldValue = atomic_get(sem);
while (oldValue > -1) {
int32 value = atomic_test_and_set(sem, oldValue - 1, oldValue);
if (value == oldValue && value > 0)
return B_OK;
oldValue = value;
}
bool lastWaiter;
status_t error = user_mutex_wait_locked(sem, physicalAddress, name, flags,
timeout, locker, lastWaiter);
if (lastWaiter)
atomic_test_and_set(sem, 0, -1);
return error;
}
static void
user_mutex_sem_release_locked(int32* sem, addr_t physicalAddress)
{
UserMutexEntry* entry = sUserMutexTable.Lookup(physicalAddress);
if (!entry) {
// no waiters - mark as uncontended and release
int32 oldValue = atomic_get(sem);
while (true) {
int32 inc = oldValue < 0 ? 2 : 1;
int32 value = atomic_test_and_set(sem, oldValue + inc, oldValue);
if (value == oldValue)
return;
oldValue = value;
}
}
bool otherWaiters = remove_user_mutex_entry(entry);
entry->locked = true;
entry->condition.NotifyOne();
if (!otherWaiters) {
// mark the semaphore uncontended
atomic_test_and_set(sem, 0, -1);
}
}
static status_t static status_t
user_mutex_lock(int32* mutex, const char* name, uint32 flags, bigtime_t timeout) user_mutex_lock(int32* mutex, const char* name, uint32 flags, bigtime_t timeout)
{ {
@ -312,3 +378,53 @@ _user_mutex_switch_lock(int32* fromMutex, int32* toMutex, const char* name,
return user_mutex_switch_lock(fromMutex, toMutex, name, return user_mutex_switch_lock(fromMutex, toMutex, name,
flags | B_CAN_INTERRUPT, timeout); flags | B_CAN_INTERRUPT, timeout);
} }
status_t
_user_mutex_sem_acquire(int32* sem, const char* name, uint32 flags,
bigtime_t timeout)
{
if (sem == NULL || !IS_USER_ADDRESS(sem) || (addr_t)sem % 4 != 0)
return B_BAD_ADDRESS;
syscall_restart_handle_timeout_pre(flags, timeout);
// wire the page and get the physical address
VMPageWiringInfo wiringInfo;
status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)sem, true,
&wiringInfo);
if (error != B_OK)
return error;
{
MutexLocker locker(sUserMutexTableLock);
error = user_mutex_sem_acquire_locked(sem, wiringInfo.physicalAddress,
name, flags | B_CAN_INTERRUPT, timeout, locker);
}
vm_unwire_page(&wiringInfo);
return syscall_restart_handle_timeout_post(error, timeout);
}
status_t
_user_mutex_sem_release(int32* sem)
{
if (sem == NULL || !IS_USER_ADDRESS(sem) || (addr_t)sem % 4 != 0)
return B_BAD_ADDRESS;
// wire the page and get the physical address
VMPageWiringInfo wiringInfo;
status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)sem, true,
&wiringInfo);
if (error != B_OK)
return error;
{
MutexLocker locker(sUserMutexTableLock);
user_mutex_sem_release_locked(sem, wiringInfo.physicalAddress);
}
vm_unwire_page(&wiringInfo);
return B_OK;
}

@ -150,104 +150,6 @@ private:
}; };
class UnnamedSem : public SemInfo {
public:
UnnamedSem()
:
fID(0)
{
}
virtual ~UnnamedSem()
{
}
status_t Init(int32 semCount, const char* name)
{
return SemInfo::Init(semCount, name);
}
void SetID(sem_id id)
{
fID = id;
}
virtual sem_id ID() const
{
return fID;
}
virtual SemInfo* Clone()
{
sem_info info;
if (get_sem_info(SemaphoreID(), &info) != B_OK)
return NULL;
UnnamedSem* clone = new(std::nothrow) UnnamedSem;
if (clone == NULL)
return NULL;
if (clone->Init(info.count, info.name) != B_OK) {
delete clone;
return NULL;
}
clone->SetID(fID);
return clone;
}
virtual void Delete()
{
delete this;
}
private:
sem_id fID;
};
class UnnamedSharedSem : public SemInfo {
public:
UnnamedSharedSem()
{
}
virtual ~UnnamedSharedSem()
{
}
status_t Init(int32 semCount, const char* name)
{
return SemInfo::Init(semCount, name);
}
virtual sem_id ID() const
{
return SemaphoreID();
}
virtual SemInfo* Clone()
{
// Can't be cloned.
return NULL;
}
virtual void Delete()
{
delete this;
}
UnnamedSharedSem*& HashLink()
{
return fHashLink;
}
private:
UnnamedSharedSem* fHashLink;
};
struct NamedSemHashDefinition { struct NamedSemHashDefinition {
typedef const char* KeyType; typedef const char* KeyType;
typedef NamedSem ValueType; typedef NamedSem ValueType;
@ -274,32 +176,6 @@ struct NamedSemHashDefinition {
}; };
struct UnnamedSemHashDefinition {
typedef sem_id KeyType;
typedef UnnamedSharedSem ValueType;
size_t HashKey(const KeyType& key) const
{
return (size_t)key;
}
size_t Hash(UnnamedSharedSem* semaphore) const
{
return HashKey(semaphore->SemaphoreID());
}
bool Compare(const KeyType& key, UnnamedSharedSem* semaphore) const
{
return key == semaphore->SemaphoreID();
}
UnnamedSharedSem*& GetLink(UnnamedSharedSem* semaphore) const
{
return semaphore->HashLink();
}
};
class GlobalSemTable { class GlobalSemTable {
public: public:
GlobalSemTable() GlobalSemTable()
@ -316,10 +192,7 @@ public:
status_t Init() status_t Init()
{ {
status_t error = fNamedSemaphores.Init(); return fNamedSemaphores.Init();
if (error != B_OK)
return error;
return fUnnamedSemaphores.Init();
} }
status_t OpenNamedSem(const char* name, int openFlags, mode_t mode, status_t OpenNamedSem(const char* name, int openFlags, mode_t mode,
@ -393,61 +266,11 @@ public:
return B_OK; return B_OK;
} }
status_t CreateUnnamedSem(uint32 semCount, int32_t& _id)
{
MutexLocker _(fLock);
if (fSemaphoreCount >= MAX_POSIX_SEMS)
return ENOSPC;
UnnamedSharedSem* sem = new(std::nothrow) UnnamedSharedSem;
if (sem == NULL)
return B_NO_MEMORY;
status_t error = sem->Init(semCount, "unnamed shared sem");
if (error == B_OK)
error = fUnnamedSemaphores.Insert(sem);
if (error != B_OK) {
delete sem;
return error;
}
fSemaphoreCount++;
_id = sem->SemaphoreID();
return B_OK;
}
status_t DeleteUnnamedSem(sem_id id)
{
MutexLocker _(fLock);
UnnamedSharedSem* sem = fUnnamedSemaphores.Lookup(id);
if (sem == NULL)
return B_BAD_VALUE;
fUnnamedSemaphores.Remove(sem);
delete sem;
fSemaphoreCount--;
return B_OK;
}
bool IsUnnamedValidSem(sem_id id)
{
MutexLocker _(fLock);
return fUnnamedSemaphores.Lookup(id) != NULL;
}
private: private:
typedef BOpenHashTable<NamedSemHashDefinition, true> NamedSemTable; typedef BOpenHashTable<NamedSemHashDefinition, true> NamedSemTable;
typedef BOpenHashTable<UnnamedSemHashDefinition, true> UnnamedSemTable;
mutex fLock; mutex fLock;
NamedSemTable fNamedSemaphores; NamedSemTable fNamedSemaphores;
UnnamedSemTable fUnnamedSemaphores;
int32 fSemaphoreCount; int32 fSemaphoreCount;
}; };
@ -602,46 +425,6 @@ struct realtime_sem_context {
return context; return context;
} }
status_t CreateUnnamedSem(uint32 semCount, bool shared, int32_t& _id)
{
if (shared)
return sSemTable.CreateUnnamedSem(semCount, _id);
UnnamedSem* sem = new(std::nothrow) UnnamedSem;
if (sem == NULL)
return B_NO_MEMORY;
ObjectDeleter<UnnamedSem> semDeleter(sem);
status_t error = sem->Init(semCount, "unnamed sem");
if (error != B_OK)
return error;
TeamSemInfo* teamSem = new(std::nothrow) TeamSemInfo(sem, NULL);
if (teamSem == NULL)
return B_NO_MEMORY;
semDeleter.Detach();
MutexLocker _(fLock);
if (fSemaphoreCount >= MAX_POSIX_SEMS_PER_TEAM) {
delete teamSem;
return ENOSPC;
}
sem->SetID(_NextPrivateSemID());
error = fSemaphores.Insert(teamSem);
if (error != B_OK) {
delete teamSem;
return error;
}
fSemaphoreCount++;
_id = teamSem->ID();
return B_OK;
}
status_t OpenSem(const char* name, int openFlags, mode_t mode, status_t OpenSem(const char* name, int openFlags, mode_t mode,
uint32 semCount, sem_t* userSem, sem_t*& _usedUserSem, int32_t& _id, uint32 semCount, sem_t* userSem, sem_t*& _usedUserSem, int32_t& _id,
bool& _created) bool& _created)
@ -706,7 +489,7 @@ struct realtime_sem_context {
TeamSemInfo* sem = fSemaphores.Lookup(id); TeamSemInfo* sem = fSemaphores.Lookup(id);
if (sem == NULL) if (sem == NULL)
return sSemTable.DeleteUnnamedSem(id); return B_BAD_VALUE;
if (sem->Close()) { if (sem->Close()) {
// last reference closed // last reference closed
@ -724,10 +507,9 @@ struct realtime_sem_context {
MutexLocker locker(fLock); MutexLocker locker(fLock);
TeamSemInfo* sem = fSemaphores.Lookup(id); TeamSemInfo* sem = fSemaphores.Lookup(id);
if (sem == NULL) { if (sem == NULL)
if (!sSemTable.IsUnnamedValidSem(id))
return B_BAD_VALUE; return B_BAD_VALUE;
} else else
id = sem->SemaphoreID(); id = sem->SemaphoreID();
locker.Unlock(); locker.Unlock();
@ -751,10 +533,9 @@ struct realtime_sem_context {
MutexLocker locker(fLock); MutexLocker locker(fLock);
TeamSemInfo* sem = fSemaphores.Lookup(id); TeamSemInfo* sem = fSemaphores.Lookup(id);
if (sem == NULL) { if (sem == NULL)
if (!sSemTable.IsUnnamedValidSem(id))
return B_BAD_VALUE; return B_BAD_VALUE;
} else else
id = sem->SemaphoreID(); id = sem->SemaphoreID();
locker.Unlock(); locker.Unlock();
@ -768,10 +549,9 @@ struct realtime_sem_context {
MutexLocker locker(fLock); MutexLocker locker(fLock);
TeamSemInfo* sem = fSemaphores.Lookup(id); TeamSemInfo* sem = fSemaphores.Lookup(id);
if (sem == NULL) { if (sem == NULL)
if (!sSemTable.IsUnnamedValidSem(id))
return B_BAD_VALUE; return B_BAD_VALUE;
} else else
id = sem->SemaphoreID(); id = sem->SemaphoreID();
locker.Unlock(); locker.Unlock();
@ -914,23 +694,6 @@ _user_realtime_sem_open(const char* userName, int openFlagsOrShared,
if (!IS_USER_ADDRESS(userSem)) if (!IS_USER_ADDRESS(userSem))
return B_BAD_ADDRESS; return B_BAD_ADDRESS;
// unnamed semaphores are less work -- deal with them first
if (userName == NULL) {
int32_t id;
status_t error = context->CreateUnnamedSem(semCount, openFlagsOrShared,
id);
if (error != B_OK)
return error;
if (user_memcpy(&userSem->id, &id, sizeof(int)) != B_OK) {
sem_t* dummy;
context->CloseSem(id, dummy);
return B_BAD_ADDRESS;
}
return B_OK;
}
// check user pointers // check user pointers
if (_usedUserSem == NULL) if (_usedUserSem == NULL)
return B_BAD_VALUE; return B_BAD_VALUE;
@ -954,7 +717,7 @@ _user_realtime_sem_open(const char* userName, int openFlagsOrShared,
return error; return error;
// copy results back to userland // copy results back to userland
if (user_memcpy(&userSem->id, &id, sizeof(int)) != B_OK if (user_memcpy(&userSem->u.named_sem_id, &id, sizeof(int32_t)) != B_OK
|| user_memcpy(_usedUserSem, &usedUserSem, sizeof(sem_t*)) != B_OK) { || user_memcpy(_usedUserSem, &usedUserSem, sizeof(sem_t*)) != B_OK) {
if (created) if (created)
sSemTable.UnlinkNamedSem(name); sSemTable.UnlinkNamedSem(name);

@ -1,4 +1,5 @@
/* /*
* Copyright 2015, Hamish Morrison, hamishm53@gmail.com.
* Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@ -17,6 +18,25 @@
#include <posix/realtime_sem_defs.h> #include <posix/realtime_sem_defs.h>
#include <syscall_utils.h> #include <syscall_utils.h>
#include <syscalls.h> #include <syscalls.h>
#include <user_mutex_defs.h>
#define SEM_TYPE_NAMED 1
#define SEM_TYPE_UNNAMED 2
static int32
atomic_add_if_greater(int32* value, int32 amount, int32 testValue)
{
int32 current = atomic_get(value);
while (current > testValue) {
int32 old = atomic_test_and_set(value, current + amount, current);
if (old == current)
return old;
current = old;
}
return current;
}
sem_t* sem_t*
@ -50,6 +70,8 @@ sem_open(const char* name, int openFlags,...)
__set_errno(B_NO_MEMORY); __set_errno(B_NO_MEMORY);
return SEM_FAILED; return SEM_FAILED;
} }
sem->type = SEM_TYPE_NAMED;
MemoryDeleter semDeleter(sem); MemoryDeleter semDeleter(sem);
// ask the kernel to open the semaphore // ask the kernel to open the semaphore
@ -72,7 +94,8 @@ int
sem_close(sem_t* semaphore) sem_close(sem_t* semaphore)
{ {
sem_t* deleteSem = NULL; sem_t* deleteSem = NULL;
status_t error = _kern_realtime_sem_close(semaphore->id, &deleteSem); status_t error = _kern_realtime_sem_close(semaphore->u.named_sem_id,
&deleteSem);
if (error == B_OK) if (error == B_OK)
free(deleteSem); free(deleteSem);
@ -90,35 +113,87 @@ sem_unlink(const char* name)
int int
sem_init(sem_t* semaphore, int shared, unsigned value) sem_init(sem_t* semaphore, int shared, unsigned value)
{ {
RETURN_AND_SET_ERRNO(_kern_realtime_sem_open(NULL, shared, 0, value, semaphore->type = SEM_TYPE_UNNAMED;
semaphore, NULL)); semaphore->u.unnamed_sem = value;
return 0;
} }
int int
sem_destroy(sem_t* semaphore) sem_destroy(sem_t* semaphore)
{ {
RETURN_AND_SET_ERRNO(_kern_realtime_sem_close(semaphore->id, NULL)); if (semaphore->type != SEM_TYPE_UNNAMED)
RETURN_AND_SET_ERRNO(EINVAL);
return 0;
}
static int
unnamed_sem_post(sem_t* semaphore) {
int32* sem = (int32*)&semaphore->u.unnamed_sem;
int32 oldValue = atomic_add_if_greater(sem, 1, -1);
if (oldValue > -1)
return 0;
return _kern_mutex_sem_release(sem);
}
static int
unnamed_sem_trywait(sem_t* semaphore) {
int32* sem = (int32*)&semaphore->u.unnamed_sem;
int32 oldValue = atomic_add_if_greater(sem, -1, 0);
if (oldValue > 0)
return 0;
return EAGAIN;
}
static int
unnamed_sem_timedwait(sem_t* semaphore, const struct timespec* timeout) {
int32* sem = (int32*)&semaphore->u.unnamed_sem;
bigtime_t timeoutMicros = B_INFINITE_TIMEOUT;
if (timeout != NULL) {
timeoutMicros = ((bigtime_t)timeout->tv_sec) * 1000000
+ timeout->tv_nsec / 1000;
}
int result = unnamed_sem_trywait(semaphore);
if (result == 0)
return 0;
return _kern_mutex_sem_acquire(sem, NULL,
timeoutMicros == B_INFINITE_TIMEOUT ? 0 : B_ABSOLUTE_REAL_TIME_TIMEOUT,
timeoutMicros);
} }
int int
sem_post(sem_t* semaphore) sem_post(sem_t* semaphore)
{ {
RETURN_AND_SET_ERRNO(_kern_realtime_sem_post(semaphore->id)); status_t error;
if (semaphore->type == SEM_TYPE_NAMED)
error = _kern_realtime_sem_post(semaphore->u.named_sem_id);
else
error = unnamed_sem_post(semaphore);
RETURN_AND_SET_ERRNO(error);
} }
int static int
sem_timedwait(sem_t* semaphore, const struct timespec* timeout) named_sem_timedwait(sem_t* semaphore, const struct timespec* timeout)
{ {
if (timeout != NULL if (timeout != NULL
&& (timeout->tv_nsec < 0 || timeout->tv_nsec >= 1000000000)) { && (timeout->tv_nsec < 0 || timeout->tv_nsec >= 1000000000)) {
status_t err = _kern_realtime_sem_wait(semaphore->id, 0); status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id, 0);
if (err == B_WOULD_BLOCK) if (err == B_WOULD_BLOCK)
err = EINVAL; err = EINVAL;
// do nothing, return err as it is. // do nothing, return err as it is.
RETURN_AND_SET_ERRNO_TEST_CANCEL(err); return err;
} }
bigtime_t timeoutMicros = B_INFINITE_TIMEOUT; bigtime_t timeoutMicros = B_INFINITE_TIMEOUT;
@ -126,31 +201,62 @@ sem_timedwait(sem_t* semaphore, const struct timespec* timeout)
timeoutMicros = ((bigtime_t)timeout->tv_sec) * 1000000 timeoutMicros = ((bigtime_t)timeout->tv_sec) * 1000000
+ timeout->tv_nsec / 1000; + timeout->tv_nsec / 1000;
} }
status_t err = _kern_realtime_sem_wait(semaphore->id, timeoutMicros); status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id,
timeoutMicros);
if (err == B_WOULD_BLOCK) if (err == B_WOULD_BLOCK)
err = ETIMEDOUT; err = ETIMEDOUT;
RETURN_AND_SET_ERRNO_TEST_CANCEL(err); return err;
} }
int int
sem_trywait(sem_t* semaphore) sem_trywait(sem_t* semaphore)
{ {
RETURN_AND_SET_ERRNO(_kern_realtime_sem_wait(semaphore->id, 0)); status_t error;
if (semaphore->type == SEM_TYPE_NAMED)
error = _kern_realtime_sem_wait(semaphore->u.named_sem_id, 0);
else
error = unnamed_sem_trywait(semaphore);
RETURN_AND_SET_ERRNO(error);
} }
int int
sem_wait(sem_t* semaphore) sem_wait(sem_t* semaphore)
{ {
RETURN_AND_SET_ERRNO_TEST_CANCEL( status_t error;
_kern_realtime_sem_wait(semaphore->id, B_INFINITE_TIMEOUT)); if (semaphore->type == SEM_TYPE_NAMED)
error = named_sem_timedwait(semaphore, NULL);
else
error = unnamed_sem_timedwait(semaphore, NULL);
RETURN_AND_SET_ERRNO_TEST_CANCEL(error);
}
int
sem_timedwait(sem_t* semaphore, const struct timespec* timeout)
{
status_t error;
if (semaphore->type == SEM_TYPE_NAMED)
error = named_sem_timedwait(semaphore, timeout);
else
error = unnamed_sem_timedwait(semaphore, timeout);
RETURN_AND_SET_ERRNO_TEST_CANCEL(error);
} }
int int
sem_getvalue(sem_t* semaphore, int* value) sem_getvalue(sem_t* semaphore, int* value)
{ {
RETURN_AND_SET_ERRNO(_kern_realtime_sem_get_value(semaphore->id, value)); if (semaphore->type == SEM_TYPE_NAMED) {
RETURN_AND_SET_ERRNO(_kern_realtime_sem_get_value(
semaphore->u.named_sem_id, value));
} else {
*value = semaphore->u.unnamed_sem < 0 ? 0 : semaphore->u.unnamed_sem;
return 0;
}
} }