/* * 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 #include #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)); }