OSDN Git Service

am beeeb788: am 05fa5fd5: Merge "Simplify merges of the annotation code."
[android-x86/dalvik.git] / vm / AtomicCache.cpp
1 /*
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 /*
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  */
40 AtomicCache* 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);
52     if (newCache->entryAlloc == NULL) {
53         free(newCache);
54         return NULL;
55     }
56
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  */
72 void dvmFreeAtomicCache(AtomicCache* cache)
73 {
74     if (cache != NULL) {
75         free(cache->entryAlloc);
76         free(cache);
77     }
78 }
79
80
81 /*
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  */
88 void 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     /*
96      * 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.
99      *
100      * If the lock flag was already set in "firstVersion", somebody else
101      * 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.)
105      *
106      * 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.
109      */
110     if ((firstVersion & ATOMIC_LOCK_FLAG) != 0 ||
111         android_atomic_release_cas(
112                 firstVersion, firstVersion | ATOMIC_LOCK_FLAG,
113                 (volatile s4*) &pEntry->version) != 0)
114     {
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
135     /*
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      */
140     u4 newVersion = (firstVersion | ATOMIC_LOCK_FLAG) + 1;
141     assert((newVersion & 0x01) == 1);
142
143     pEntry->version = newVersion;
144
145     android_atomic_release_store(key1, (int32_t*) &pEntry->key1);
146     pEntry->key2 = key2;
147     pEntry->value = value;
148
149     newVersion++;
150     android_atomic_release_store(newVersion, (int32_t*) &pEntry->version);
151
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      */
156     assert(newVersion == ((firstVersion + 2) | ATOMIC_LOCK_FLAG));
157     if (android_atomic_release_cas(
158             newVersion, newVersion & ~ATOMIC_LOCK_FLAG,
159             (volatile s4*) &pEntry->version) != 0)
160     {
161         //ALOGE("unable to reset the instanceof cache ownership");
162         dvmAbort();
163     }
164 }
165
166
167 /*
168  * Dump the "instanceof" cache stats.
169  */
170 void 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 }