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