// // 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 // ---------------------------------------------------------------------