blob: 4c619c240c9eb884291c924b3b7b6330419f55c8 [file] [log] [blame]
The Android Open Source Project52d4c302009-03-03 19:29:09 -08001//
2// Copyright 2005 The Android Open Source Project
3//
4// Shared memory interface.
5//
6#include "Shmem.h"
7#include "utils/Log.h"
8
9#if defined(HAVE_MACOSX_IPC) || defined(HAVE_ANDROID_IPC)
10# include <sys/mman.h>
11# include <fcntl.h>
12# include <unistd.h>
13#elif defined(HAVE_SYSV_IPC)
14# include <sys/types.h>
15# include <sys/ipc.h>
16# include <sys/shm.h>
17#elif defined(HAVE_WIN32_IPC)
18# include <windows.h>
19#else
20# error "unknown shm config"
21#endif
22
23#include <errno.h>
24#include <assert.h>
25
26using namespace android;
27
28
29#if defined(HAVE_MACOSX_IPC) || defined(HAVE_ANDROID_IPC)
30
31/*
32 * SysV IPC under Mac OS X seems to have problems. It works fine on
33 * some machines but totally fails on others. We're working around it
34 * here by using mmap().
35 */
36
37#define kInvalidHandle ((unsigned long)-1)
38
39static const char* kShmemFile = "/tmp/android-";
40
41/*
42 * Constructor. Just set up the fields.
43 */
44Shmem::Shmem(void)
45 : mHandle(kInvalidHandle), mAddr(MAP_FAILED), mLength(-1), mCreator(false),
46 mKey(-1)
47{
48}
49
50/*
51 * Destructor. Detach and, if we created it, mark the segment for
52 * destruction.
53 */
54Shmem::~Shmem(void)
55{
56 if (mAddr != MAP_FAILED)
57 munmap(mAddr, mLength);
58 if ((long)mHandle >= 0) {
59 close(mHandle);
60
61 if (mCreator) {
62 char nameBuf[64];
63 int cc;
64
65 snprintf(nameBuf, sizeof(nameBuf), "%s%d", kShmemFile, mKey);
66 cc = unlink(nameBuf);
67 if (cc != 0) {
68 LOG(LOG_WARN, "shmem", "Couldn't clean up '%s'\n", nameBuf);
69 /* oh well */
70 }
71 }
72 }
73}
74
75/*
76 * Create the segment and attach ourselves to it.
77 */
78bool Shmem::create(int key, long size, bool deleteExisting)
79{
80 char nameBuf[64];
81 int fd, cc;
82
83 snprintf(nameBuf, sizeof(nameBuf), "%s%d", kShmemFile, key);
84
85 if (deleteExisting) {
86 cc = unlink(nameBuf);
87 if (cc != 0 && errno != ENOENT) {
88 LOG(LOG_ERROR, "shmem", "Failed to remove old map file '%s'\n",
89 nameBuf);
90 return false;
91 }
92 }
93
94 fd = open(nameBuf, O_CREAT|O_EXCL|O_RDWR, 0600);
95 if (fd < 0) {
96 LOG(LOG_ERROR, "shmem", "Unable to create map file '%s' (errno=%d)\n",
97 nameBuf, errno);
98 return false;
99 }
100
101 /*
102 * Set the file size by seeking and writing.
103 */
104 if (ftruncate(fd, size) == -1) {
105 LOG(LOG_ERROR, "shmem", "Unable to set file size in '%s' (errno=%d)\n",
106 nameBuf, errno);
107 close(fd);
108 return false;
109 }
110
111 mAddr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
112 if (mAddr == MAP_FAILED) {
113 LOG(LOG_ERROR, "shmem", "mmap failed (errno=%d)\n", errno);
114 close(fd);
115 return false;
116 }
117
118 mHandle = fd;
119 mLength = size;
120 mCreator = true;
121 mKey = key;
122
123 /* done with shmem, create the associated semaphore */
124 if (!mSem.create(key, 1, true)) {
125 LOG(LOG_ERROR, "shmem",
126 "Failed creating semaphore for Shmem (key=%d)\n", key);
127 return false;
128 }
129
130 return true;
131}
132
133/*
134 * Attach ourselves to an existing segment.
135 */
136bool Shmem::attach(int key)
137{
138 char nameBuf[64];
139 int fd;
140
141 snprintf(nameBuf, sizeof(nameBuf), "%s%d", kShmemFile, key);
142 fd = open(nameBuf, O_RDWR, 0600);
143 if (fd < 0) {
144 LOG(LOG_ERROR, "shmem", "Unable to open map file '%s' (errno=%d)\n",
145 nameBuf, errno);
146 return false;
147 }
148
149 off_t len;
150 len = lseek(fd, 0, SEEK_END);
151 if (len == (off_t) -1) {
152 LOG(LOG_ERROR, "shmem",
153 "Could not determine file size of '%s' (errno=%d)\n",
154 nameBuf, errno);
155 close(fd);
156 return false;
157 }
158
159 mAddr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
160 if (mAddr == MAP_FAILED) {
161 LOG(LOG_ERROR, "shmem", "mmap failed (errno=%d)\n", errno);
162 close(fd);
163 return false;
164 }
165
166 mHandle = fd;
167 mLength = len;
168 assert(mCreator == false);
169 mKey = key;
170
171 /* done with shmem, attach to associated semaphore */
172 if (!mSem.attach(key)) {
173 LOG(LOG_ERROR, "shmem",
174 "Failed to attach to semaphore for Shmem (key=%d)\n", key);
175 return false;
176 }
177
178 return true;
179}
180
181/*
182 * Get address.
183 */
184void* Shmem::getAddr(void)
185{
186 assert(mAddr != MAP_FAILED);
187 return mAddr;
188}
189
190/*
191 * Return the length of the segment.
192 *
193 * Returns -1 on failure.
194 */
195long Shmem::getLength(void)
196{
197 if (mLength >= 0)
198 return mLength;
199
200 // we should always have it by now
201 assert(false);
202 return -1;
203}
204
205
206#elif defined(HAVE_SYSV_IPC) // ----------------------------------------------
207
208/*
209 * SysV-style IPC. The SysV shared memory API is fairly annoying to
210 * deal with, but it's present on many UNIX-like systems.
211 */
212
213#define kInvalidHandle ((unsigned long)-1)
214
215/*
216 * Constructor. Just set up the fields.
217 */
218Shmem::Shmem(void)
219 : mHandle(kInvalidHandle), mAddr(NULL), mLength(-1), mCreator(false),
220 mKey(-1)
221{
222}
223
224/*
225 * Destructor. Detach and, if we created it, mark the segment for
226 * destruction.
227 */
228Shmem::~Shmem(void)
229{
230 int cc;
231
232 //LOG(LOG_DEBUG, "shmem", "~Shmem(handle=%ld creator=%d)",
233 // mHandle, mCreator);
234
235 if (mAddr != NULL)
236 cc = shmdt(mAddr);
237
238 if (mCreator && mHandle != kInvalidHandle) {
239 cc = shmctl((int) mHandle, IPC_RMID, NULL);
240 if (cc != 0) {
241 LOG(LOG_WARN, "shmem",
242 "Destructor failed to remove shmid=%ld (errno=%d)\n",
243 mHandle, errno);
244 }
245 }
246}
247
248/*
249 * Create the segment and attach ourselves to it.
250 */
251bool Shmem::create(int key, long size, bool deleteExisting)
252{
253 int shmid, cc;
254
255 if (deleteExisting) {
256 shmid = shmget(key, size, 0);
257 if (shmid != -1) {
258 LOG(LOG_DEBUG, "shmem",
259 "Key %d exists (shmid=%d), marking for destroy", key, shmid);
260 cc = shmctl(shmid, IPC_RMID, NULL);
261 if (cc != 0) {
262 LOG(LOG_ERROR, "shmem",
263 "Failed to remove key=%d shmid=%d (errno=%d)\n",
264 key, shmid, errno);
265 return false; // IPC_CREAT | IPC_EXCL will fail, so bail now
266 } else {
267 LOG(LOG_DEBUG, "shmem",
268 "Removed previous segment with key=%d\n", key);
269 }
270 }
271 }
272
273 shmid = shmget(key, size, 0600 | IPC_CREAT | IPC_EXCL);
274 if (shmid == -1) {
275 LOG(LOG_ERROR, "shmem", "Failed to create key=%d (errno=%d)\n",
276 key, errno);
277 return false;
278 }
279
280 mHandle = shmid;
281 mCreator = true;
282 mKey = key;
283
284 void* addr = shmat(shmid, NULL, 0);
285 if (addr == (void*) -1) {
286 LOG(LOG_ERROR, "shmem",
287 "Could not attach to key=%d shmid=%d (errno=%d)\n",
288 key, shmid, errno);
289 return false;
290 }
291
292 mAddr = addr;
293 mLength = size;
294
295 /* done with shmem, create the associated semaphore */
296 if (!mSem.create(key, 1, true)) {
297 LOG(LOG_ERROR, "shmem",
298 "Failed creating semaphore for Shmem (key=%d)\n", key);
299 return false;
300 }
301
302 return true;
303}
304
305/*
306 * Attach ourselves to an existing segment.
307 */
308bool Shmem::attach(int key)
309{
310 int shmid;
311
312 shmid = shmget(key, 0, 0);
313 if (shmid == -1) {
314 LOG(LOG_ERROR, "shmem", "Failed to find key=%d\n", key);
315 return false;
316 }
317
318 mHandle = shmid;
319 assert(mCreator == false);
320 mKey = key;
321
322 void* addr = shmat(shmid, NULL, 0);
323 if (addr == (void*) -1) {
324 LOG(LOG_ERROR, "shmem", "Could not attach to key=%d shmid=%d\n",
325 key, shmid);
326 return false;
327 }
328
329 mAddr = addr;
330
331 /* done with shmem, attach to associated semaphore */
332 if (!mSem.attach(key)) {
333 LOG(LOG_ERROR, "shmem",
334 "Failed to attach to semaphore for Shmem (key=%d)\n", key);
335 return false;
336 }
337
338 return true;
339}
340
341/*
342 * Get address.
343 */
344void* Shmem::getAddr(void)
345{
346 assert(mAddr != NULL);
347 return mAddr;
348}
349
350/*
351 * Return the length of the segment.
352 *
353 * Returns -1 on failure.
354 */
355long Shmem::getLength(void)
356{
357 if (mLength >= 0)
358 return mLength;
359
360 assert(mHandle != kInvalidHandle);
361
362 struct shmid_ds shmids;
363 int cc;
364
365 cc = shmctl((int) mHandle, IPC_STAT, &shmids);
366 if (cc != 0) {
367 LOG(LOG_ERROR, "shmem", "Could not IPC_STAT shmid=%ld\n", mHandle);
368 return -1;
369 }
370 mLength = shmids.shm_segsz; // save a copy to avoid future lookups
371
372 return mLength;
373}
374
375
376#elif defined(HAVE_WIN32_IPC) // ---------------------------------------------
377
378/*
379 * Win32 shared memory implementation.
380 *
381 * Shared memory is implemented as an "anonymous" file mapping, using the
382 * memory-mapped I/O interfaces.
383 */
384
385static const char* kShmemStr = "android-shmem-";
386
387/*
388 * Constructor. Just set up the fields.
389 */
390Shmem::Shmem(void)
391 : mHandle((unsigned long) INVALID_HANDLE_VALUE),
392 mAddr(NULL), mLength(-1), mCreator(false), mKey(-1)
393{
394}
395
396/*
397 * Destructor. The Win32 API doesn't require a distinction between
398 * the "creator" and other mappers.
399 */
400Shmem::~Shmem(void)
401{
402 LOG(LOG_DEBUG, "shmem", "~Shmem(handle=%ld creator=%d)",
403 mHandle, mCreator);
404
405 if (mAddr != NULL)
406 UnmapViewOfFile(mAddr);
407 if (mHandle != (unsigned long) INVALID_HANDLE_VALUE)
408 CloseHandle((HANDLE) mHandle);
409}
410
411/*
412 * Create the segment and map it.
413 */
414bool Shmem::create(int key, long size, bool deleteExisting)
415{
416 char keyBuf[64];
417 HANDLE hMapFile;
418
419 snprintf(keyBuf, sizeof(keyBuf), "%s%d", kShmemStr, key);
420
421 hMapFile = CreateFileMapping(
422 INVALID_HANDLE_VALUE, // use paging file, not actual file
423 NULL, // default security
424 PAGE_READWRITE, // read/write access
425 0, // max size; no need to cap
426 size, // min size
427 keyBuf); // mapping name
428 if (hMapFile == NULL || hMapFile == INVALID_HANDLE_VALUE) {
429 LOG(LOG_ERROR, "shmem",
430 "Could not create mapping object '%s' (err=%ld)\n",
431 keyBuf, GetLastError());
432 return false;
433 }
434
435 mHandle = (unsigned long) hMapFile;
436 mCreator = true;
437 mKey = key;
438
439 mAddr = MapViewOfFile(
440 hMapFile, // handle to map object
441 FILE_MAP_ALL_ACCESS, // read/write
442 0, // offset (hi)
443 0, // offset (lo)
444 size); // #of bytes to map
445 if (mAddr == NULL) {
446 LOG(LOG_ERROR, "shmem", "Could not map shared area (err=%ld)\n",
447 GetLastError());
448 return false;
449 }
450 mLength = size;
451
452 /* done with shmem, create the associated semaphore */
453 if (!mSem.create(key, 1, true)) {
454 LOG(LOG_ERROR, "shmem",
455 "Failed creating semaphore for Shmem (key=%d)\n", key);
456 return false;
457 }
458
459 return true;
460}
461
462/*
463 * Attach ourselves to an existing segment.
464 */
465bool Shmem::attach(int key)
466{
467 char keyBuf[64];
468 HANDLE hMapFile;
469
470 snprintf(keyBuf, sizeof(keyBuf), "%s%d", kShmemStr, key);
471
472 hMapFile = OpenFileMapping(
473 FILE_MAP_ALL_ACCESS, // read/write
474 FALSE, // don't let kids inherit handle
475 keyBuf); // mapping name
476 if (hMapFile == NULL) {
477 LOG(LOG_ERROR, "shmem",
478 "Could not open mapping object '%s' (err=%ld)\n",
479 keyBuf, GetLastError());
480 return false;
481 }
482
483 mHandle = (unsigned long) hMapFile;
484 assert(mCreator == false);
485 mKey = key;
486
487 mAddr = MapViewOfFile(
488 hMapFile, // handle to map object
489 FILE_MAP_ALL_ACCESS, // read/write
490 0, // offset (hi)
491 0, // offset (lo)
492 0); // #of bytes to map
493 if (mAddr == NULL) {
494 LOG(LOG_ERROR, "shmem", "Could not map shared area (err=%ld)\n",
495 GetLastError());
496 return false;
497 }
498
499 /* done with shmem, attach to associated semaphore */
500 if (!mSem.attach(key)) {
501 LOG(LOG_ERROR, "shmem",
502 "Failed to attach to semaphore for Shmem (key=%d)\n", key);
503 return false;
504 }
505
506 return true;
507}
508
509/*
510 * Get address.
511 */
512void* Shmem::getAddr(void)
513{
514 assert(mAddr != NULL);
515 return mAddr;
516}
517
518/*
519 * Get the length of the segment.
520 */
521long Shmem::getLength(void)
522{
523 SIZE_T size;
524 MEMORY_BASIC_INFORMATION mbInfo;
525
526 if (mLength >= 0)
527 return mLength;
528
529 assert(mAddr != NULL);
530
531 size = VirtualQuery(mAddr, &mbInfo, sizeof(mbInfo));
532 if (size == 0) {
533 LOG(LOG_WARN, "shmem", "VirtualQuery returned no data\n");
534 return -1;
535 }
536
537 mLength = mbInfo.RegionSize;
538 return mLength;
539}
540
541#endif // --------------------------------------------------------------------
542
543/*
544 * Semaphore operations.
545 */
546void Shmem::lock(void)
547{
548 mSem.acquire();
549}
550void Shmem::unlock(void)
551{
552 mSem.release();
553}
554bool Shmem::tryLock(void)
555{
556 return mSem.tryAcquire();
557}
558