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