blob: 4c619c240c9eb884291c924b3b7b6330419f55c8 [file] [log] [blame]
//
// Copyright 2005 The Android Open Source Project
//
// Shared memory interface.
//
#include "Shmem.h"
#include "utils/Log.h"
#if defined(HAVE_MACOSX_IPC) || defined(HAVE_ANDROID_IPC)
# include <sys/mman.h>
# include <fcntl.h>
# include <unistd.h>
#elif defined(HAVE_SYSV_IPC)
# include <sys/types.h>
# include <sys/ipc.h>
# include <sys/shm.h>
#elif defined(HAVE_WIN32_IPC)
# include <windows.h>
#else
# error "unknown shm config"
#endif
#include <errno.h>
#include <assert.h>
using namespace android;
#if defined(HAVE_MACOSX_IPC) || defined(HAVE_ANDROID_IPC)
/*
* SysV IPC under Mac OS X seems to have problems. It works fine on
* some machines but totally fails on others. We're working around it
* here by using mmap().
*/
#define kInvalidHandle ((unsigned long)-1)
static const char* kShmemFile = "/tmp/android-";
/*
* Constructor. Just set up the fields.
*/
Shmem::Shmem(void)
: mHandle(kInvalidHandle), mAddr(MAP_FAILED), mLength(-1), mCreator(false),
mKey(-1)
{
}
/*
* Destructor. Detach and, if we created it, mark the segment for
* destruction.
*/
Shmem::~Shmem(void)
{
if (mAddr != MAP_FAILED)
munmap(mAddr, mLength);
if ((long)mHandle >= 0) {
close(mHandle);
if (mCreator) {
char nameBuf[64];
int cc;
snprintf(nameBuf, sizeof(nameBuf), "%s%d", kShmemFile, mKey);
cc = unlink(nameBuf);
if (cc != 0) {
LOG(LOG_WARN, "shmem", "Couldn't clean up '%s'\n", nameBuf);
/* oh well */
}
}
}
}
/*
* Create the segment and attach ourselves to it.
*/
bool Shmem::create(int key, long size, bool deleteExisting)
{
char nameBuf[64];
int fd, cc;
snprintf(nameBuf, sizeof(nameBuf), "%s%d", kShmemFile, key);
if (deleteExisting) {
cc = unlink(nameBuf);
if (cc != 0 && errno != ENOENT) {
LOG(LOG_ERROR, "shmem", "Failed to remove old map file '%s'\n",
nameBuf);
return false;
}
}
fd = open(nameBuf, O_CREAT|O_EXCL|O_RDWR, 0600);
if (fd < 0) {
LOG(LOG_ERROR, "shmem", "Unable to create map file '%s' (errno=%d)\n",
nameBuf, errno);
return false;
}
/*
* Set the file size by seeking and writing.
*/
if (ftruncate(fd, size) == -1) {
LOG(LOG_ERROR, "shmem", "Unable to set file size in '%s' (errno=%d)\n",
nameBuf, errno);
close(fd);
return false;
}
mAddr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (mAddr == MAP_FAILED) {
LOG(LOG_ERROR, "shmem", "mmap failed (errno=%d)\n", errno);
close(fd);
return false;
}
mHandle = fd;
mLength = size;
mCreator = true;
mKey = key;
/* done with shmem, create the associated semaphore */
if (!mSem.create(key, 1, true)) {
LOG(LOG_ERROR, "shmem",
"Failed creating semaphore for Shmem (key=%d)\n", key);
return false;
}
return true;
}
/*
* Attach ourselves to an existing segment.
*/
bool Shmem::attach(int key)
{
char nameBuf[64];
int fd;
snprintf(nameBuf, sizeof(nameBuf), "%s%d", kShmemFile, key);
fd = open(nameBuf, O_RDWR, 0600);
if (fd < 0) {
LOG(LOG_ERROR, "shmem", "Unable to open map file '%s' (errno=%d)\n",
nameBuf, errno);
return false;
}
off_t len;
len = lseek(fd, 0, SEEK_END);
if (len == (off_t) -1) {
LOG(LOG_ERROR, "shmem",
"Could not determine file size of '%s' (errno=%d)\n",
nameBuf, errno);
close(fd);
return false;
}
mAddr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (mAddr == MAP_FAILED) {
LOG(LOG_ERROR, "shmem", "mmap failed (errno=%d)\n", errno);
close(fd);
return false;
}
mHandle = fd;
mLength = len;
assert(mCreator == false);
mKey = key;
/* done with shmem, attach to associated semaphore */
if (!mSem.attach(key)) {
LOG(LOG_ERROR, "shmem",
"Failed to attach to semaphore for Shmem (key=%d)\n", key);
return false;
}
return true;
}
/*
* Get address.
*/
void* Shmem::getAddr(void)
{
assert(mAddr != MAP_FAILED);
return mAddr;
}
/*
* Return the length of the segment.
*
* Returns -1 on failure.
*/
long Shmem::getLength(void)
{
if (mLength >= 0)
return mLength;
// we should always have it by now
assert(false);
return -1;
}
#elif defined(HAVE_SYSV_IPC) // ----------------------------------------------
/*
* SysV-style IPC. The SysV shared memory API is fairly annoying to
* deal with, but it's present on many UNIX-like systems.
*/
#define kInvalidHandle ((unsigned long)-1)
/*
* Constructor. Just set up the fields.
*/
Shmem::Shmem(void)
: mHandle(kInvalidHandle), mAddr(NULL), mLength(-1), mCreator(false),
mKey(-1)
{
}
/*
* Destructor. Detach and, if we created it, mark the segment for
* destruction.
*/
Shmem::~Shmem(void)
{
int cc;
//LOG(LOG_DEBUG, "shmem", "~Shmem(handle=%ld creator=%d)",
// mHandle, mCreator);
if (mAddr != NULL)
cc = shmdt(mAddr);
if (mCreator && mHandle != kInvalidHandle) {
cc = shmctl((int) mHandle, IPC_RMID, NULL);
if (cc != 0) {
LOG(LOG_WARN, "shmem",
"Destructor failed to remove shmid=%ld (errno=%d)\n",
mHandle, errno);
}
}
}
/*
* Create the segment and attach ourselves to it.
*/
bool Shmem::create(int key, long size, bool deleteExisting)
{
int shmid, cc;
if (deleteExisting) {
shmid = shmget(key, size, 0);
if (shmid != -1) {
LOG(LOG_DEBUG, "shmem",
"Key %d exists (shmid=%d), marking for destroy", key, shmid);
cc = shmctl(shmid, IPC_RMID, NULL);
if (cc != 0) {
LOG(LOG_ERROR, "shmem",
"Failed to remove key=%d shmid=%d (errno=%d)\n",
key, shmid, errno);
return false; // IPC_CREAT | IPC_EXCL will fail, so bail now
} else {
LOG(LOG_DEBUG, "shmem",
"Removed previous segment with key=%d\n", key);
}
}
}
shmid = shmget(key, size, 0600 | IPC_CREAT | IPC_EXCL);
if (shmid == -1) {
LOG(LOG_ERROR, "shmem", "Failed to create key=%d (errno=%d)\n",
key, errno);
return false;
}
mHandle = shmid;
mCreator = true;
mKey = key;
void* addr = shmat(shmid, NULL, 0);
if (addr == (void*) -1) {
LOG(LOG_ERROR, "shmem",
"Could not attach to key=%d shmid=%d (errno=%d)\n",
key, shmid, errno);
return false;
}
mAddr = addr;
mLength = size;
/* done with shmem, create the associated semaphore */
if (!mSem.create(key, 1, true)) {
LOG(LOG_ERROR, "shmem",
"Failed creating semaphore for Shmem (key=%d)\n", key);
return false;
}
return true;
}
/*
* Attach ourselves to an existing segment.
*/
bool Shmem::attach(int key)
{
int shmid;
shmid = shmget(key, 0, 0);
if (shmid == -1) {
LOG(LOG_ERROR, "shmem", "Failed to find key=%d\n", key);
return false;
}
mHandle = shmid;
assert(mCreator == false);
mKey = key;
void* addr = shmat(shmid, NULL, 0);
if (addr == (void*) -1) {
LOG(LOG_ERROR, "shmem", "Could not attach to key=%d shmid=%d\n",
key, shmid);
return false;
}
mAddr = addr;
/* done with shmem, attach to associated semaphore */
if (!mSem.attach(key)) {
LOG(LOG_ERROR, "shmem",
"Failed to attach to semaphore for Shmem (key=%d)\n", key);
return false;
}
return true;
}
/*
* Get address.
*/
void* Shmem::getAddr(void)
{
assert(mAddr != NULL);
return mAddr;
}
/*
* Return the length of the segment.
*
* Returns -1 on failure.
*/
long Shmem::getLength(void)
{
if (mLength >= 0)
return mLength;
assert(mHandle != kInvalidHandle);
struct shmid_ds shmids;
int cc;
cc = shmctl((int) mHandle, IPC_STAT, &shmids);
if (cc != 0) {
LOG(LOG_ERROR, "shmem", "Could not IPC_STAT shmid=%ld\n", mHandle);
return -1;
}
mLength = shmids.shm_segsz; // save a copy to avoid future lookups
return mLength;
}
#elif defined(HAVE_WIN32_IPC) // ---------------------------------------------
/*
* Win32 shared memory implementation.
*
* Shared memory is implemented as an "anonymous" file mapping, using the
* memory-mapped I/O interfaces.
*/
static const char* kShmemStr = "android-shmem-";
/*
* Constructor. Just set up the fields.
*/
Shmem::Shmem(void)
: mHandle((unsigned long) INVALID_HANDLE_VALUE),
mAddr(NULL), mLength(-1), mCreator(false), mKey(-1)
{
}
/*
* Destructor. The Win32 API doesn't require a distinction between
* the "creator" and other mappers.
*/
Shmem::~Shmem(void)
{
LOG(LOG_DEBUG, "shmem", "~Shmem(handle=%ld creator=%d)",
mHandle, mCreator);
if (mAddr != NULL)
UnmapViewOfFile(mAddr);
if (mHandle != (unsigned long) INVALID_HANDLE_VALUE)
CloseHandle((HANDLE) mHandle);
}
/*
* Create the segment and map it.
*/
bool Shmem::create(int key, long size, bool deleteExisting)
{
char keyBuf[64];
HANDLE hMapFile;
snprintf(keyBuf, sizeof(keyBuf), "%s%d", kShmemStr, key);
hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE, // use paging file, not actual file
NULL, // default security
PAGE_READWRITE, // read/write access
0, // max size; no need to cap
size, // min size
keyBuf); // mapping name
if (hMapFile == NULL || hMapFile == INVALID_HANDLE_VALUE) {
LOG(LOG_ERROR, "shmem",
"Could not create mapping object '%s' (err=%ld)\n",
keyBuf, GetLastError());
return false;
}
mHandle = (unsigned long) hMapFile;
mCreator = true;
mKey = key;
mAddr = MapViewOfFile(
hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write
0, // offset (hi)
0, // offset (lo)
size); // #of bytes to map
if (mAddr == NULL) {
LOG(LOG_ERROR, "shmem", "Could not map shared area (err=%ld)\n",
GetLastError());
return false;
}
mLength = size;
/* done with shmem, create the associated semaphore */
if (!mSem.create(key, 1, true)) {
LOG(LOG_ERROR, "shmem",
"Failed creating semaphore for Shmem (key=%d)\n", key);
return false;
}
return true;
}
/*
* Attach ourselves to an existing segment.
*/
bool Shmem::attach(int key)
{
char keyBuf[64];
HANDLE hMapFile;
snprintf(keyBuf, sizeof(keyBuf), "%s%d", kShmemStr, key);
hMapFile = OpenFileMapping(
FILE_MAP_ALL_ACCESS, // read/write
FALSE, // don't let kids inherit handle
keyBuf); // mapping name
if (hMapFile == NULL) {
LOG(LOG_ERROR, "shmem",
"Could not open mapping object '%s' (err=%ld)\n",
keyBuf, GetLastError());
return false;
}
mHandle = (unsigned long) hMapFile;
assert(mCreator == false);
mKey = key;
mAddr = MapViewOfFile(
hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write
0, // offset (hi)
0, // offset (lo)
0); // #of bytes to map
if (mAddr == NULL) {
LOG(LOG_ERROR, "shmem", "Could not map shared area (err=%ld)\n",
GetLastError());
return false;
}
/* done with shmem, attach to associated semaphore */
if (!mSem.attach(key)) {
LOG(LOG_ERROR, "shmem",
"Failed to attach to semaphore for Shmem (key=%d)\n", key);
return false;
}
return true;
}
/*
* Get address.
*/
void* Shmem::getAddr(void)
{
assert(mAddr != NULL);
return mAddr;
}
/*
* Get the length of the segment.
*/
long Shmem::getLength(void)
{
SIZE_T size;
MEMORY_BASIC_INFORMATION mbInfo;
if (mLength >= 0)
return mLength;
assert(mAddr != NULL);
size = VirtualQuery(mAddr, &mbInfo, sizeof(mbInfo));
if (size == 0) {
LOG(LOG_WARN, "shmem", "VirtualQuery returned no data\n");
return -1;
}
mLength = mbInfo.RegionSize;
return mLength;
}
#endif // --------------------------------------------------------------------
/*
* Semaphore operations.
*/
void Shmem::lock(void)
{
mSem.acquire();
}
void Shmem::unlock(void)
{
mSem.release();
}
bool Shmem::tryLock(void)
{
return mSem.tryAcquire();
}