1 /*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkCachedData.h"
9 #include "SkDiscardableMemoryPool.h"
10 #include "SkMalloc.h"
11 #include "SkRefCnt.h"
12 #include "SkTypes.h"
13 #include "Test.h"
14
15 #include <cstring>
16
17 class SkDiscardableMemory;
18
19 enum LockedState {
20 kUnlocked,
21 kLocked,
22 };
23
24 enum CachedState {
25 kNotInCache,
26 kInCache,
27 };
28
check_data(skiatest::Reporter * reporter,SkCachedData * data,int refcnt,CachedState cacheState,LockedState lockedState)29 static void check_data(skiatest::Reporter* reporter, SkCachedData* data,
30 int refcnt, CachedState cacheState, LockedState lockedState) {
31 REPORTER_ASSERT(reporter, data->testing_only_getRefCnt() == refcnt);
32 REPORTER_ASSERT(reporter, data->testing_only_isInCache() == (kInCache == cacheState));
33 REPORTER_ASSERT(reporter, data->testing_only_isLocked() == (lockedState == kLocked));
34 }
35
make_data(size_t size,SkDiscardableMemoryPool * pool)36 static SkCachedData* make_data(size_t size, SkDiscardableMemoryPool* pool) {
37 if (pool) {
38 SkDiscardableMemory* dm = pool->create(size);
39 // the pool "can" return null, but it shouldn't in these controlled conditions
40 SkASSERT_RELEASE(dm);
41 return new SkCachedData(size, dm);
42 } else {
43 return new SkCachedData(sk_malloc_throw(size), size);
44 }
45 }
46
47 // returns with the data locked by client and cache
test_locking(skiatest::Reporter * reporter,size_t size,SkDiscardableMemoryPool * pool)48 static SkCachedData* test_locking(skiatest::Reporter* reporter,
49 size_t size, SkDiscardableMemoryPool* pool) {
50 SkCachedData* data = make_data(size, pool);
51
52 memset(data->writable_data(), 0x80, size); // just to use writable_data()
53
54 check_data(reporter, data, 1, kNotInCache, kLocked);
55
56 data->ref();
57 check_data(reporter, data, 2, kNotInCache, kLocked);
58 data->unref();
59 check_data(reporter, data, 1, kNotInCache, kLocked);
60
61 data->attachToCacheAndRef();
62 check_data(reporter, data, 2, kInCache, kLocked);
63
64 data->unref();
65 check_data(reporter, data, 1, kInCache, kUnlocked);
66
67 data->ref();
68 check_data(reporter, data, 2, kInCache, kLocked);
69
70 return data;
71 }
72
73 /*
74 * SkCachedData behaves differently (regarding its locked/unlocked state) depending on
75 * when it is in the cache or not. Being in the cache is signaled by calling attachToCacheAndRef()
76 * instead of ref(). (and balanced by detachFromCacheAndUnref).
77 *
78 * Thus, among other things, we test the end-of-life behavior when the client is the last owner
79 * and when the cache is.
80 */
DEF_TEST(CachedData,reporter)81 DEF_TEST(CachedData, reporter) {
82 sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Make(1000));
83
84 for (int useDiscardable = 0; useDiscardable <= 1; ++useDiscardable) {
85 const size_t size = 100;
86
87 // test with client as last owner
88 SkCachedData* data = test_locking(reporter, size, useDiscardable ? pool.get() : nullptr);
89 check_data(reporter, data, 2, kInCache, kLocked);
90 data->detachFromCacheAndUnref();
91 check_data(reporter, data, 1, kNotInCache, kLocked);
92 data->unref();
93
94 // test with cache as last owner
95 data = test_locking(reporter, size, useDiscardable ? pool.get() : nullptr);
96 check_data(reporter, data, 2, kInCache, kLocked);
97 data->unref();
98 check_data(reporter, data, 1, kInCache, kUnlocked);
99 data->detachFromCacheAndUnref();
100 }
101 }
102