aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ports/SkAshmemImageCache.cpp
blob: a85542271df733e5c2a421f62e4ef9c69a38008e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*
 * 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