Index: src/ports/SkDiscardableMemory_ashmem.cpp |
diff --git a/src/ports/SkDiscardableMemory_ashmem.cpp b/src/ports/SkDiscardableMemory_ashmem.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6f8684e31c91a375d306793fb96845dd3c1255df |
--- /dev/null |
+++ b/src/ports/SkDiscardableMemory_ashmem.cpp |
@@ -0,0 +1,114 @@ |
+/* |
+ * Copyright 2013 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+#include <unistd.h> |
+#include <sys/mman.h> |
+#include "SkDiscardableMemory.h" |
+#include "SkTypes.h" |
+#include "android/ashmem.h" |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+namespace { |
+/** |
+ * DiscardableMemory implementation that uses the Android kernel's |
+ * ashmem (Android shared memory). |
+ */ |
+class SkAshmemDiscardableMemory : public SkDiscardableMemory { |
+public: |
+ SkAshmemDiscardableMemory(int fd, void* address, size_t size); |
+ virtual ~SkAshmemDiscardableMemory(); |
+ virtual bool lock() SK_OVERRIDE; |
+ virtual void* data() SK_OVERRIDE; |
+ virtual void unlock() SK_OVERRIDE; |
+private: |
+ bool fLocked; |
+ int fFd; |
+ void* fMemory; |
+ const size_t fSize; |
+}; |
+ |
+SkAshmemDiscardableMemory::SkAshmemDiscardableMemory(int fd, |
+ void* address, |
+ size_t size) |
+ : fLocked(true) // Ashmem pages are pinned by default. |
+ , fFd(fd) |
+ , fMemory(address) |
+ , fSize(size) { |
+ SkASSERT(fFd >= 0); |
+ SkASSERT(fMemory != NULL); |
+ SkASSERT(fSize > 0); |
+} |
+ |
+SkAshmemDiscardableMemory::~SkAshmemDiscardableMemory() { |
+ SkASSERT(!fLocked); |
+ if (NULL != fMemory) { |
+ munmap(fMemory, fSize); |
+ } |
+ if (fFd != -1) { |
+ close(fFd); |
+ } |
+} |
+ |
+bool SkAshmemDiscardableMemory::lock() { |
+ SkASSERT(!fLocked); |
+ if (-1 == fFd) { |
+ fLocked = false; |
+ return false; |
+ } |
+ SkASSERT(fMemory != NULL); |
+ if (fLocked || (ASHMEM_NOT_PURGED == ashmem_pin_region(fFd, 0, 0))) { |
+ fLocked = true; |
+ return true; |
+ } else { |
+ munmap(fMemory, fSize); |
+ fMemory = NULL; |
+ |
+ close(fFd); |
+ fFd = -1; |
+ fLocked = false; |
+ return false; |
+ } |
+} |
+ |
+void* SkAshmemDiscardableMemory::data() { |
+ SkASSERT(fLocked); |
+ return fLocked ? fMemory : NULL; |
+} |
+ |
+void SkAshmemDiscardableMemory::unlock() { |
+ SkASSERT(fLocked); |
+ if (fLocked && (fFd != -1)) { |
+ ashmem_unpin_region(fFd, 0, 0); |
+ } |
+ fLocked = false; |
+} |
+} // namespace |
+//////////////////////////////////////////////////////////////////////////////// |
+ |
+SkDiscardableMemory* SkDiscardableMemory::Create(size_t bytes) { |
+ // ashmem likes lengths on page boundaries. |
+ const size_t mask = getpagesize() - 1; |
+ size_t size = (bytes + mask) & ~mask; |
+ |
+ static const char name[] = "Skia_Ashmem_Discardable_Memory"; |
+ int fd = ashmem_create_region(name, size); |
+ if (fd < 0) { |
+ return NULL; |
+ } |
+ if (0 != ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE)) { |
+ close(fd); |
+ return NULL; |
+ } |
+ void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); |
+ if ((MAP_FAILED == addr) || (NULL == addr)) { |
+ close(fd); |
+ return NULL; |
+ } |
+ |
+ return SkNEW_ARGS(SkAshmemDiscardableMemory, (fd, addr, size)); |
+} |
+ |