blob: b4f98621944182372ff30efe8c1860c7d95516b0 [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 */
16/*
17 * Mutex-free cache. Each entry has two 32-bit keys, one 32-bit value,
18 * and a 32-bit version.
19 */
20#include "Dalvik.h"
21
22#include <stdlib.h>
23
24/*
25 * I think modern C mandates that the results of a boolean expression are
26 * 0 or 1. If not, or we suddenly turn into C++ and bool != int, use this.
27 */
28#define BOOL_TO_INT(x) (x)
29//#define BOOL_TO_INT(x) ((x) ? 1 : 0)
30
31#define CPU_CACHE_WIDTH 32
32#define CPU_CACHE_WIDTH_1 (CPU_CACHE_WIDTH-1)
33
34#define ATOMIC_LOCK_FLAG (1 << 31)
35
36/*
37 * Allocate cache.
38 */
39AtomicCache* dvmAllocAtomicCache(int numEntries)
40{
41 AtomicCache* newCache;
42
43 newCache = (AtomicCache*) calloc(1, sizeof(AtomicCache));
44 if (newCache == NULL)
45 return NULL;
46
47 newCache->numEntries = numEntries;
48
49 newCache->entryAlloc = calloc(1,
50 sizeof(AtomicCacheEntry) * numEntries + CPU_CACHE_WIDTH);
51 if (newCache->entryAlloc == NULL)
52 return NULL;
53
54 /*
55 * Adjust storage to align on a 32-byte boundary. Each entry is 16 bytes
56 * wide. This ensures that each cache entry sits on a single CPU cache
57 * line.
58 */
59 assert(sizeof(AtomicCacheEntry) == 16);
60 newCache->entries = (AtomicCacheEntry*)
61 (((int) newCache->entryAlloc + CPU_CACHE_WIDTH_1) & ~CPU_CACHE_WIDTH_1);
62
63 return newCache;
64}
65
66/*
67 * Free cache.
68 */
69void dvmFreeAtomicCache(AtomicCache* cache)
70{
71 if (cache != NULL) {
72 free(cache->entryAlloc);
73 free(cache);
74 }
75}
76
77
78
79/*
80 * Update a cache entry.
81 *
82 * In the event of a collision with another thread, the update may be skipped.
83 *
84 * We only need "pCache" for stats.
85 */
86void dvmUpdateAtomicCache(u4 key1, u4 key2, u4 value, AtomicCacheEntry* pEntry,
87 u4 firstVersion
88#if CALC_CACHE_STATS > 0
89 , AtomicCache* pCache
90#endif
91 )
92{
93 /*
94 * The fields don't match, so we need to update them. There is a
95 * risk that another thread is also trying to update them, so we
96 * grab an ownership flag to lock out other threads.
97 *
98 * If the lock flag was already set in "firstVersion", somebody else
99 * was in mid-update. (This means that using "firstVersion" as the
100 * "before" argument to the CAS would succeed when it shouldn't and
101 * vice-versa -- we could also just pass in
102 * (firstVersion & ~ATOMIC_LOCK_FLAG) as the first argument.)
103 *
104 * NOTE: we don't really deal with the situation where we overflow
105 * the version counter (at 2^31). Probably not a real concern.
106 */
107 if ((firstVersion & ATOMIC_LOCK_FLAG) != 0 ||
Andy McFadden6e10b9a2010-06-14 15:24:39 -0700108 android_atomic_release_cas(
109 firstVersion, firstVersion | ATOMIC_LOCK_FLAG,
110 (volatile s4*) &pEntry->version) != 0)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800111 {
112 /*
113 * We couldn't get the write lock. Return without updating the table.
114 */
115#if CALC_CACHE_STATS > 0
116 pCache->fail++;
117#endif
118 return;
119 }
120
121 /* must be even-valued on entry */
122 assert((firstVersion & 0x01) == 0);
123
124#if CALC_CACHE_STATS > 0
125 /* for stats, assume a key value of zero indicates an empty entry */
126 if (pEntry->key1 == 0)
127 pCache->fills++;
128 else
129 pCache->misses++;
130#endif
131
132 /* volatile incr */
133 pEntry->version++;
Andy McFadden6e10b9a2010-06-14 15:24:39 -0700134 ANDROID_MEMBAR_FULL();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800135
136 pEntry->key1 = key1;
137 pEntry->key2 = key2;
138 pEntry->value = value;
139
140 /* volatile incr */
141 pEntry->version++;
Andy McFadden6e10b9a2010-06-14 15:24:39 -0700142 ANDROID_MEMBAR_FULL();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800143
144 /*
145 * Clear the lock flag. Nobody else should have been able to modify
146 * pEntry->version, so if this fails the world is broken.
147 */
148 firstVersion += 2;
Andy McFadden6e10b9a2010-06-14 15:24:39 -0700149 if (android_atomic_release_cas(
150 firstVersion | ATOMIC_LOCK_FLAG, firstVersion,
151 (volatile s4*) &pEntry->version) != 0)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800152 {
153 //LOGE("unable to reset the instanceof cache ownership\n");
154 dvmAbort();
155 }
156}
157
158
159/*
160 * Dump the "instanceof" cache stats.
161 */
162void dvmDumpAtomicCacheStats(const AtomicCache* pCache)
163{
164 if (pCache == NULL)
165 return;
166 dvmFprintf(stdout,
167 "Cache stats: trv=%d fai=%d hit=%d mis=%d fil=%d %d%% (size=%d)\n",
168 pCache->trivial, pCache->fail, pCache->hits,
169 pCache->misses, pCache->fills,
170 (pCache->hits == 0) ? 0 :
171 pCache->hits * 100 /
172 (pCache->fail + pCache->hits + pCache->misses + pCache->fills),
173 pCache->numEntries);
174}