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 } 183