sems-linux.cpp revision 881b5ff6bc55e1fb0f4ef42f9782ccec79c0a138
/* $Id$ */
/** @file
* InnoTek Portable Runtime - Semaphores, Linux (AMD64 only ATM).
*/
/*
* Copyright (C) 2006 InnoTek Systemberatung GmbH
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License as published by the Free Software Foundation,
* in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
* distribution. VirtualBox OSE is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY of any kind.
*
* If you received this file as part of a commercial VirtualBox
* distribution, then only the terms of your commercial VirtualBox
* license agreement apply instead of the previous paragraph.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <iprt/semaphore.h>
#include <errno.h>
#include <limits.h>
#include <pthread.h>
#include <unistd.h>
#if 0 /* With 2.6.17 futex.h has become C++ unfriendly. */
#else
# define FUTEX_WAIT 0
# define FUTEX_WAKE 1
#endif
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Linux (single wakup) event semaphore.
*/
struct RTSEMEVENTINTERNAL
{
/** Magic value. */
/** The futex state variable.
* <0 means signaled.
* 0 means not signaled, no waiters.
* >0 means not signaled, and the value gives the number of waiters.
*/
};
/**
* Linux multiple wakup event semaphore.
*/
struct RTSEMEVENTMULTIINTERNAL
{
/** Magic value. */
/** The futex state variable.
* -1 means signaled.
* 0 means not signaled, no waiters.
* >0 means not signaled, and the value gives the number of waiters.
*/
};
/**
* Posix internal representation of a Mutex semaphore.
*/
struct RTSEMMUTEXINTERNAL
{
/** pthread mutex. */
/** The owner of the mutex. */
/** Nesting count. */
};
/**
* Posix internal representation of a read-write semaphore.
*/
struct RTSEMRWINTERNAL
{
/** pthread rwlock. */
/** Variable to check if initialized.
* 0 is uninitialized, ~0 is inititialized. */
volatile unsigned uCheck;
/** The write owner of the lock. */
};
/**
* Wrapper for the futex syscall.
*/
static long sys_futex(int32_t volatile *uaddr, int op, int val, struct timespec *utime, int32_t *uaddr2, int val3)
{
errno = 0;
if (rc < 0)
{
}
return rc;
}
{
/*
* Allocate semaphore handle.
*/
struct RTSEMEVENTINTERNAL *pIntEventSem = (struct RTSEMEVENTINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTINTERNAL));
if (pIntEventSem)
{
pIntEventSem->cWaiters = 0;
return VINF_SUCCESS;
}
return VERR_NO_MEMORY;
}
{
/*
* Validate input.
*/
/*
* Invalidate the semaphore and wake up anyone waiting on it.
*/
{
usleep(1000);
}
/*
* Free the semaphore memory and be gone.
*/
return VINF_SUCCESS;
}
{
/*
* Validate input.
*/
/*
* Try signal it.
*/
for (unsigned i = 0;; i++)
{
if (iCur == 0)
{
break; /* nobody is waiting */
}
else if (iCur < 0)
break; /* already signaled */
else
{
/* somebody is waiting, try wake up one of them. */
{
break;
}
/*
* This path is taken in two situations:
* 1) A waiting thread is returning from the sys_futex call with a
* non-zero return value.
* 2) There are two threads signaling the event at the
* same time and only one thread waiting.
*
* At this point we know that nobody is activly waiting on the event but
* at the same time, we are racing someone updating the state. The current
* strategy is to spin till the thread racing us is done, this is kind of
* brain dead and need fixing of course.
*/
if (RT_UNLIKELY(i > 32))
{
if ((i % 128) == 127)
usleep(1000);
else if (!(i % 4))
else
}
}
}
return VINF_SUCCESS;
}
{
/*
* Validate input.
*/
/*
* Quickly check whether it's signaled.
*/
return VINF_SUCCESS;
/*
* Convert timeout value.
*/
if (cMillies != RT_INDEFINITE_WAIT)
{
}
/*
* The wait loop.
*/
for (unsigned i = 0;; i++)
{
/*
* Announce that we're among the waiters.
*/
if (iNew == 0)
return VINF_SUCCESS;
{
/*
* Go to sleep.
*/
return VERR_SEM_DESTROYED;
/* Did somebody wake us up us from RTSemEventSignal()? */
if (rc == 0)
return VINF_SUCCESS;
/* No, then the kernel woke us up or we failed going to sleep. Adjust the accounting. */
/*
* Act on the wakup code.
*/
{
return VERR_TIMEOUT;
}
if (rc == -EWOULDBLOCK)
/* retry with new value. */;
{
if (!fAutoResume)
return VERR_INTERRUPTED;
}
else
{
/* this shouldn't happen! */
return RTErrConvertFromErrno(rc);
}
}
else
{
/* this can't happen. */
return VERR_SEM_DESTROYED;
}
}
}
{
return rc;
}
{
}
{
/*
* Allocate semaphore handle.
*/
struct RTSEMEVENTMULTIINTERNAL *pIntEventMultiSem = (struct RTSEMEVENTMULTIINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTMULTIINTERNAL));
if (pIntEventMultiSem)
{
pIntEventMultiSem->iState = 0;
return VINF_SUCCESS;
}
return VERR_NO_MEMORY;
}
{
/*
* Validate input.
*/
/*
* Invalidate the semaphore and wake up anyone waiting on it.
*/
{
usleep(1000);
}
/*
* Free the semaphore memory and be gone.
*/
return VINF_SUCCESS;
}
{
/*
* Validate input.
*/
/*
* Signal it.
*/
if (iOld > 0)
{
/* wake up sleeping threads. */
}
return VINF_SUCCESS;
}
{
/*
* Validate input.
*/
#ifdef RT_STRICT
#endif
/*
* Reset it.
*/
return VINF_SUCCESS;
}
{
/*
* Validate input.
*/
/*
* Quickly check whether it's signaled.
*/
if (iCur == -1)
return VINF_SUCCESS;
if (!cMillies)
return VERR_TIMEOUT;
/*
* Convert timeout value.
*/
if (cMillies != RT_INDEFINITE_WAIT)
{
}
/*
* The wait loop.
*/
for (unsigned i = 0;; i++)
{
/*
* Start waiting. We only account for there being or having been
* threads waiting on the semaphore to keep things simple.
*/
if ( iCur == 1
{
return VERR_SEM_DESTROYED;
if (rc == 0)
return VINF_SUCCESS;
/*
* Act on the wakup code.
*/
{
return VERR_TIMEOUT;
}
if (rc == -EWOULDBLOCK)
/* retry, the value changed. */;
{
if (!fAutoResume)
return VERR_INTERRUPTED;
}
else
{
/* this shouldn't happen! */
return RTErrConvertFromErrno(rc);
}
}
else if (iCur == -1)
return VINF_SUCCESS;
}
}
{
return rc;
}
{
}
/**
* Validate a Mutex semaphore handle passed to one of the interface.
*
* @returns true if valid.
* @returns false if invalid.
* @param pIntMutexSem Pointer to the mutex semaphore to validate.
*/
{
return false;
return false;
return true;
}
{
int rc;
/*
* Allocate semaphore handle.
*/
struct RTSEMMUTEXINTERNAL *pIntMutexSem = (struct RTSEMMUTEXINTERNAL *)RTMemAlloc(sizeof(struct RTSEMMUTEXINTERNAL));
if (pIntMutexSem)
{
/*
* Create the semaphore.
*/
if (!rc)
{
if (!rc)
{
pIntMutexSem->cNesting = 0;
return VINF_SUCCESS;
}
}
}
else
rc = VERR_NO_MEMORY;
return rc;
}
{
/*
* Validate input.
*/
if (!rtsemMutexValid(MutexSem))
{
return VERR_INVALID_HANDLE;
}
/*
* Try destroy it.
*/
if (rc)
{
return RTErrConvertFromErrno(rc);
}
/*
* Free the memory and be gone.
*/
pIntMutexSem->cNesting = ~0;
return VINF_SUCCESS;
}
{
/*
* Validate input.
*/
if (!rtsemMutexValid(MutexSem))
{
return VERR_INVALID_HANDLE;
}
/*
* Check if nested request.
*/
&& pIntMutexSem->cNesting > 0)
{
pIntMutexSem->cNesting++;
return VINF_SUCCESS;
}
/*
* Lock it.
*/
if (cMillies == RT_INDEFINITE_WAIT)
{
/* take mutex */
if (rc)
{
return RTErrConvertFromErrno(rc);
}
}
else
{
/*
* Get current time and calc end of wait time.
*/
if (cMillies != 0)
{
{
}
}
/* take mutex */
if (rc)
{
return RTErrConvertFromErrno(rc);
}
}
/*
* Set the owner and nesting.
*/
return VINF_SUCCESS;
}
{
/* EINTR isn't returned by the wait functions we're using. */
}
{
/*
* Validate input.
*/
if (!rtsemMutexValid(MutexSem))
{
return VERR_INVALID_HANDLE;
}
/*
* Check if nested.
*/
{
AssertMsgFailed(("Not owner of mutex %p!! Self=%08x Owner=%08x cNesting=%d\n",
return VERR_NOT_OWNER;
}
/*
* If nested we'll just pop a nesting.
*/
{
pIntMutexSem->cNesting--;
return VINF_SUCCESS;
}
/*
* Clear the state. (cNesting == 1)
*/
/*
* Unlock mutex semaphore.
*/
if (rc)
{
return RTErrConvertFromErrno(rc);
}
return VINF_SUCCESS;
}
/**
* Validate a read-write semaphore handle passed to one of the interface.
*
* @returns true if valid.
* @returns false if invalid.
* @param pIntRWSem Pointer to the read-write semaphore to validate.
*/
{
return false;
return false;
return true;
}
{
int rc;
/*
* Allocate handle.
*/
struct RTSEMRWINTERNAL *pIntRWSem = (struct RTSEMRWINTERNAL *)RTMemAlloc(sizeof(struct RTSEMRWINTERNAL));
if (pIntRWSem)
{
/*
* Create the rwlock.
*/
if (!rc)
{
if (!rc)
{
return VINF_SUCCESS;
}
}
}
else
rc = VERR_NO_MEMORY;
return rc;
}
{
/*
* Validate input.
*/
if (!rtsemRWValid(RWSem))
{
return VERR_INVALID_HANDLE;
}
/*
* Try destroy it.
*/
if (!rc)
{
rc = VINF_SUCCESS;
}
else
{
}
return rc;
}
{
/*
* Validate input.
*/
if (!rtsemRWValid(RWSem))
{
return VERR_INVALID_HANDLE;
}
/*
* Try lock it.
*/
if (cMillies == RT_INDEFINITE_WAIT)
{
/* take rwlock */
if (rc)
{
return RTErrConvertFromErrno(rc);
}
}
else
{
/*
* Get current time and calc end of wait time.
*/
if (cMillies != 0)
{
{
}
}
/* take rwlock */
if (rc)
{
return RTErrConvertFromErrno(rc);
}
}
return VINF_SUCCESS;
}
{
/* EINTR isn't returned by the wait functions we're using. */
}
{
/*
* Validate input.
*/
if (!rtsemRWValid(RWSem))
{
return VERR_INVALID_HANDLE;
}
/*
* Try unlock it.
*/
{
return VERR_NOT_OWNER;
}
if (rc)
{
return RTErrConvertFromErrno(rc);
}
return VINF_SUCCESS;
}
{
/*
* Validate input.
*/
if (!rtsemRWValid(RWSem))
{
return VERR_INVALID_HANDLE;
}
/*
* Try lock it.
*/
if (cMillies == RT_INDEFINITE_WAIT)
{
/* take rwlock */
if (rc)
{
return RTErrConvertFromErrno(rc);
}
}
else
{
/*
* Get current time and calc end of wait time.
*/
if (cMillies != 0)
{
{
}
}
/* take rwlock */
if (rc)
{
return RTErrConvertFromErrno(rc);
}
}
return VINF_SUCCESS;
}
{
/* EINTR isn't returned by the wait functions we're using. */
}
{
/*
* Validate input.
*/
if (!rtsemRWValid(RWSem))
{
return VERR_INVALID_HANDLE;
}
/*
* Try unlock it.
*/
{
AssertMsgFailed(("Not Write owner!\n"));
return VERR_NOT_OWNER;
}
/*
* Try unlock it.
*/
AssertMsg(sizeof(pthread_t) == sizeof(void *), ("pthread_t is not the size of a pointer but %d bytes\n", sizeof(pthread_t)));
if (rc)
{
return RTErrConvertFromErrno(rc);
}
return VINF_SUCCESS;
}