| // |
| // 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(); |
| } |
| |