/* * 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 "SkPurgeableMemoryBlock.h" #include "android/ashmem.h" #include #include bool SkPurgeableMemoryBlock::IsSupported() { return true; } #ifdef SK_DEBUG bool SkPurgeableMemoryBlock::PlatformSupportsPurgingAllUnpinnedBlocks() { return false; } bool SkPurgeableMemoryBlock::PurgeAllUnpinnedBlocks() { return false; } bool SkPurgeableMemoryBlock::purge() { SkASSERT(!fPinned); if (-1 != fFD) { ashmem_purge_all_caches(fFD); return true; } else { return false; } } #endif // ashmem likes lengths on page boundaries. static size_t round_to_page_size(size_t size) { const size_t mask = getpagesize() - 1; size_t newSize = (size + mask) & ~mask; return newSize; } SkPurgeableMemoryBlock::SkPurgeableMemoryBlock(size_t size) : fAddr(NULL) , fSize(round_to_page_size(size)) , fPinned(false) , fFD(-1) { } SkPurgeableMemoryBlock::~SkPurgeableMemoryBlock() { if (-1 != fFD) { munmap(fAddr, fSize); close(fFD); } } void* SkPurgeableMemoryBlock::pin(SkPurgeableMemoryBlock::PinResult* pinResult) { SkASSERT(!fPinned); if (-1 == fFD) { int fd = ashmem_create_region(NULL, fSize); if (-1 == fd) { SkDebugf("ashmem_create_region failed\n"); return NULL; } int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE); if (err != 0) { SkDebugf("ashmem_set_prot_region failed\n"); close(fd); return NULL; } void* addr = mmap(NULL, fSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (-1 == (long) addr) { SkDebugf("mmap failed\n"); close(fd); return NULL; } fAddr = addr; fFD = fd; (void) ashmem_pin_region(fd, 0, 0); *pinResult = kUninitialized_PinResult; fPinned = true; } else { int pin = ashmem_pin_region(fFD, 0, 0); if (ASHMEM_NOT_PURGED == pin) { fPinned = true; *pinResult = kRetained_PinResult; } else if (ASHMEM_WAS_PURGED == pin) { fPinned = true; *pinResult = kUninitialized_PinResult; } else { // Failed. munmap(fAddr, fSize); close(fFD); fFD = -1; fAddr = NULL; } } return fAddr; } void SkPurgeableMemoryBlock::unpin() { if (-1 != fFD) { ashmem_unpin_region(fFD, 0, 0); fPinned = false; } }