blob: 6fbbc1294e75f7fee776bf979659ceb9a0783a3b [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Andy McFaddendeeeeb22010-06-16 08:32:04 -070016
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080017/*
18 * Mutex-free cache. Each entry has two 32-bit keys, one 32-bit value,
19 * and a 32-bit version.
20 */
21#include "Dalvik.h"
22
23#include <stdlib.h>
24
25/*
26 * I think modern C mandates that the results of a boolean expression are
27 * 0 or 1. If not, or we suddenly turn into C++ and bool != int, use this.
28 */
29#define BOOL_TO_INT(x) (x)
30//#define BOOL_TO_INT(x) ((x) ? 1 : 0)
31
32#define CPU_CACHE_WIDTH 32
33#define CPU_CACHE_WIDTH_1 (CPU_CACHE_WIDTH-1)
34
35#define ATOMIC_LOCK_FLAG (1 << 31)
36
37/*
38 * Allocate cache.
39 */
40AtomicCache* dvmAllocAtomicCache(int numEntries)
41{
42 AtomicCache* newCache;
43
44 newCache = (AtomicCache*) calloc(1, sizeof(AtomicCache));
45 if (newCache == NULL)
46 return NULL;
47
48 newCache->numEntries = numEntries;
49
50 newCache->entryAlloc = calloc(1,
51 sizeof(AtomicCacheEntry) * numEntries + CPU_CACHE_WIDTH);
You Kimf61171f2012-12-17 03:14:01 +090052 if (newCache->entryAlloc == NULL) {
53 free(newCache);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080054 return NULL;
You Kimf61171f2012-12-17 03:14:01 +090055 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080056
57 /*
58 * Adjust storage to align on a 32-byte boundary. Each entry is 16 bytes
59 * wide. This ensures that each cache entry sits on a single CPU cache
60 * line.
61 */
62 assert(sizeof(AtomicCacheEntry) == 16);
63 newCache->entries = (AtomicCacheEntry*)
64 (((int) newCache->entryAlloc + CPU_CACHE_WIDTH_1) & ~CPU_CACHE_WIDTH_1);
65
66 return newCache;
67}
68
69/*
70 * Free cache.
71 */
72void dvmFreeAtomicCache(AtomicCache* cache)
73{
74 if (cache != NULL) {
75 free(cache->entryAlloc);
76 free(cache);
77 }
78}
79
80
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080081/*
82 * Update a cache entry.
83 *
84 * In the event of a collision with another thread, the update may be skipped.
85 *
86 * We only need "pCache" for stats.
87 */
88void dvmUpdateAtomicCache(u4 key1, u4 key2, u4 value, AtomicCacheEntry* pEntry,
89 u4 firstVersion
90#if CALC_CACHE_STATS > 0
91 , AtomicCache* pCache
92#endif
93 )
94{
95 /*
Andy McFaddendeeeeb22010-06-16 08:32:04 -070096 * The fields don't match, so we want to update them. There is a risk
97 * that another thread is also trying to update them, so we grab an
98 * ownership flag to lock out other threads.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080099 *
100 * If the lock flag was already set in "firstVersion", somebody else
Andy McFaddendeeeeb22010-06-16 08:32:04 -0700101 * was in mid-update, and we don't want to continue here. (This means
102 * that using "firstVersion" as the "before" argument to the CAS would
103 * succeed when it shouldn't and vice-versa -- we could also just pass
104 * in (firstVersion & ~ATOMIC_LOCK_FLAG) as the first argument.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800105 *
Andy McFaddendeeeeb22010-06-16 08:32:04 -0700106 * NOTE: we don't deal with the situation where we overflow the version
107 * counter and trample the ATOMIC_LOCK_FLAG (at 2^31). Probably not
108 * a real concern.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800109 */
110 if ((firstVersion & ATOMIC_LOCK_FLAG) != 0 ||
Andy McFadden6e10b9a2010-06-14 15:24:39 -0700111 android_atomic_release_cas(
112 firstVersion, firstVersion | ATOMIC_LOCK_FLAG,
113 (volatile s4*) &pEntry->version) != 0)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800114 {
115 /*
116 * We couldn't get the write lock. Return without updating the table.
117 */
118#if CALC_CACHE_STATS > 0
119 pCache->fail++;
120#endif
121 return;
122 }
123
124 /* must be even-valued on entry */
125 assert((firstVersion & 0x01) == 0);
126
127#if CALC_CACHE_STATS > 0
128 /* for stats, assume a key value of zero indicates an empty entry */
129 if (pEntry->key1 == 0)
130 pCache->fills++;
131 else
132 pCache->misses++;
133#endif
134
Andy McFaddendeeeeb22010-06-16 08:32:04 -0700135 /*
136 * We have the write lock, but somebody could be reading this entry
137 * while we work. We use memory barriers to ensure that the state
138 * is always consistent when the version number is even.
139 */
Andy McFaddenfc3d3162010-08-05 14:34:26 -0700140 u4 newVersion = (firstVersion | ATOMIC_LOCK_FLAG) + 1;
141 assert((newVersion & 0x01) == 1);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800142
Andy McFaddenfc3d3162010-08-05 14:34:26 -0700143 pEntry->version = newVersion;
144
145 android_atomic_release_store(key1, (int32_t*) &pEntry->key1);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800146 pEntry->key2 = key2;
147 pEntry->value = value;
148
Andy McFaddenfc3d3162010-08-05 14:34:26 -0700149 newVersion++;
150 android_atomic_release_store(newVersion, (int32_t*) &pEntry->version);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800151
152 /*
153 * Clear the lock flag. Nobody else should have been able to modify
154 * pEntry->version, so if this fails the world is broken.
155 */
Andy McFaddenfc3d3162010-08-05 14:34:26 -0700156 assert(newVersion == ((firstVersion + 2) | ATOMIC_LOCK_FLAG));
Andy McFadden6e10b9a2010-06-14 15:24:39 -0700157 if (android_atomic_release_cas(
Andy McFaddenfc3d3162010-08-05 14:34:26 -0700158 newVersion, newVersion & ~ATOMIC_LOCK_FLAG,
Andy McFadden6e10b9a2010-06-14 15:24:39 -0700159 (volatile s4*) &pEntry->version) != 0)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800160 {
Steve Blockc1a4ab92012-01-06 19:16:58 +0000161 //ALOGE("unable to reset the instanceof cache ownership");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800162 dvmAbort();
163 }
164}
165
166
167/*
168 * Dump the "instanceof" cache stats.
169 */
170void dvmDumpAtomicCacheStats(const AtomicCache* pCache)
171{
172 if (pCache == NULL)
173 return;
174 dvmFprintf(stdout,
175 "Cache stats: trv=%d fai=%d hit=%d mis=%d fil=%d %d%% (size=%d)\n",
176 pCache->trivial, pCache->fail, pCache->hits,
177 pCache->misses, pCache->fills,
178 (pCache->hits == 0) ? 0 :
179 pCache->hits * 100 /
180 (pCache->fail + pCache->hits + pCache->misses + pCache->fills),
181 pCache->numEntries);
182}