Implement SkAshmemDiscardableMemory

-   Implement ashmem-backed SkDiscardableMemory subclass:
    This class in only accesible via the SkDiscardableMemory::Create()
    function, which replaces the mock implementation in
    SkDiscardableMemory_none.cpp

-   Added SkDiscardableMemory_ashmem.cpp to the Android port of Skia
    Removed SkDiscardableMemory_none.cpp from the Android port.

-   Added DiscardableMemoryTest.
    Still needs work.

-   SkDiscardablePixelRef Bugfix:
    onLockPixels() now calls SkDELETE on the SkDiscardableMemory pointer
    when it fails to unlock.

-   Improved documentation inside ashmem.h

BUG=
R=scroggo@google.com

Review URL: https://codereview.chromium.org/83563002

git-svn-id: http://skia.googlecode.com/svn/trunk/src@12608 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/ports/SkDiscardableMemory_ashmem.cpp b/ports/SkDiscardableMemory_ashmem.cpp
new file mode 100644
index 0000000..6f8684e
--- /dev/null
+++ b/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));
+}
+
diff --git a/utils/android/ashmem.h b/utils/android/ashmem.h
index 278192b..94ffe1a 100644
--- a/utils/android/ashmem.h
+++ b/utils/android/ashmem.h
@@ -16,6 +16,12 @@
 
 int ashmem_create_region(const char *name, size_t size);
 int ashmem_set_prot_region(int fd, int prot);
+
+/**
+ *   @return ASHMEM_NOT_PURGED if the memory was not purged.
+ *           ASHMEM_WAS_PURGED if the memory was purged.
+ *           -1 on error.
+ */
 int ashmem_pin_region(int fd, size_t offset, size_t len);
 int ashmem_unpin_region(int fd, size_t offset, size_t len);
 int ashmem_get_size_region(int fd);