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 * 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 */ 39 AtomicCache* 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 */ 69 void 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 */ 86 void 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 || 108 !ATOMIC_CMP_SWAP((volatile s4*) &pEntry->version, 109 firstVersion, firstVersion | ATOMIC_LOCK_FLAG)) 110 { 111 /* 112 * We couldn't get the write lock. Return without updating the table. 113 */ 114 #if CALC_CACHE_STATS > 0 115 pCache->fail++; 116 #endif 117 return; 118 } 119 120 /* must be even-valued on entry */ 121 assert((firstVersion & 0x01) == 0); 122 123 #if CALC_CACHE_STATS > 0 124 /* for stats, assume a key value of zero indicates an empty entry */ 125 if (pEntry->key1 == 0) 126 pCache->fills++; 127 else 128 pCache->misses++; 129 #endif 130 131 /* volatile incr */ 132 pEntry->version++; 133 MEM_BARRIER(); 134 135 pEntry->key1 = key1; 136 pEntry->key2 = key2; 137 pEntry->value = value; 138 139 /* volatile incr */ 140 pEntry->version++; 141 MEM_BARRIER(); 142 143 /* 144 * Clear the lock flag. Nobody else should have been able to modify 145 * pEntry->version, so if this fails the world is broken. 146 */ 147 firstVersion += 2; 148 if (!ATOMIC_CMP_SWAP((volatile s4*) &pEntry->version, 149 firstVersion | ATOMIC_LOCK_FLAG, firstVersion)) 150 { 151 //LOGE("unable to reset the instanceof cache ownership\n"); 152 dvmAbort(); 153 } 154 } 155 156 157 /* 158 * Dump the "instanceof" cache stats. 159 */ 160 void dvmDumpAtomicCacheStats(const AtomicCache* pCache) 161 { 162 if (pCache == NULL) 163 return; 164 dvmFprintf(stdout, 165 "Cache stats: trv=%d fai=%d hit=%d mis=%d fil=%d %d%% (size=%d)\n", 166 pCache->trivial, pCache->fail, pCache->hits, 167 pCache->misses, pCache->fills, 168 (pCache->hits == 0) ? 0 : 169 pCache->hits * 100 / 170 (pCache->fail + pCache->hits + pCache->misses + pCache->fills), 171 pCache->numEntries); 172 } 173 174