559 lines
13 KiB
C++
559 lines
13 KiB
C++
//
|
|
// 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();
|
|
}
|
|
|