blob: a85542271df733e5c2a421f62e4ef9c69a38008e [file] [log] [blame]
/*
* 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 "SkAshmemImageCache.h"
#include "SkThread.h"
#ifdef SK_DEBUG
#include "SkTSearch.h"
#endif
#include "android/ashmem.h"
#include <sys/mman.h>
#include <unistd.h>
SkAshmemImageCache::SkAshmemImageCache() {}
SK_DECLARE_STATIC_MUTEX(gAshmemMutex);
SkAshmemImageCache* SkAshmemImageCache::GetAshmemImageCache() {
SkAutoMutexAcquire ac(&gAshmemMutex);
static SkAshmemImageCache gCache;
return &gCache;
}
#ifdef SK_DEBUG
SkAshmemImageCache::~SkAshmemImageCache() {
SkASSERT(fRecs.count() == 0);
}
#endif
// ashmem likes lengths on page boundaries.
static size_t roundToPageSize(size_t size) {
const size_t mask = getpagesize() - 1;
size_t newSize = (size + mask) & ~mask;
return newSize;
}
void* SkAshmemImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) {
AshmemRec rec;
rec.fSize = roundToPageSize(bytes);
SkAutoMutexAcquire ac(&gAshmemMutex);
rec.fFD = ashmem_create_region(NULL, rec.fSize);
if (-1 == rec.fFD) {
SkDebugf("ashmem_create_region failed\n");
return NULL;
}
int err = ashmem_set_prot_region(rec.fFD, PROT_READ | PROT_WRITE);
if (err != 0) {
SkDebugf("ashmem_set_prot_region failed\n");
close(rec.fFD);
return NULL;
}
rec.fAddr = mmap(NULL, rec.fSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, rec.fFD, 0);
if (-1 == (long) rec.fAddr) {
SkDebugf("mmap failed\n");
close(rec.fFD);
return NULL;
}
(void) ashmem_pin_region(rec.fFD, 0, 0);
#ifdef SK_DEBUG
rec.fPinned = true;
#endif
// In release mode, we do not keep a pointer to this object. It will be destroyed
// either when pinCache returns NULL or when throwAwayCache is called.
AshmemRec* pRec = SkNEW_ARGS(AshmemRec, (rec));
SkASSERT(ID != NULL);
*ID = reinterpret_cast<intptr_t>(pRec);
#ifdef SK_DEBUG
this->appendRec(pRec);
#endif
return rec.fAddr;
}
void* SkAshmemImageCache::pinCache(intptr_t ID) {
SkAutoMutexAcquire ac(&gAshmemMutex);
AshmemRec* rec = reinterpret_cast<AshmemRec*>(ID);
const int fd = rec->fFD;
int pin = ashmem_pin_region(fd, 0, 0);
if (ASHMEM_NOT_PURGED == pin) {
#ifdef SK_DEBUG
rec->fPinned = true;
#endif
return rec->fAddr;
}
// Purged. Remove the associated AshmemRec:
this->removeRec(rec);
ashmem_unpin_region(fd, 0, 0);
return NULL;
}
void SkAshmemImageCache::releaseCache(intptr_t ID) {
SkAutoMutexAcquire ac(&gAshmemMutex);
AshmemRec* rec = reinterpret_cast<AshmemRec*>(ID);
ashmem_unpin_region(rec->fFD, 0, 0);
#ifdef SK_DEBUG
rec->fPinned = false;
#endif
}
void SkAshmemImageCache::throwAwayCache(intptr_t ID) {
SkAutoMutexAcquire ac(&gAshmemMutex);
AshmemRec* rec = reinterpret_cast<AshmemRec*>(ID);
#ifdef SK_DEBUG
SkASSERT(!rec->fPinned);
#endif
this->removeRec(rec);
}
void SkAshmemImageCache::removeRec(SkAshmemImageCache::AshmemRec* rec) {
munmap(rec->fAddr, rec->fSize);
close(rec->fFD);
#ifdef SK_DEBUG
int index = this->findRec(rec);
SkASSERT(index >= 0);
fRecs.remove(index);
#endif
SkDELETE(rec);
}
#ifdef SK_DEBUG
void SkAshmemImageCache::appendRec(SkAshmemImageCache::AshmemRec* rec) {
int index = this->findRec(rec);
// Should not already exist.
SkASSERT(index < 0);
fRecs.insert(~index, 1, &rec);
}
int SkAshmemImageCache::AshmemRec::Compare(const SkAshmemImageCache::AshmemRec* a,
const SkAshmemImageCache::AshmemRec* b) {
return reinterpret_cast<intptr_t>(a) - reinterpret_cast<intptr_t>(b);
}
int SkAshmemImageCache::findRec(const SkAshmemImageCache::AshmemRec* rec) const {
return SkTSearch<AshmemRec>((const AshmemRec**)fRecs.begin(), fRecs.count(), rec,
sizeof(intptr_t), AshmemRec::Compare);
}
SkImageCache::CacheStatus SkAshmemImageCache::getCacheStatus(intptr_t ID) const {
SkAutoMutexAcquire ac(&gAshmemMutex);
AshmemRec* rec = reinterpret_cast<AshmemRec*>(ID);
int index = this->findRec(rec);
if (index < 0) {
return SkImageCache::kThrownAway_CacheStatus;
}
return rec->fPinned ? SkImageCache::kPinned_CacheStatus
: SkImageCache::kUnpinned_CacheStatus;
}
#endif