Home | History | Annotate | Download | only in ports
      1 /*
      2  * Copyright 2013 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include <unistd.h>
      9 #include <sys/mman.h>
     10 #include "SkDiscardableMemory.h"
     11 #include "SkTypes.h"
     12 #include "android/ashmem.h"
     13 
     14 ////////////////////////////////////////////////////////////////////////////////
     15 namespace {
     16 /**
     17  *  DiscardableMemory implementation that uses the Android kernel's
     18  *  ashmem (Android shared memory).
     19  */
     20 class SkAshmemDiscardableMemory : public SkDiscardableMemory {
     21 public:
     22     SkAshmemDiscardableMemory(int fd, void* address, size_t size);
     23     virtual ~SkAshmemDiscardableMemory();
     24     virtual bool lock() SK_OVERRIDE;
     25     virtual void* data() SK_OVERRIDE;
     26     virtual void unlock() SK_OVERRIDE;
     27 private:
     28     bool         fLocked;
     29     int          fFd;
     30     void*        fMemory;
     31     const size_t fSize;
     32 };
     33 
     34 SkAshmemDiscardableMemory::SkAshmemDiscardableMemory(int fd,
     35                                                      void* address,
     36                                                      size_t size)
     37     : fLocked(true)  // Ashmem pages are pinned by default.
     38     , fFd(fd)
     39     , fMemory(address)
     40     , fSize(size) {
     41     SkASSERT(fFd >= 0);
     42     SkASSERT(fMemory != NULL);
     43     SkASSERT(fSize > 0);
     44 }
     45 
     46 SkAshmemDiscardableMemory::~SkAshmemDiscardableMemory() {
     47     SkASSERT(!fLocked);
     48     if (NULL != fMemory) {
     49         munmap(fMemory, fSize);
     50     }
     51     if (fFd != -1) {
     52         close(fFd);
     53     }
     54 }
     55 
     56 bool SkAshmemDiscardableMemory::lock() {
     57     SkASSERT(!fLocked);
     58     if (-1 == fFd) {
     59         fLocked = false;
     60         return false;
     61     }
     62     SkASSERT(fMemory != NULL);
     63     if (fLocked || (ASHMEM_NOT_PURGED == ashmem_pin_region(fFd, 0, 0))) {
     64         fLocked = true;
     65         return true;
     66     } else {
     67         munmap(fMemory, fSize);
     68         fMemory = NULL;
     69 
     70         close(fFd);
     71         fFd = -1;
     72         fLocked = false;
     73         return false;
     74     }
     75 }
     76 
     77 void* SkAshmemDiscardableMemory::data() {
     78     SkASSERT(fLocked);
     79     return fLocked ? fMemory : NULL;
     80 }
     81 
     82 void SkAshmemDiscardableMemory::unlock() {
     83     SkASSERT(fLocked);
     84     if (fLocked && (fFd != -1)) {
     85         ashmem_unpin_region(fFd, 0, 0);
     86     }
     87     fLocked = false;
     88 }
     89 }  // namespace
     90 ////////////////////////////////////////////////////////////////////////////////
     91 
     92 SkDiscardableMemory* SkDiscardableMemory::Create(size_t bytes) {
     93     // ashmem likes lengths on page boundaries.
     94     const size_t mask = getpagesize() - 1;
     95     size_t size = (bytes + mask) & ~mask;
     96 
     97     static const char name[] = "Skia_Ashmem_Discardable_Memory";
     98     int fd = ashmem_create_region(name, size);
     99     if (fd < 0) {
    100         return NULL;
    101     }
    102     if (0 != ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE)) {
    103         close(fd);
    104         return NULL;
    105     }
    106     void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
    107     if ((MAP_FAILED == addr) || (NULL == addr)) {
    108         close(fd);
    109         return NULL;
    110     }
    111 
    112     return SkNEW_ARGS(SkAshmemDiscardableMemory, (fd, addr, size));
    113 }
    114