2 * Copyright (C) 2009 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * Indirect reference table management.
23 * Initialize an IndirectRefTable structure.
25 bool dvmInitIndirectRefTable(IndirectRefTable* pRef, int initialCount,
26 int maxCount, IndirectRefKind kind)
28 assert(initialCount > 0);
29 assert(initialCount <= maxCount);
30 assert(kind != kIndirectKindInvalid);
32 pRef->table = (Object**) malloc(initialCount * sizeof(Object*));
33 if (pRef->table == NULL)
36 memset(pRef->table, 0xd1, initialCount * sizeof(Object*));
40 (IndirectRefSlot*) calloc(maxCount, sizeof(IndirectRefSlot));
41 if (pRef->slotData == NULL)
44 pRef->segmentState.all = IRT_FIRST_SEGMENT;
45 pRef->allocEntries = initialCount;
46 pRef->maxEntries = maxCount;
53 * Clears out the contents of a IndirectRefTable, freeing allocated storage.
55 void dvmClearIndirectRefTable(IndirectRefTable* pRef)
60 pRef->allocEntries = pRef->maxEntries = -1;
64 * Remove one or more segments from the top. The table entry identified
65 * by "cookie" becomes the new top-most entry.
67 * Returns false if "cookie" is invalid or the table has only one segment.
69 bool dvmPopIndirectRefTableSegmentCheck(IndirectRefTable* pRef, u4 cookie)
74 * The new value for "top" must be <= the current value. Otherwise
75 * this would represent an expansion of the table.
78 if (sst.parts.topIndex > pRef->segmentState.parts.topIndex) {
79 LOGE("Attempt to expand table with segment pop (%d to %d)\n",
80 pRef->segmentState.parts.topIndex, sst.parts.topIndex);
83 if (sst.parts.numHoles >= sst.parts.topIndex) {
84 LOGE("Absurd numHoles in cookie (%d bi=%d)\n",
85 sst.parts.numHoles, sst.parts.topIndex);
89 LOGV("IRT %p[%d]: pop, top=%d holes=%d\n",
90 pRef, pRef->kind, sst.parts.topIndex, sst.parts.numHoles);
96 * Make sure that the entry at "idx" is correctly paired with "iref".
98 static bool checkEntry(IndirectRefTable* pRef, IndirectRef iref, int idx)
100 Object* obj = pRef->table[idx];
101 IndirectRef checkRef = dvmObjectToIndirectRef(pRef, obj, idx, pRef->kind);
102 if (checkRef != iref) {
103 LOGW("IRT %p[%d]: iref mismatch (req=%p vs cur=%p)\n",
104 pRef, pRef->kind, iref, checkRef);
111 * Update extended debug info when an entry is added.
113 * We advance the serial number, invalidating any outstanding references to
116 static inline void updateSlotAdd(IndirectRefTable* pRef, Object* obj, int slot)
118 if (pRef->slotData != NULL) {
119 IndirectRefSlot* pSlot = &pRef->slotData[slot];
121 //LOGI("+++ add [%d] slot %d (%p->%p), serial=%d\n",
122 // pRef->kind, slot, obj, iref, pSlot->serial);
123 pSlot->previous[pSlot->serial % kIRTPrevCount] = obj;
128 * Update extended debug info when an entry is removed.
130 static inline void updateSlotRemove(IndirectRefTable* pRef, int slot)
132 if (pRef->slotData != NULL) {
133 //IndirectRefSlot* pSlot = &pRef->slotData[slot];
134 //LOGI("+++ remove [%d] slot %d, serial now %d\n",
135 // pRef->kind, slot, pSlot->serial);
140 * Add "obj" to "pRef".
142 IndirectRef dvmAddToIndirectRefTable(IndirectRefTable* pRef, u4 cookie,
145 IRTSegmentState prevState;
146 prevState.all = cookie;
147 int topIndex = pRef->segmentState.parts.topIndex;
150 assert(dvmIsValidObject(obj));
151 assert(pRef->table != NULL);
152 assert(pRef->allocEntries <= pRef->maxEntries);
153 assert(pRef->segmentState.parts.numHoles >= prevState.parts.numHoles);
155 if (topIndex == pRef->allocEntries) {
156 /* reached end of allocated space; did we hit buffer max? */
157 if (topIndex == pRef->maxEntries) {
158 LOGW("IndirectRefTable overflow (max=%d)\n", pRef->maxEntries);
165 newSize = pRef->allocEntries * 2;
166 if (newSize > pRef->maxEntries)
167 newSize = pRef->maxEntries;
168 assert(newSize > pRef->allocEntries);
170 newTable = (Object**) realloc(pRef->table, newSize * sizeof(Object*));
171 if (newTable == NULL) {
172 LOGE("Unable to expand iref table (from %d to %d, max=%d)\n",
173 pRef->allocEntries, newSize, pRef->maxEntries);
176 LOGV("Growing ireftab %p from %d to %d (max=%d)\n",
177 pRef, pRef->allocEntries, newSize, pRef->maxEntries);
179 /* update entries; adjust "nextEntry" in case memory moved */
180 pRef->table = newTable;
181 pRef->allocEntries = newSize;
187 * We know there's enough room in the table. Now we just need to find
188 * the right spot. If there's a hole, find it and fill it; otherwise,
189 * add to the end of the list.
191 int numHoles = pRef->segmentState.parts.numHoles - prevState.parts.numHoles;
193 assert(topIndex > 1);
194 /* find the first hole; likely to be near the end of the list */
195 Object** pScan = &pRef->table[topIndex - 1];
196 assert(*pScan != NULL);
197 while (*--pScan != NULL) {
198 assert(pScan >= pRef->table + prevState.parts.topIndex);
200 updateSlotAdd(pRef, obj, pScan - pRef->table);
201 result = dvmObjectToIndirectRef(pRef, obj, pScan - pRef->table,
204 pRef->segmentState.parts.numHoles--;
207 updateSlotAdd(pRef, obj, topIndex);
208 result = dvmObjectToIndirectRef(pRef, obj, topIndex, pRef->kind);
209 pRef->table[topIndex++] = obj;
210 pRef->segmentState.parts.topIndex = topIndex;
213 assert(result != NULL);
218 * Verify that the indirect table lookup is valid.
220 * Returns "false" if something looks bad.
222 bool dvmGetFromIndirectRefTableCheck(IndirectRefTable* pRef, IndirectRef iref)
224 if (dvmGetIndirectRefType(iref) == kIndirectKindInvalid) {
225 LOGW("Invalid indirect reference 0x%08x\n", (u4) iref);
229 int topIndex = pRef->segmentState.parts.topIndex;
230 int idx = dvmIndirectRefToIndex(iref);
233 LOGD("Attempt to look up NULL iref\n");
236 if (idx >= topIndex) {
237 /* bad -- stale reference? */
238 LOGD("Attempt to access invalid index %d (top=%d)\n",
243 Object* obj = pRef->table[idx];
245 LOGD("Attempt to read from hole, iref=%p\n", iref);
248 if (!checkEntry(pRef, iref, idx))
255 * Remove "obj" from "pRef". We extract the table offset bits from "iref"
256 * and zap the corresponding entry, leaving a hole if it's not at the top.
258 * If the entry is not between the current top index and the bottom index
259 * specified by the cookie, we don't remove anything. This is the behavior
260 * required by JNI's DeleteLocalRef function.
262 * Note this is NOT called when a local frame is popped. This is only used
263 * for explict single removals.
265 * Returns "false" if nothing was removed.
267 bool dvmRemoveFromIndirectRefTable(IndirectRefTable* pRef, u4 cookie,
270 IRTSegmentState prevState;
271 prevState.all = cookie;
272 int topIndex = pRef->segmentState.parts.topIndex;
273 int bottomIndex = prevState.parts.topIndex;
275 assert(pRef->table != NULL);
276 assert(pRef->allocEntries <= pRef->maxEntries);
277 assert(pRef->segmentState.parts.numHoles >= prevState.parts.numHoles);
279 int idx = dvmIndirectRefToIndex(iref);
280 if (idx < bottomIndex) {
282 LOGV("Attempt to remove index outside index area (%d vs %d-%d)\n",
283 idx, bottomIndex, topIndex);
286 if (idx >= topIndex) {
287 /* bad -- stale reference? */
288 LOGD("Attempt to remove invalid index %d (bottom=%d top=%d)\n",
289 idx, bottomIndex, topIndex);
293 if (idx == topIndex-1) {
295 * Top-most entry. Scan up and consume holes. No need to NULL
296 * out the entry, since the test vs. topIndex will catch it.
298 if (!checkEntry(pRef, iref, idx))
300 updateSlotRemove(pRef, idx);
303 pRef->table[idx] = (Object*)0xd3d3d3d3;
307 pRef->segmentState.parts.numHoles - prevState.parts.numHoles;
309 while (--topIndex > bottomIndex && numHoles != 0) {
310 LOGV("+++ checking for hole at %d (cookie=0x%08x) val=%p\n",
311 topIndex-1, cookie, pRef->table[topIndex-1]);
312 if (pRef->table[topIndex-1] != NULL)
314 LOGV("+++ ate hole at %d\n", topIndex-1);
317 pRef->segmentState.parts.numHoles =
318 numHoles + prevState.parts.numHoles;
319 pRef->segmentState.parts.topIndex = topIndex;
321 pRef->segmentState.parts.topIndex = topIndex-1;
322 LOGV("+++ ate last entry %d\n", topIndex-1);
326 * Not the top-most entry. This creates a hole. We NULL out the
327 * entry to prevent somebody from deleting it twice and screwing up
330 if (pRef->table[idx] == NULL) {
331 LOGV("--- WEIRD: removing null entry %d\n", idx);
334 if (!checkEntry(pRef, iref, idx))
336 updateSlotRemove(pRef, idx);
338 pRef->table[idx] = NULL;
339 pRef->segmentState.parts.numHoles++;
340 LOGV("+++ left hole at %d, holes=%d\n",
341 idx, pRef->segmentState.parts.numHoles);
348 * Return a type name, useful for debugging.
350 const char* dvmIndirectRefTypeName(IndirectRef iref)
352 switch (dvmGetIndirectRefType(iref)) {
353 case kIndirectKindInvalid: return "invalid";
354 case kIndirectKindLocal: return "local";
355 case kIndirectKindGlobal: return "global";
356 case kIndirectKindWeakGlobal: return "weak global";
357 default: return "UNKNOWN";
362 * Dump the contents of a IndirectRefTable to the log.
364 void dvmDumpIndirectRefTable(const IndirectRefTable* pRef, const char* descr)
366 dvmDumpReferenceTableContents(pRef->table, dvmIndirectRefTableEntries(pRef),