/* Moves a link. Assume the lock is held. */ STATIC int GC_move_disappearing_link_inner( struct dl_hashtbl_s *dl_hashtbl, void **link, void **new_link) { struct disappearing_link *curr_dl, *prev_dl, *new_dl; size_t curr_index, new_index; word curr_hidden_link; word new_hidden_link; /* Find current link. */ curr_index = HASH2(link, dl_hashtbl -> log_size); curr_hidden_link = GC_HIDE_POINTER(link); prev_dl = NULL; for (curr_dl = dl_hashtbl -> head[curr_index]; curr_dl; curr_dl = dl_next(curr_dl)) { if (curr_dl -> dl_hidden_link == curr_hidden_link) break; prev_dl = curr_dl; } if (NULL == curr_dl) { return GC_NOT_FOUND; } if (link == new_link) { return GC_SUCCESS; /* Nothing to do. */ } /* link found; now check new_link not present. */ new_index = HASH2(new_link, dl_hashtbl -> log_size); new_hidden_link = GC_HIDE_POINTER(new_link); for (new_dl = dl_hashtbl -> head[new_index]; new_dl; new_dl = dl_next(new_dl)) { if (new_dl -> dl_hidden_link == new_hidden_link) { /* Target already registered; bail. */ return GC_DUPLICATE; } } /* Remove from old, add to new, update link. */ if (NULL == prev_dl) { dl_hashtbl -> head[curr_index] = dl_next(curr_dl); } else { dl_set_next(prev_dl, dl_next(curr_dl)); } curr_dl -> dl_hidden_link = new_hidden_link; dl_set_next(curr_dl, dl_hashtbl -> head[new_index]); dl_hashtbl -> head[new_index] = curr_dl; return GC_SUCCESS; }
void rvmRegisterReference(Env* env, Object* reference, Object* referent) { if (referent) { // Add 'reference' to the references list for 'referent' in the referents hashtable rvmLockMutex(&referentsLock); ReferenceList* l = rvmAllocateMemory(env, sizeof(ReferenceList)); if (!l) goto done; // OOM thrown l->reference = reference; void* key = (void*) GC_HIDE_POINTER(referent); // Hide the pointer from the GC so that it doesn't prevent the referent from being GCed. ReferentEntry* referentEntry; HASH_FIND_PTR(referents, &key, referentEntry); if (!referentEntry) { // referent is not in the hashtable. Add it. referentEntry = rvmAllocateMemory(env, sizeof(ReferentEntry)); if (!referentEntry) goto done; // OOM thrown referentEntry->key = key; HASH_ADD_PTR(referents, key, referentEntry); } // Add the reference to the referent's list of references LL_PREPEND(referentEntry->references, l); // Register the referent for finalization GC_REGISTER_FINALIZER_NO_ORDER(referent, _finalizeObject, NULL, NULL, NULL); done: rvmUnlockMutex(&referentsLock); } }
GC_INNER int GC_clone_finalizer(void * src_p, void * dest_p) { struct finalizable_object * curr_fo; size_t index; ptr_t base, result; GC_finalization_proc fn; ptr_t cd; finalization_mark_proc mp; DCL_LOCK_STATE; base = (ptr_t)src_p; result = (ptr_t)dest_p; LOCK(); index = HASH2(base, log_fo_table_size); curr_fo = GC_fo_head[index]; while (curr_fo != 0) { GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object)); if (curr_fo -> fo_hidden_base == GC_HIDE_POINTER(base)) { fn = curr_fo -> fo_fn; cd = curr_fo -> fo_client_data; mp = curr_fo -> fo_mark_proc; UNLOCK(); return GC_register_finalizer_inner(result, fn, cd, 0, 0, mp); } curr_fo = fo_next(curr_fo); } UNLOCK(); return TRUE; }
static void finalizeObject(Env* env, Object* obj) { // TRACEF("finalizeObject: %p (%s)\n", obj, obj->clazz->name); rvmLockMutex(&referentsLock); void* key = (void*) GC_HIDE_POINTER(obj); ReferentEntry* referentEntry; HASH_FIND_PTR(referents, &key, referentEntry); assert(referentEntry != NULL); if (referentEntry->references == NULL) { // The object is not referenced by any type of reference and can never be resurrected. HASH_DEL(referents, referentEntry); rvmUnlockMutex(&referentsLock); return; } Object* softReferences = NULL; Object* weakReferences = NULL; Object* finalizerReferences = NULL; Object* phantomReferences = NULL; Object* clearedReferences = NULL; ReferenceList* refNode; while (referentEntry->references != NULL) { refNode = referentEntry->references; LL_DELETE(referentEntry->references, refNode); Object** list = NULL; Object* reference = refNode->reference; if (rvmIsSubClass(java_lang_ref_SoftReference, reference->clazz)) { list = &softReferences; } else if (rvmIsSubClass(java_lang_ref_WeakReference, reference->clazz)) { list = &weakReferences; } else if (rvmIsSubClass(java_lang_ref_FinalizerReference, reference->clazz)) { list = &finalizerReferences; } else if (rvmIsSubClass(java_lang_ref_PhantomReference, reference->clazz)) { list = &phantomReferences; } enqueuePendingReference(env, reference, list); } assert(referentEntry->references == NULL); clearAndEnqueueReferences(env, &softReferences, &clearedReferences); clearAndEnqueueReferences(env, &weakReferences, &clearedReferences); enqueueFinalizerReferences(env, &finalizerReferences, &clearedReferences); clearAndEnqueueReferences(env, &phantomReferences, &clearedReferences); // Reregister for finalization. If no new references have been added to the list of references for the referent the // next time it gets finalized we know it will never be resurrected. GC_REGISTER_FINALIZER_NO_ORDER(obj, _finalizeObject, NULL, NULL, NULL); rvmUnlockMutex(&referentsLock); if (clearedReferences != NULL) { rvmCallVoidClassMethod(env, java_lang_ref_ReferenceQueue, java_lang_ref_ReferenceQueue_add, clearedReferences); assert(rvmExceptionOccurred(env) == NULL); } }
/** * Returns the ReferentEntry for the specified object or creates one and adds * it to the referents hash if none exists. referentsLock MUST be held. */ static ReferentEntry* getReferentEntryForObject(Env* env, Object* o) { void* key = (void*) GC_HIDE_POINTER(o); // Hide the pointer from the GC so that the key doesn't prevent the object from being GCed. ReferentEntry* referentEntry; HASH_FIND_PTR(referents, &key, referentEntry); if (!referentEntry) { // Object is not in the hashtable. Add it. referentEntry = allocateMemoryOfKind(env, sizeof(ReferentEntry), referentEntryGCKind); if (!referentEntry) return NULL; // OOM thrown referentEntry->key = key; HASH_ADD_PTR(referents, key, referentEntry); } return referentEntry; }
GC_API int GC_CALL GC_toggleref_add(void *obj, int is_strong_ref) { int res = GC_SUCCESS; DCL_LOCK_STATE; GC_ASSERT(obj != NULL); LOCK(); if (GC_toggleref_callback != 0) { if (!ensure_toggleref_capacity(1)) { res = GC_NO_MEMORY; } else { GC_toggleref_arr[GC_toggleref_array_size++].strong_ref = is_strong_ref ? obj : (void *)GC_HIDE_POINTER(obj); } } UNLOCK(); return res; }
/* Assume the lock is held. */ GC_INLINE struct disappearing_link *GC_unregister_disappearing_link_inner( struct dl_hashtbl_s *dl_hashtbl, void **link) { struct disappearing_link *curr_dl; struct disappearing_link *prev_dl = NULL; size_t index = HASH2(link, dl_hashtbl->log_size); for (curr_dl = dl_hashtbl -> head[index]; curr_dl; curr_dl = dl_next(curr_dl)) { if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) { /* Remove found entry from the table. */ if (NULL == prev_dl) { dl_hashtbl -> head[index] = dl_next(curr_dl); } else { dl_set_next(prev_dl, dl_next(curr_dl)); } dl_hashtbl -> entries--; break; } prev_dl = curr_dl; } return curr_dl; }
GC_INNER void GC_process_togglerefs(void) { int i; int new_size = 0; GC_ASSERT(I_HOLD_LOCK()); for (i = 0; i < GC_toggleref_array_size; ++i) { GCToggleRef r = GC_toggleref_arr[i]; void *obj = r.strong_ref; if (((word)obj & 1) != 0) { obj = GC_REVEAL_POINTER(r.weak_ref); } if (NULL == obj) { continue; } switch (GC_toggleref_callback(obj)) { case GC_TOGGLE_REF_DROP: break; case GC_TOGGLE_REF_STRONG: GC_toggleref_arr[new_size++].strong_ref = obj; break; case GC_TOGGLE_REF_WEAK: GC_toggleref_arr[new_size++].weak_ref = GC_HIDE_POINTER(obj); break; default: ABORT("Bad toggle-ref status returned by callback"); } } if (new_size < GC_toggleref_array_size) { BZERO(&GC_toggleref_arr[new_size], (GC_toggleref_array_size - new_size) * sizeof(GCToggleRef)); GC_toggleref_array_size = new_size; } }
/* enqueued for finalization. */ GC_INNER void GC_finalize(void) { struct finalizable_object * curr_fo, * prev_fo, * next_fo; ptr_t real_ptr; size_t i; size_t fo_size = log_fo_table_size == -1 ? 0 : (size_t)1 << log_fo_table_size; # ifndef SMALL_CONFIG /* Save current GC_[dl/ll]_entries value for stats printing */ GC_old_dl_entries = GC_dl_hashtbl.entries; # ifndef GC_LONG_REFS_NOT_NEEDED GC_old_ll_entries = GC_ll_hashtbl.entries; # endif # endif # ifndef GC_TOGGLE_REFS_NOT_NEEDED GC_mark_togglerefs(); # endif GC_make_disappearing_links_disappear(&GC_dl_hashtbl); /* Mark all objects reachable via chains of 1 or more pointers */ /* from finalizable objects. */ GC_ASSERT(GC_mark_state == MS_NONE); for (i = 0; i < fo_size; i++) { for (curr_fo = GC_fnlz_roots.fo_head[i]; curr_fo != NULL; curr_fo = fo_next(curr_fo)) { GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object)); real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base); if (!GC_is_marked(real_ptr)) { GC_MARKED_FOR_FINALIZATION(real_ptr); GC_MARK_FO(real_ptr, curr_fo -> fo_mark_proc); if (GC_is_marked(real_ptr)) { WARN("Finalization cycle involving %p\n", real_ptr); } } } } /* Enqueue for finalization all objects that are still */ /* unreachable. */ GC_bytes_finalized = 0; for (i = 0; i < fo_size; i++) { curr_fo = GC_fnlz_roots.fo_head[i]; prev_fo = 0; while (curr_fo != 0) { real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base); if (!GC_is_marked(real_ptr)) { if (!GC_java_finalization) { GC_set_mark_bit(real_ptr); } /* Delete from hash table */ next_fo = fo_next(curr_fo); if (NULL == prev_fo) { GC_fnlz_roots.fo_head[i] = next_fo; } else { fo_set_next(prev_fo, next_fo); } GC_fo_entries--; if (GC_object_finalized_proc) GC_object_finalized_proc(real_ptr); /* Add to list of objects awaiting finalization. */ fo_set_next(curr_fo, GC_fnlz_roots.finalize_now); GC_fnlz_roots.finalize_now = curr_fo; /* unhide object pointer so any future collections will */ /* see it. */ curr_fo -> fo_hidden_base = (word)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base); GC_bytes_finalized += curr_fo -> fo_object_size + sizeof(struct finalizable_object); GC_ASSERT(GC_is_marked(GC_base(curr_fo))); curr_fo = next_fo; } else { prev_fo = curr_fo; curr_fo = fo_next(curr_fo); } } } if (GC_java_finalization) { /* make sure we mark everything reachable from objects finalized using the no_order mark_proc */ for (curr_fo = GC_fnlz_roots.finalize_now; curr_fo != NULL; curr_fo = fo_next(curr_fo)) { real_ptr = (ptr_t)curr_fo -> fo_hidden_base; if (!GC_is_marked(real_ptr)) { if (curr_fo -> fo_mark_proc == GC_null_finalize_mark_proc) { GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc); } if (curr_fo -> fo_mark_proc != GC_unreachable_finalize_mark_proc) { GC_set_mark_bit(real_ptr); } } } /* now revive finalize-when-unreachable objects reachable from other finalizable objects */ if (need_unreachable_finalization) { curr_fo = GC_fnlz_roots.finalize_now; prev_fo = NULL; while (curr_fo != NULL) { next_fo = fo_next(curr_fo); if (curr_fo -> fo_mark_proc == GC_unreachable_finalize_mark_proc) { real_ptr = (ptr_t)curr_fo -> fo_hidden_base; if (!GC_is_marked(real_ptr)) { GC_set_mark_bit(real_ptr); } else { if (NULL == prev_fo) { GC_fnlz_roots.finalize_now = next_fo; } else { fo_set_next(prev_fo, next_fo); } curr_fo -> fo_hidden_base = GC_HIDE_POINTER(curr_fo -> fo_hidden_base); GC_bytes_finalized -= curr_fo->fo_object_size + sizeof(struct finalizable_object); i = HASH2(real_ptr, log_fo_table_size); fo_set_next(curr_fo, GC_fnlz_roots.fo_head[i]); GC_fo_entries++; GC_fnlz_roots.fo_head[i] = curr_fo; curr_fo = prev_fo; } } prev_fo = curr_fo; curr_fo = next_fo; } } } GC_remove_dangling_disappearing_links(&GC_dl_hashtbl); # ifndef GC_TOGGLE_REFS_NOT_NEEDED GC_clear_togglerefs(); # endif # ifndef GC_LONG_REFS_NOT_NEEDED GC_make_disappearing_links_disappear(&GC_ll_hashtbl); GC_remove_dangling_disappearing_links(&GC_ll_hashtbl); # endif if (GC_fail_count) { /* Don't prevent running finalizers if there has been an allocation */ /* failure recently. */ # ifdef THREADS GC_reset_finalizer_nested(); # else GC_finalizer_nested = 0; # endif } }
/* finalized when this finalizer is invoked. */ STATIC void GC_register_finalizer_inner(void * obj, GC_finalization_proc fn, void *cd, GC_finalization_proc *ofn, void **ocd, finalization_mark_proc mp) { ptr_t base; struct finalizable_object * curr_fo, * prev_fo; size_t index; struct finalizable_object *new_fo = 0; hdr *hhdr = NULL; /* initialized to prevent warning. */ GC_oom_func oom_fn; DCL_LOCK_STATE; LOCK(); if (log_fo_table_size == -1 || GC_fo_entries > ((word)1 << log_fo_table_size)) { GC_grow_table((struct hash_chain_entry ***)&GC_fnlz_roots.fo_head, &log_fo_table_size); GC_COND_LOG_PRINTF("Grew fo table to %u entries\n", 1 << (unsigned)log_fo_table_size); } /* in the THREADS case we hold allocation lock. */ base = (ptr_t)obj; for (;;) { index = HASH2(base, log_fo_table_size); prev_fo = 0; curr_fo = GC_fnlz_roots.fo_head[index]; while (curr_fo != 0) { GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object)); if (curr_fo -> fo_hidden_base == GC_HIDE_POINTER(base)) { /* Interruption by a signal in the middle of this */ /* should be safe. The client may see only *ocd */ /* updated, but we'll declare that to be his problem. */ if (ocd) *ocd = (void *) (curr_fo -> fo_client_data); if (ofn) *ofn = curr_fo -> fo_fn; /* Delete the structure for base. */ if (prev_fo == 0) { GC_fnlz_roots.fo_head[index] = fo_next(curr_fo); } else { fo_set_next(prev_fo, fo_next(curr_fo)); } if (fn == 0) { GC_fo_entries--; /* May not happen if we get a signal. But a high */ /* estimate will only make the table larger than */ /* necessary. */ # if !defined(THREADS) && !defined(DBG_HDRS_ALL) GC_free((void *)curr_fo); # endif } else { curr_fo -> fo_fn = fn; curr_fo -> fo_client_data = (ptr_t)cd; curr_fo -> fo_mark_proc = mp; /* Reinsert it. We deleted it first to maintain */ /* consistency in the event of a signal. */ if (prev_fo == 0) { GC_fnlz_roots.fo_head[index] = curr_fo; } else { fo_set_next(prev_fo, curr_fo); } } UNLOCK(); # ifndef DBG_HDRS_ALL if (EXPECT(new_fo != 0, FALSE)) { /* Free unused new_fo returned by GC_oom_fn() */ GC_free((void *)new_fo); } # endif return; } prev_fo = curr_fo; curr_fo = fo_next(curr_fo); } if (EXPECT(new_fo != 0, FALSE)) { /* new_fo is returned by GC_oom_fn(), so fn != 0 and hhdr != 0. */ break; } if (fn == 0) { if (ocd) *ocd = 0; if (ofn) *ofn = 0; UNLOCK(); return; } GET_HDR(base, hhdr); if (EXPECT(0 == hhdr, FALSE)) { /* We won't collect it, hence finalizer wouldn't be run. */ if (ocd) *ocd = 0; if (ofn) *ofn = 0; UNLOCK(); return; } new_fo = (struct finalizable_object *) GC_INTERNAL_MALLOC(sizeof(struct finalizable_object),NORMAL); if (EXPECT(new_fo != 0, TRUE)) break; oom_fn = GC_oom_fn; UNLOCK(); new_fo = (struct finalizable_object *) (*oom_fn)(sizeof(struct finalizable_object)); if (0 == new_fo) { /* No enough memory. *ocd and *ofn remains unchanged. */ return; } /* It's not likely we'll make it here, but ... */ LOCK(); /* Recalculate index since the table may grow and */ /* check again that our finalizer is not in the table. */ } GC_ASSERT(GC_size(new_fo) >= sizeof(struct finalizable_object)); if (ocd) *ocd = 0; if (ofn) *ofn = 0; new_fo -> fo_hidden_base = GC_HIDE_POINTER(base); new_fo -> fo_fn = fn; new_fo -> fo_client_data = (ptr_t)cd; new_fo -> fo_object_size = hhdr -> hb_sz; new_fo -> fo_mark_proc = mp; fo_set_next(new_fo, GC_fnlz_roots.fo_head[index]); GC_fo_entries++; GC_fnlz_roots.fo_head[index] = new_fo; UNLOCK(); }
STATIC int GC_register_disappearing_link_inner( struct dl_hashtbl_s *dl_hashtbl, void **link, const void *obj, const char *tbl_log_name) { struct disappearing_link *curr_dl; size_t index; struct disappearing_link * new_dl; DCL_LOCK_STATE; LOCK(); GC_ASSERT(obj != NULL && GC_base_C(obj) == obj); if (dl_hashtbl -> log_size == -1 || dl_hashtbl -> entries > ((word)1 << dl_hashtbl -> log_size)) { GC_grow_table((struct hash_chain_entry ***)&dl_hashtbl -> head, &dl_hashtbl -> log_size); GC_COND_LOG_PRINTF("Grew %s table to %u entries\n", tbl_log_name, 1 << (unsigned)dl_hashtbl -> log_size); } index = HASH2(link, dl_hashtbl -> log_size); for (curr_dl = dl_hashtbl -> head[index]; curr_dl != 0; curr_dl = dl_next(curr_dl)) { if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) { curr_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj); UNLOCK(); return GC_DUPLICATE; } } new_dl = (struct disappearing_link *) GC_INTERNAL_MALLOC(sizeof(struct disappearing_link),NORMAL); if (0 == new_dl) { GC_oom_func oom_fn = GC_oom_fn; UNLOCK(); new_dl = (struct disappearing_link *) (*oom_fn)(sizeof(struct disappearing_link)); if (0 == new_dl) { return GC_NO_MEMORY; } /* It's not likely we'll make it here, but ... */ LOCK(); /* Recalculate index since the table may grow. */ index = HASH2(link, dl_hashtbl -> log_size); /* Check again that our disappearing link not in the table. */ for (curr_dl = dl_hashtbl -> head[index]; curr_dl != 0; curr_dl = dl_next(curr_dl)) { if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) { curr_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj); UNLOCK(); # ifndef DBG_HDRS_ALL /* Free unused new_dl returned by GC_oom_fn() */ GC_free((void *)new_dl); # endif return GC_DUPLICATE; } } } new_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj); new_dl -> dl_hidden_link = GC_HIDE_POINTER(link); dl_set_next(new_dl, dl_hashtbl -> head[index]); dl_hashtbl -> head[index] = new_dl; dl_hashtbl -> entries++; UNLOCK(); return GC_SUCCESS; }
void * entry (void *arg) { pthread_setspecific(key, (void *)GC_HIDE_POINTER(GC_STRDUP("hello, world"))); return arg; }