//
// Copyright 2005 The Android Open Source Project
//
// Inter-process semaphores.
//
#include "Semaphore.h"
#if defined(HAVE_MACOSX_IPC)
# include <semaphore.h>
#elif defined(HAVE_SYSV_IPC)
# include <sys/types.h>
# include <sys/ipc.h>
# include <sys/sem.h>
#elif defined(HAVE_WIN32_IPC)
# include <windows.h>
#elif defined(HAVE_ANDROID_IPC)
// not yet
#else
# error "unknown sem config"
#endif
#include <utils/Log.h>
#include <errno.h>
#include <assert.h>
using namespace android;
#if defined(HAVE_ANDROID_IPC) // ----------------------------------------------
Semaphore::Semaphore(void)
: mHandle(0), mCreator(false), mKey(-1)
{}
Semaphore::~Semaphore(void)
{}
bool Semaphore::create(int key, int initialValue, bool deleteExisting)
{
return false;
}
bool Semaphore::attach(int key)
{
return false;
}
void Semaphore::acquire(void)
{}
void Semaphore::release(void)
{}
bool Semaphore::tryAcquire(void)
{
return false;
}
#elif defined(HAVE_MACOSX_IPC) // ---------------------------------------------
/*
* The SysV semaphores don't work on all of our machines. The POSIX
* named semaphores seem to work better.
*/
#define kInvalidHandle SEM_FAILED
static const char* kSemStr = "/tmp/android-sem-";
/*
* Constructor. Just init fields.
*/
Semaphore::Semaphore(void)
: mHandle((unsigned long) kInvalidHandle), mCreator(false), mKey(-1)
{
}
/*
* Destructor. If we created the semaphore, destroy it.
*/
Semaphore::~Semaphore(void)
{
LOG(LOG_VERBOSE, "sem", "~Semaphore(handle=%ld creator=%d)\n",
mHandle, mCreator);
if (mHandle != (unsigned long) kInvalidHandle) {
sem_close((sem_t*) mHandle);
if (mCreator) {
char nameBuf[64];
int cc;
snprintf(nameBuf, sizeof(nameBuf), "%s%d", kSemStr, mKey);
cc = sem_unlink(nameBuf);
if (cc != 0) {
LOG(LOG_ERROR, "sem",
"Failed to remove sem '%s' (errno=%d)\n", nameBuf, errno);
}
}
}
}
/*
* Create the semaphore.
*/
bool Semaphore::create(int key, int initialValue, bool deleteExisting)
{
int cc;
char nameBuf[64];
snprintf(nameBuf, sizeof(nameBuf), "%s%d", kSemStr, key);
if (deleteExisting) {
cc = sem_unlink(nameBuf);
if (cc != 0 && errno != ENOENT) {
LOG(LOG_WARN, "sem", "Warning: failed to remove sem '%s'\n",
nameBuf);
/* keep going? */
}
}
/* create and set initial value */
sem_t* semPtr;
semPtr = sem_open(nameBuf, O_CREAT | O_EXCL, 0666, 1);
if (semPtr == (sem_t*)SEM_FAILED) {
LOG(LOG_ERROR, "sem",
"ERROR: sem_open failed to create '%s' (errno=%d)\n",
nameBuf, errno);
return false;
}
mHandle = (unsigned long) semPtr;
mCreator = true;
mKey = key;
return true;
}
/*
* Attach to an existing semaphore.
*/
bool Semaphore::attach(int key)
{
char nameBuf[64];
snprintf(nameBuf, sizeof(nameBuf), "%s%d", kSemStr, key);
sem_t* semPtr;
semPtr = sem_open(nameBuf, 0, 0666, 0);
if (semPtr == (sem_t*) SEM_FAILED) {
LOG(LOG_ERROR, "sem",
"ERROR: sem_open failed to attach to '%s' (errno=%d)\n",
nameBuf, errno);
return false;
}
mHandle = (unsigned long) semPtr;
assert(mCreator == false);
mKey = key;
return true;
}
/*
* Acquire or release the semaphore.
*/
void Semaphore::acquire(void)
{
int cc = sem_wait((sem_t*) mHandle);
if (cc != 0)
LOG(LOG_WARN, "sem", "acquire failed (errno=%d)\n", errno);
}
void Semaphore::release(void)
{
int cc = sem_post((sem_t*) mHandle);
if (cc != 0)
LOG(LOG_WARN, "sem", "release failed (errno=%d)\n", errno);
}
bool Semaphore::tryAcquire(void)
{
int cc = sem_trywait((sem_t*) mHandle);
if (cc != 0) {
if (errno != EAGAIN)
LOG(LOG_WARN, "sem", "tryAcquire failed (errno=%d)\n", errno);
return false;
}
return true;
}
#elif defined(HAVE_SYSV_IPC) // -----------------------------------------------
/*
* Basic SysV semaphore stuff.
*/
#define kInvalidHandle ((unsigned long)-1)
#if defined(_SEM_SEMUN_UNDEFINED)
/* according to X/OPEN we have to define it ourselves */
union semun {
int val; /* value for SETVAL */
struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* array for GETALL, SETALL */
/* Linux specific part: */
struct seminfo *__buf; /* buffer for IPC_INFO */
};
#endif
/*
* Constructor. Just init fields.
*/
Semaphore::Semaphore(void)
: mHandle(kInvalidHandle), mCreator(false)
{
}
/*
* Destructor. If we created the semaphore, destroy it.
*/
Semaphore::~Semaphore(void)
{
LOG(LOG_VERBOSE, "sem", "~Semaphore(handle=%ld creator=%d)\n",
mHandle, mCreator);
if (mCreator && mHandle != kInvalidHandle) {
int cc;
cc = semctl((int) mHandle, 0, IPC_RMID);
if (cc != 0) {
LOG(LOG_WARN, "sem",
"Destructor failed to destroy key=%ld\n", mHandle);
}
}
}
/*
* Create the semaphore.
*/
bool Semaphore::create(int key, int initialValue, bool deleteExisting)
{
int semid, cc;
if (deleteExisting) {
semid = semget(key, 1, 0);
if (semid != -1) {
LOG(LOG_DEBUG, "sem", "Key %d exists (semid=%d), removing\n",
key, semid);
cc = semctl(semid, 0, IPC_RMID);
if (cc != 0) {
LOG(LOG_ERROR, "sem", "Failed to remove key=%d semid=%d\n",
key, semid);
return false;
} else {
LOG(LOG_DEBUG, "sem",
"Removed previous semaphore with key=%d\n", key);
}
}
}
semid = semget(key, 1, 0600 | IPC_CREAT | IPC_EXCL);
if (semid == -1) {
LOG(LOG_ERROR, "sem", "Failed to create key=%d (errno=%d)\n",
key, errno);
return false;
}
mHandle = semid;
mCreator = true;
mKey = key;
/*
* Set initial value.
*/
union semun init;
init.val = initialValue;
cc = semctl(semid, 0, SETVAL, init);
if (cc == -1) {
LOG(LOG_ERROR, "sem",
"Unable to initialize semaphore, key=%d iv=%d (errno=%d)\n",
key, initialValue, errno);
return false;
}
return true;
}
/*
* Attach to an existing semaphore.
*/
bool Semaphore::attach(int key)
{
int semid;
semid = semget(key, 0, 0);
if (semid == -1) {
LOG(LOG_ERROR, "sem", "Failed to find key=%d\n", key);
return false;
}
mHandle = semid;
assert(mCreator == false);
mKey = key;
return true;
}
/*
* Acquire or release the semaphore.
*/
void Semaphore::acquire(void)
{
assert(mHandle != kInvalidHandle);
adjust(-1, true);
}
void Semaphore::release(void)
{
assert(mHandle != kInvalidHandle);
adjust(1, true);
}
bool Semaphore::tryAcquire(void)
{
assert(mHandle != kInvalidHandle);
return adjust(-1, false);
}
/*
* Do the actual semaphore manipulation.
*
* The semaphore's value indicates the number of free resources. Pass
* in a negative value for "adj" to acquire resources, or a positive
* value to free resources.
*
* Returns true on success, false on failure.
*/
bool Semaphore::adjust(int adj, bool wait)
{
struct sembuf op;
int cc;
op.sem_num = 0;
op.sem_op = adj;
op.sem_flg = SEM_UNDO;
if (!wait)
op.sem_flg |= IPC_NOWAIT;
cc = semop((int) mHandle, &op, 1);
if (cc != 0) {
if (wait || errno != EAGAIN) {
LOG(LOG_WARN, "sem",
"semaphore adjust by %d failed for semid=%ld (errno=%d)\n",
adj, mHandle, errno);
}
return false;
}
//LOG(LOG_VERBOSE, "sem",
// "adjusted semaphore by %d (semid=%ld)\n", adj, mHandle);
return true;
}
#elif defined(HAVE_WIN32_IPC) // ----------------------------------------------
/*
* Win32 semaphore implementation.
*
* Pretty straightforward.
*/
static const char* kSemStr = "android-sem-";
/*
* Constructor. Just init fields.
*/
Semaphore::Semaphore(void)
: mHandle((unsigned long) INVALID_HANDLE_VALUE), mCreator(false)
{
}
/*
* Destructor. Just close the semaphore handle.
*/
Semaphore::~Semaphore(void)
{
LOG(LOG_DEBUG, "sem", "~Semaphore(handle=%ld creator=%d)\n",
mHandle, mCreator);
if (mHandle != (unsigned long) INVALID_HANDLE_VALUE)
CloseHandle((HANDLE) mHandle);
}
/*
* Create the semaphore.
*/
bool Semaphore::create(int key, int initialValue, bool deleteExisting)
{
char keyBuf[64];
HANDLE hSem;
long max;
snprintf(keyBuf, sizeof(keyBuf), "%s%d", kSemStr, key);
if (initialValue == 0)
max = 1;
else
max = initialValue;
hSem = CreateSemaphore(
NULL, // security attributes
initialValue, // initial count
max, // max count, must be >= initial
keyBuf); // object name
if (hSem == NULL) {
DWORD err = GetLastError();
if (err == ERROR_ALREADY_EXISTS) {
LOG(LOG_ERROR, "sem", "Semaphore '%s' already exists\n", keyBuf);
} else {
LOG(LOG_ERROR, "sem", "CreateSemaphore(%s) failed (err=%ld)\n",
keyBuf, err);
}
return false;
}
mHandle = (unsigned long) hSem;
mCreator = true;
mKey = key;
//LOG(LOG_DEBUG, "sem", "Semaphore '%s' created (handle=0x%08lx)\n",
// keyBuf, mHandle);
return true;
}
/*
* Attach to an existing semaphore.
*/
bool Semaphore::attach(int key)
{
char keyBuf[64];
HANDLE hSem;
snprintf(keyBuf, sizeof(keyBuf), "%s%d", kSemStr, key);
hSem = OpenSemaphore(
//SEMAPHORE_MODIFY_STATE, // mostly-full access
SEMAPHORE_ALL_ACCESS, // full access
FALSE, // don't let kids inherit handle
keyBuf); // object name
if (hSem == NULL) {
LOG(LOG_ERROR, "sem", "OpenSemaphore(%s) failed (err=%ld)\n",
keyBuf, GetLastError());
return false;
}
mHandle = (unsigned long) hSem;
assert(mCreator == false);
mKey = key;
return true;
}
/*
* Acquire or release the semaphore.
*/
void Semaphore::acquire(void)
{
DWORD result;
assert(mHandle != (unsigned long) INVALID_HANDLE_VALUE);
result = WaitForSingleObject((HANDLE) mHandle, INFINITE);
if (result != WAIT_OBJECT_0) {
LOG(LOG_WARN, "sem",
"WaitForSingleObject(INF) on semaphore returned %ld (err=%ld)\n",
result, GetLastError());
}
}
void Semaphore::release(void)
{
DWORD result;
assert(mHandle != (unsigned long) INVALID_HANDLE_VALUE);
result = ReleaseSemaphore((HANDLE) mHandle, 1, NULL); // incr by 1
if (result == 0) {
LOG(LOG_WARN, "sem", "ReleaseSemaphore failed (err=%ld)\n",
GetLastError());
}
}
bool Semaphore::tryAcquire(void)
{
DWORD result;
assert(mHandle != (unsigned long) INVALID_HANDLE_VALUE);
result = WaitForSingleObject((HANDLE) mHandle, 0);
if (result == WAIT_OBJECT_0)
return true; // grabbed it
else if (result == WAIT_TIMEOUT)
return false; // not available
else if (result == WAIT_FAILED) {
LOG(LOG_WARN, "sem", "WaitForSingleObject(0) on sem failed (err=%ld)\n",
GetLastError());
return false;
} else {
LOG(LOG_WARN, "sem",
"WaitForSingleObject(0) on sem returned %ld (err=%ld)\n",
result, GetLastError());
return false;
}
}
#endif // ---------------------------------------------------------------------