/// Put a Message into Queue sorted by Priority (Highest at Head). /// \param[in] mq message queue object. /// \param[in] msg message object. static void MessageQueuePut (os_message_queue_t *mq, os_message_t *msg) { #if (__EXCLUSIVE_ACCESS == 0U) uint32_t primask = __get_PRIMASK(); #endif os_message_t *prev, *next; if (mq->msg_last != NULL) { prev = mq->msg_last; next = NULL; while ((prev != NULL) && (prev->priority < msg->priority)) { next = prev; prev = prev->prev; } msg->prev = prev; msg->next = next; if (prev != NULL) { prev->next = msg; } else { mq->msg_first = msg; } if (next != NULL) { next->prev = msg; } else { mq->msg_last = msg; } } else { msg->prev = NULL; msg->next = NULL; mq->msg_first= msg; mq->msg_last = msg; } #if (__EXCLUSIVE_ACCESS == 0U) __disable_irq(); mq->msg_count++; if (primask == 0U) { __enable_irq(); } #else atomic_inc32(&mq->msg_count); #endif }
void wspace_mark_scan_mostly_concurrent(Conclctor* marker) { GC *gc = marker->gc; GC_Metadata *metadata = gc->metadata; unsigned int num_dirtyset_slot = 0; marker->trace_stack = free_task_pool_get_entry(metadata); /* first step: copy all root objects to mark tasks.*/ Vector_Block *root_set = pool_iterator_next(metadata->gc_rootset_pool); while(root_set){ POINTER_SIZE_INT *iter = vector_block_iterator_init(root_set); while(!vector_block_iterator_end(root_set,iter)){ Partial_Reveal_Object *p_obj = (Partial_Reveal_Object *)*iter; iter = vector_block_iterator_advance(root_set,iter); assert(p_obj!=NULL); assert(address_belongs_to_gc_heap(p_obj, gc)); if(obj_mark_gray_in_table(p_obj)) collector_tracestack_push((Collector*)marker, p_obj); } root_set = pool_iterator_next(metadata->gc_rootset_pool); } /* put back the last trace_stack task */ pool_put_entry(metadata->mark_task_pool, marker->trace_stack); marker->trace_stack = free_task_pool_get_entry(metadata); /* following code has such concerns: 1, current_thread_id should be unique 2, mostly concurrent do not need adding new marker dynamically 3, when the heap is exhausted, final marking will enumeration rootset, it should be after above actions */ unsigned int current_thread_id = atomic_inc32(&num_active_markers); if((current_thread_id+1) == gc->num_active_markers ) state_transformation( gc, GC_CON_START_MARKERS, GC_CON_TRACING); while( gc->gc_concurrent_status == GC_CON_START_MARKERS ); retry: /*second step: mark dirty pool*/ Vector_Block* dirty_set = pool_get_entry(metadata->gc_dirty_set_pool); while(dirty_set){ POINTER_SIZE_INT* iter = vector_block_iterator_init(dirty_set); while(!vector_block_iterator_end(dirty_set,iter)){ Partial_Reveal_Object *p_obj = (Partial_Reveal_Object *)*iter; iter = vector_block_iterator_advance(dirty_set,iter); assert(p_obj!=NULL); //FIXME: restrict condition? obj_clear_dirty_in_table(p_obj); obj_clear_mark_in_table(p_obj, marker); if(obj_mark_gray_in_table(p_obj)) collector_tracestack_push((Collector*)marker, p_obj); num_dirtyset_slot ++; } vector_block_clear(dirty_set); pool_put_entry(metadata->free_set_pool, dirty_set); dirty_set = pool_get_entry(metadata->gc_dirty_set_pool); } /* put back the last trace_stack task */ pool_put_entry(metadata->mark_task_pool, marker->trace_stack); /* third step: iterate over the mark tasks and scan objects */ marker->trace_stack = free_task_pool_get_entry(metadata); Vector_Block *mark_task = pool_get_entry(metadata->mark_task_pool); while(mark_task){ POINTER_SIZE_INT *iter = vector_block_iterator_init(mark_task); while(!vector_block_iterator_end(mark_task,iter)){ Partial_Reveal_Object *p_obj = (Partial_Reveal_Object*)*iter; iter = vector_block_iterator_advance(mark_task,iter); trace_object(marker, p_obj); } /* run out one task, put back to the pool and grab another task */ vector_stack_clear(mark_task); pool_put_entry(metadata->free_task_pool, mark_task); mark_task = pool_get_entry(metadata->mark_task_pool); } /* if(current_thread_id == 0){ gc_prepare_dirty_set(marker->gc); }*/ gc_copy_local_dirty_set_to_global(gc); /* conditions to terminate mark: 1.All thread finished current job. 2.Flag is set to terminate concurrent mark. */ atomic_dec32(&num_active_markers); while(num_active_markers != 0 || !concurrent_mark_need_terminating_mc(gc) ) { if(!pool_is_empty(metadata->mark_task_pool) || !pool_is_empty(metadata->gc_dirty_set_pool)) { atomic_inc32(&num_active_markers); goto retry; } else if( current_thread_id >= mostly_con_long_marker_num ) { break; } apr_sleep(15000); } /* while(num_active_markers != 0 || !concurrent_mark_need_terminating_mc(gc)){ if(!pool_is_empty(metadata->mark_task_pool) || !pool_is_empty(metadata->gc_dirty_set_pool)){ atomic_inc32(&num_active_markers); goto retry; } }*/ /* put back the last mark stack to the free pool */ mark_task = (Vector_Block*)marker->trace_stack; vector_stack_clear(mark_task); pool_put_entry(metadata->free_task_pool, mark_task); marker->trace_stack = NULL; marker->num_dirty_slots_traced = num_dirtyset_slot; /* if(num_dirtyset_slot!=0) { lock(info_lock); INFO2("gc.marker", "marker ["<< current_thread_id <<"] processed dirty slot="<<num_dirtyset_slot); unlock(info_lock); }*/ return; }
void slide_compact_mspace(Collector* collector) { GC* gc = collector->gc; Mspace* mspace = (Mspace*)gc_get_mos((GC_Gen*)gc); Lspace* lspace = (Lspace*)gc_get_los((GC_Gen*)gc); unsigned int num_active_collectors = gc->num_active_collectors; /* Pass 1: ************************************************** *mark all live objects in heap, and save all the slots that *have references that are going to be repointed. */ TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: pass1: marking..."); unsigned int old_num = atomic_cas32( &num_marking_collectors, 0, num_active_collectors+1); if(collect_is_fallback()) mark_scan_heap_for_fallback(collector); else if(gc->tuner->kind != TRANS_NOTHING) mark_scan_heap_for_space_tune(collector); else mark_scan_heap(collector); old_num = atomic_inc32(&num_marking_collectors); /* last collector's world here */ if( ++old_num == num_active_collectors ) { if(!IGNORE_FINREF ) collector_identify_finref(collector); #ifndef BUILD_IN_REFERENT else { gc_set_weakref_sets(gc); gc_update_weakref_ignore_finref(gc); } #endif gc_identify_dead_weak_roots(gc); if( gc->tuner->kind != TRANS_NOTHING ) gc_compute_space_tune_size_after_marking(gc); //assert(!(gc->tuner->tuning_size % GC_BLOCK_SIZE_BYTES)); /* prepare for next phase */ gc_init_block_for_collectors(gc, mspace); #ifdef USE_32BITS_HASHCODE if(collect_is_fallback()) fallback_clear_fwd_obj_oi_init(collector); #endif last_block_for_dest = NULL; /* let other collectors go */ num_marking_collectors++; } while(num_marking_collectors != num_active_collectors + 1); TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: finish pass1 and start pass2: relocating mos&nos..."); /* Pass 2: ************************************************** assign target addresses for all to-be-moved objects */ atomic_cas32( &num_repointing_collectors, 0, num_active_collectors+1); #ifdef USE_32BITS_HASHCODE if(collect_is_fallback()) fallback_clear_fwd_obj_oi(collector); #endif mspace_compute_object_target(collector, mspace); old_num = atomic_inc32(&num_repointing_collectors); /*last collector's world here*/ if( ++old_num == num_active_collectors ) { if(lspace->move_object) { TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: pass2: relocating los ..."); lspace_compute_object_target(collector, lspace); } gc->collect_result = gc_collection_result(gc); if(!gc->collect_result) { num_repointing_collectors++; return; } gc_reset_block_for_collectors(gc, mspace); gc_init_block_for_fix_repointed_refs(gc, mspace); num_repointing_collectors++; } while(num_repointing_collectors != num_active_collectors + 1); if(!gc->collect_result) return; TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: finish pass2 and start pass3: repointing..."); /* Pass 3: ************************************************** *update all references whose objects are to be moved */ old_num = atomic_cas32( &num_fixing_collectors, 0, num_active_collectors+1); mspace_fix_repointed_refs(collector, mspace); old_num = atomic_inc32(&num_fixing_collectors); /*last collector's world here */ if( ++old_num == num_active_collectors ) { lspace_fix_repointed_refs(collector, lspace); gc_fix_rootset(collector, FALSE); gc_init_block_for_sliding_compact(gc, mspace); /*LOS_Shrink: This operation moves objects in LOS, and should be part of Pass 4 *lspace_sliding_compact is not binded with los shrink, we could slide compact los individually. *So we use a flag lspace->move_object here, not tuner->kind == TRANS_FROM_LOS_TO_MOS. */ if(lspace->move_object) lspace_sliding_compact(collector, lspace); /*The temp blocks for storing interim infomation is copied to the real place they should be. *And the space of the blocks are freed, which is alloced in gc_space_tuner_init_fake_blocks_for_los_shrink. */ last_block_for_dest = (Block_Header *)round_down_to_size((POINTER_SIZE_INT)last_block_for_dest->base, GC_BLOCK_SIZE_BYTES); if(gc->tuner->kind == TRANS_FROM_LOS_TO_MOS) gc_space_tuner_release_fake_blocks_for_los_shrink(gc); num_fixing_collectors++; } while(num_fixing_collectors != num_active_collectors + 1); TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: finish pass3 and start pass4: moving..."); /* Pass 4: ************************************************** move objects */ atomic_cas32( &num_moving_collectors, 0, num_active_collectors); mspace_sliding_compact(collector, mspace); atomic_inc32(&num_moving_collectors); while(num_moving_collectors != num_active_collectors); TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: finish pass4 and start pass 5: restoring obj_info..."); /* Pass 5: ************************************************** restore obj_info */ atomic_cas32( &num_restoring_collectors, 0, num_active_collectors+1); collector_restore_obj_info(collector); #ifdef USE_32BITS_HASHCODE collector_attach_hashcode(collector); #endif old_num = atomic_inc32(&num_restoring_collectors); if( ++old_num == num_active_collectors ) { if(gc->tuner->kind != TRANS_NOTHING) mspace_update_info_after_space_tuning(mspace); num_restoring_collectors++; } while(num_restoring_collectors != num_active_collectors + 1); /* Dealing with out of memory in mspace */ void* mspace_border = &mspace->blocks[mspace->free_block_idx - mspace->first_block_idx]; if( mspace_border > nos_boundary) { atomic_cas32( &num_extending_collectors, 0, num_active_collectors); mspace_extend_compact(collector); atomic_inc32(&num_extending_collectors); while(num_extending_collectors != num_active_collectors); } TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: finish pass5 and done."); return; }
static void mspace_move_objects(Collector* collector, Mspace* mspace) { Block_Header* curr_block = collector->cur_compact_block; Block_Header* dest_block = collector->cur_target_block; Block_Header *local_last_dest = dest_block; void* dest_sector_addr = dest_block->base; Boolean is_fallback = collect_is_fallback(); #ifdef USE_32BITS_HASHCODE Hashcode_Buf* old_hashcode_buf = NULL; Hashcode_Buf* new_hashcode_buf = hashcode_buf_create(); hashcode_buf_init(new_hashcode_buf); #endif #ifdef GC_GEN_STATS GC_Gen_Collector_Stats* stats = (GC_Gen_Collector_Stats*)collector->stats; #endif unsigned int debug_num_live_obj = 0; while( curr_block ){ if(verify_live_heap){ atomic_inc32(&debug_num_compact_blocks); debug_num_live_obj = 0; } void* start_pos; Partial_Reveal_Object* p_obj = block_get_first_marked_object(curr_block, &start_pos); if( !p_obj ){ #ifdef USE_32BITS_HASHCODE hashcode_buf_clear(curr_block->hashcode_buf); #endif assert(!verify_live_heap ||debug_num_live_obj == curr_block->num_live_objs); curr_block = mspace_get_next_compact_block(collector, mspace); continue; } int curr_sector = OBJECT_INDEX_TO_OFFSET_TABLE(p_obj); void* src_sector_addr = p_obj; while( p_obj ){ debug_num_live_obj++; assert( obj_is_marked_in_vt(p_obj)); /* we don't check if it's set, since only non-forwarded objs from last NOS partial-forward collection need it. */ obj_clear_dual_bits_in_oi(p_obj); #ifdef GC_GEN_STATS gc_gen_collector_update_moved_nos_mos_obj_stats_major(stats, vm_object_size(p_obj)); #endif #ifdef USE_32BITS_HASHCODE move_compact_process_hashcode(p_obj, curr_block->hashcode_buf, new_hashcode_buf); #endif POINTER_SIZE_INT curr_sector_size = (POINTER_SIZE_INT)start_pos - (POINTER_SIZE_INT)src_sector_addr; /* check if dest block is not enough to hold this sector. If yes, grab next one */ POINTER_SIZE_INT block_end = (POINTER_SIZE_INT)GC_BLOCK_END(dest_block); if( ((POINTER_SIZE_INT)dest_sector_addr + curr_sector_size) > block_end ){ dest_block->new_free = dest_sector_addr; #ifdef USE_32BITS_HASHCODE block_swap_hashcode_buf(dest_block, &new_hashcode_buf, &old_hashcode_buf); #endif dest_block = mspace_get_next_target_block(collector, mspace); if(dest_block == NULL){ #ifdef USE_32BITS_HASHCODE hashcode_buf_rollback_new_entry(old_hashcode_buf); #endif collector->result = FALSE; return; } #ifdef USE_32BITS_HASHCODE hashcode_buf_transfer_new_entry(old_hashcode_buf, new_hashcode_buf); #endif if((!local_last_dest) || (dest_block->block_idx > local_last_dest->block_idx)) local_last_dest = dest_block; block_end = (POINTER_SIZE_INT)GC_BLOCK_END(dest_block); dest_sector_addr = dest_block->base; } assert(((POINTER_SIZE_INT)dest_sector_addr + curr_sector_size) <= block_end ); Partial_Reveal_Object *last_obj_end = (Partial_Reveal_Object *)start_pos; /* check if next live object is out of current sector. If not, loop back to continue within this sector. FIXME:: we should add a condition for block check (?) */ p_obj = block_get_next_marked_object(curr_block, &start_pos); if ((p_obj != NULL) && (OBJECT_INDEX_TO_OFFSET_TABLE(p_obj) == curr_sector)) { if(last_obj_end != p_obj) obj_set_vt_to_next_obj(last_obj_end, p_obj); continue; } /* current sector is done, let's move it. */ POINTER_SIZE_INT sector_distance = (POINTER_SIZE_INT)src_sector_addr - (POINTER_SIZE_INT)dest_sector_addr; assert((sector_distance % GC_OBJECT_ALIGNMENT) == 0); /* if sector_distance is zero, we don't do anything. But since block offset table is never cleaned, we have to set 0 to it. */ curr_block->table[curr_sector] = sector_distance; if(sector_distance != 0) memmove(dest_sector_addr, src_sector_addr, curr_sector_size); #ifdef USE_32BITS_HASHCODE hashcode_buf_refresh_new_entry(new_hashcode_buf, sector_distance); #endif dest_sector_addr = (void*)((POINTER_SIZE_INT)dest_sector_addr + curr_sector_size); src_sector_addr = p_obj; curr_sector = OBJECT_INDEX_TO_OFFSET_TABLE(p_obj); } #ifdef USE_32BITS_HASHCODE hashcode_buf_clear(curr_block->hashcode_buf); #endif assert(!verify_live_heap ||debug_num_live_obj == curr_block->num_live_objs); curr_block = mspace_get_next_compact_block(collector, mspace); } dest_block->new_free = dest_sector_addr; collector->cur_target_block = local_last_dest; #ifdef USE_32BITS_HASHCODE old_hashcode_buf = block_set_hashcode_buf(dest_block, new_hashcode_buf); hashcode_buf_destory(old_hashcode_buf); #endif return; }
void mark_scan_pool(Collector* collector) { GC* gc = collector->gc; GC_Metadata* metadata = gc->metadata; #ifdef GC_GEN_STATS GC_Gen_Collector_Stats* stats = (GC_Gen_Collector_Stats*)collector->stats; #endif /* reset the num_finished_collectors to be 0 by one collector. This is necessary for the barrier later. */ unsigned int num_active_collectors = gc->num_active_collectors; atomic_cas32( &num_finished_collectors, 0, num_active_collectors); collector->trace_stack = free_task_pool_get_entry(metadata); Vector_Block* root_set = pool_iterator_next(metadata->gc_rootset_pool); /* first step: copy all root objects to mark tasks. FIXME:: can be done sequentially before coming here to eliminate atomic ops */ while(root_set){ POINTER_SIZE_INT* iter = vector_block_iterator_init(root_set); while(!vector_block_iterator_end(root_set,iter)){ REF *p_ref = (REF *)*iter; iter = vector_block_iterator_advance(root_set,iter); Partial_Reveal_Object *p_obj = read_slot(p_ref); /* root ref can't be NULL, (remset may have NULL ref entry, but this function is only for ALGO_MAJOR */ assert(p_obj!=NULL); /* we have to mark the object before put it into marktask, because it is possible to have two slots containing a same object. They will be scanned twice and their ref slots will be recorded twice. Problem occurs after the ref slot is updated first time with new position and the second time the value is the ref slot is the old position as expected. This can be worked around if we want. */ if(obj_mark_in_vt(p_obj)){ collector_tracestack_push(collector, p_obj); #ifdef GC_GEN_STATS gc_gen_collector_update_rootset_ref_num(stats); gc_gen_collector_update_marked_obj_stats_major(stats); #endif } } root_set = pool_iterator_next(metadata->gc_rootset_pool); } /* put back the last trace_stack task */ pool_put_entry(metadata->mark_task_pool, collector->trace_stack); /* second step: iterate over the mark tasks and scan objects */ /* get a task buf for the mark stack */ collector->trace_stack = free_task_pool_get_entry(metadata); retry: Vector_Block* mark_task = pool_get_entry(metadata->mark_task_pool); while(mark_task){ POINTER_SIZE_INT* iter = vector_block_iterator_init(mark_task); while(!vector_block_iterator_end(mark_task,iter)){ Partial_Reveal_Object* p_obj = (Partial_Reveal_Object *)*iter; iter = vector_block_iterator_advance(mark_task,iter); /* FIXME:: we should not let mark_task empty during working, , other may want to steal it. degenerate my stack into mark_task, and grab another mark_task */ trace_object(collector, p_obj); } /* run out one task, put back to the pool and grab another task */ vector_stack_clear(mark_task); pool_put_entry(metadata->free_task_pool, mark_task); mark_task = pool_get_entry(metadata->mark_task_pool); } /* termination detection. This is also a barrier. NOTE:: We can simply spin waiting for num_finished_collectors, because each generated new task would surely be processed by its generating collector eventually. So code below is only for load balance optimization. */ atomic_inc32(&num_finished_collectors); while(num_finished_collectors != num_active_collectors){ if( !pool_is_empty(metadata->mark_task_pool)){ atomic_dec32(&num_finished_collectors); goto retry; } } /* put back the last mark stack to the free pool */ mark_task = (Vector_Block*)collector->trace_stack; vector_stack_clear(mark_task); pool_put_entry(metadata->free_task_pool, mark_task); collector->trace_stack = NULL; return; }
void move_compact_mspace(Collector* collector) { GC* gc = collector->gc; Mspace* mspace = (Mspace*)gc_get_mos((GC_Gen*)gc); Lspace* lspace = (Lspace*)gc_get_los((GC_Gen*)gc); Blocked_Space* nos = (Blocked_Space*)gc_get_nos((GC_Gen*)gc); unsigned int num_active_collectors = gc->num_active_collectors; Boolean is_fallback = collect_is_fallback(); /* Pass 1: ************************************************** mark all live objects in heap, and save all the slots that have references that are going to be repointed */ TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: pass1: mark live objects in heap ..."); unsigned int old_num = atomic_cas32( &num_marking_collectors, 0, num_active_collectors+1); if(!is_fallback) mark_scan_heap(collector); else mark_scan_heap_for_fallback(collector); old_num = atomic_inc32(&num_marking_collectors); if( ++old_num == num_active_collectors ){ /* last collector's world here */ /* prepare for next phase */ gc_init_block_for_collectors(gc, mspace); if(!IGNORE_FINREF ) collector_identify_finref(collector); #ifndef BUILD_IN_REFERENT else { gc_set_weakref_sets(gc); gc_update_weakref_ignore_finref(gc); } #endif gc_identify_dead_weak_roots(gc); #ifdef USE_32BITS_HASHCODE if((!LOS_ADJUST_BOUNDARY) && (is_fallback)) fallback_clear_fwd_obj_oi_init(collector); #endif debug_num_compact_blocks = 0; /* let other collectors go */ num_marking_collectors++; } while(num_marking_collectors != num_active_collectors + 1); TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: finish pass1"); /* Pass 2: ************************************************** move object and set the forwarding offset table */ TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: pass2: move object and set the forwarding offset table ..."); atomic_cas32( &num_moving_collectors, 0, num_active_collectors+1); #ifdef USE_32BITS_HASHCODE if(is_fallback) fallback_clear_fwd_obj_oi(collector); #endif mspace_move_objects(collector, mspace); old_num = atomic_inc32(&num_moving_collectors); if( ++old_num == num_active_collectors ){ /* single thread world */ if(lspace->move_object) lspace_compute_object_target(collector, lspace); gc->collect_result = gc_collection_result(gc); if(!gc->collect_result){ num_moving_collectors++; return; } if(verify_live_heap){ assert( debug_num_compact_blocks == mspace->num_managed_blocks + nos->num_managed_blocks ); debug_num_compact_blocks = 0; } gc_reset_block_for_collectors(gc, mspace); blocked_space_block_iterator_init((Blocked_Space*)mspace); num_moving_collectors++; } while(num_moving_collectors != num_active_collectors + 1); if(!gc->collect_result) return; TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: finish pass2"); /* Pass 3: ************************************************** update all references whose pointed objects were moved */ TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: pass3: update all references ..."); old_num = atomic_cas32( &num_fixing_collectors, 0, num_active_collectors+1); mspace_fix_repointed_refs(collector, mspace); old_num = atomic_inc32(&num_fixing_collectors); if( ++old_num == num_active_collectors ){ /* last collector's world here */ lspace_fix_repointed_refs(collector, lspace); gc_fix_rootset(collector, FALSE); if(lspace->move_object) lspace_sliding_compact(collector, lspace); num_fixing_collectors++; } while(num_fixing_collectors != num_active_collectors + 1); TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: finish pass3"); /* Pass 4: ************************************************** restore obj_info . Actually only LOS needs it. Since oi is recorded for new address, so the restoration doesn't need to to specify space. */ TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: pass4: restore obj_info ..."); atomic_cas32( &num_restoring_collectors, 0, num_active_collectors); collector_restore_obj_info(collector); atomic_inc32(&num_restoring_collectors); while(num_restoring_collectors != num_active_collectors); /* Dealing with out of memory in mspace */ if(mspace->free_block_idx > nos->first_block_idx){ atomic_cas32( &num_extending_collectors, 0, num_active_collectors); mspace_extend_compact(collector); atomic_inc32(&num_extending_collectors); while(num_extending_collectors != num_active_collectors); } TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: finish pass4"); /* Leftover: ************************************************** */ if( (POINTER_SIZE_INT)collector->thread_handle != 0 ){ TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"] finished"); return; } TRACE2("gc.process", "GC: collector[0] finished"); return; }
void wspace_mark_scan_concurrent(Conclctor* marker) { //marker->time_measurement_start = time_now(); GC *gc = marker->gc; GC_Metadata *metadata = gc->metadata; /* reset the num_finished_collectors to be 0 by one collector. This is necessary for the barrier later. */ unsigned int current_thread_id = atomic_inc32(&num_active_markers); marker->trace_stack = free_task_pool_get_entry(metadata); Vector_Block *root_set = pool_iterator_next(metadata->gc_rootset_pool); /* first step: copy all root objects to mark tasks.*/ while(root_set){ POINTER_SIZE_INT *iter = vector_block_iterator_init(root_set); while(!vector_block_iterator_end(root_set,iter)){ Partial_Reveal_Object *p_obj = (Partial_Reveal_Object *)*iter; iter = vector_block_iterator_advance(root_set,iter); assert(p_obj!=NULL); assert(address_belongs_to_gc_heap(p_obj, gc)); //if(obj_mark_gray_in_table(p_obj, &root_set_obj_size)) if(obj_mark_gray_in_table(p_obj)) collector_tracestack_push((Collector*)marker, p_obj); } root_set = pool_iterator_next(metadata->gc_rootset_pool); } /* put back the last trace_stack task */ pool_put_entry(metadata->mark_task_pool, marker->trace_stack); marker->trace_stack = free_task_pool_get_entry(metadata); state_transformation( gc, GC_CON_START_MARKERS, GC_CON_TRACING); retry: gc_copy_local_dirty_set_to_global(marker->gc); /*second step: mark dirty object snapshot pool*/ Vector_Block* dirty_set = pool_get_entry(metadata->gc_dirty_set_pool); while(dirty_set){ POINTER_SIZE_INT* iter = vector_block_iterator_init(dirty_set); while(!vector_block_iterator_end(dirty_set,iter)){ Partial_Reveal_Object *p_obj = (Partial_Reveal_Object *)*iter; iter = vector_block_iterator_advance(dirty_set,iter); if(p_obj==NULL) { //FIXME: restrict? RAISE_ERROR; } marker->num_dirty_slots_traced++; if(obj_mark_gray_in_table(p_obj)) collector_tracestack_push((Collector*)marker, p_obj); } vector_block_clear(dirty_set); pool_put_entry(metadata->free_set_pool, dirty_set); dirty_set = pool_get_entry(metadata->gc_dirty_set_pool); } /* put back the last trace_stack task */ pool_put_entry(metadata->mark_task_pool, marker->trace_stack); /* third step: iterate over the mark tasks and scan objects */ /* get a task buf for the mark stack */ marker->trace_stack = free_task_pool_get_entry(metadata); Vector_Block *mark_task = pool_get_entry(metadata->mark_task_pool); while(mark_task){ POINTER_SIZE_INT *iter = vector_block_iterator_init(mark_task); while(!vector_block_iterator_end(mark_task,iter)){ Partial_Reveal_Object *p_obj = (Partial_Reveal_Object*)*iter; iter = vector_block_iterator_advance(mark_task,iter); trace_object(marker, p_obj); } /* run out one task, put back to the pool and grab another task */ vector_stack_clear(mark_task); pool_put_entry(metadata->free_task_pool, mark_task); mark_task = pool_get_entry(metadata->mark_task_pool); } /* termination condition: 1.all thread finished current job. 2.local snapshot vectors are empty. 3.global snapshot pool is empty. */ atomic_dec32(&num_active_markers); while(num_active_markers != 0 || !concurrent_mark_need_terminating_otf(gc)){ if(!pool_is_empty(metadata->mark_task_pool) || !concurrent_mark_need_terminating_otf(gc)){ atomic_inc32(&num_active_markers); goto retry; } apr_sleep(15000); } state_transformation( gc, GC_CON_TRACING, GC_CON_TRACE_DONE ); /* put back the last mark stack to the free pool */ mark_task = (Vector_Block*)marker->trace_stack; vector_stack_clear(mark_task); pool_put_entry(metadata->free_task_pool, mark_task); marker->trace_stack = NULL; assert(pool_is_empty(metadata->gc_dirty_set_pool)); //INFO2("gc.con.info", "<stage 5>first marker finishes its job"); return; }
static void collector_trace_rootsets(Collector* collector) { GC* gc = collector->gc; GC_Metadata* metadata = gc->metadata; #ifdef GC_GEN_STATS GC_Gen_Collector_Stats* stats = (GC_Gen_Collector_Stats*)collector->stats; #endif unsigned int num_active_collectors = gc->num_active_collectors; atomic_cas32( &num_finished_collectors, 0, num_active_collectors); Space* space = collector->collect_space; collector->trace_stack = free_task_pool_get_entry(metadata); /* find root slots saved by 1. active mutators, 2. exited mutators, 3. last cycle collectors */ Vector_Block* root_set = pool_iterator_next(metadata->gc_rootset_pool); /* first step: copy all root objects to trace tasks. */ TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: copy root objects to trace stack ..."); while(root_set){ POINTER_SIZE_INT* iter = vector_block_iterator_init(root_set); while(!vector_block_iterator_end(root_set,iter)){ REF *p_ref = (REF *)*iter; iter = vector_block_iterator_advance(root_set, iter); assert(*p_ref); /* root ref cann't be NULL, but remset can be */ collector_tracestack_push(collector, p_ref); #ifdef GC_GEN_STATS gc_gen_collector_update_rootset_ref_num(stats); #endif } root_set = pool_iterator_next(metadata->gc_rootset_pool); } /* put back the last trace_stack task */ pool_put_entry(metadata->mark_task_pool, collector->trace_stack); /* second step: iterate over the trace tasks and forward objects */ collector->trace_stack = free_task_pool_get_entry(metadata); TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: finish copying root objects to trace stack."); TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: trace and forward objects ..."); retry: Vector_Block* trace_task = pool_get_entry(metadata->mark_task_pool); while(trace_task){ POINTER_SIZE_INT* iter = vector_block_iterator_init(trace_task); while(!vector_block_iterator_end(trace_task,iter)){ REF *p_ref = (REF *)*iter; iter = vector_block_iterator_advance(trace_task, iter); #ifdef PREFETCH_SUPPORTED /* DO PREFETCH */ if( mark_prefetch ) { if(!vector_block_iterator_end(trace_task, iter)) { REF *pref= (REF*) *iter; PREFETCH( read_slot(pref)); } } #endif trace_object(collector, p_ref); if(collector->result == FALSE) break; /* force return */ } vector_stack_clear(trace_task); pool_put_entry(metadata->free_task_pool, trace_task); if(collector->result == FALSE){ gc_task_pool_clear(metadata->mark_task_pool); break; /* force return */ } trace_task = pool_get_entry(metadata->mark_task_pool); } /* A collector comes here when seeing an empty mark_task_pool. The last collector will ensure all the tasks are finished.*/ atomic_inc32(&num_finished_collectors); while(num_finished_collectors != num_active_collectors){ if( pool_is_empty(metadata->mark_task_pool)) continue; /* we can't grab the task here, because of a race condition. If we grab the task, and the pool is empty, other threads may fall to this barrier and then pass. */ atomic_dec32(&num_finished_collectors); goto retry; } TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: finish tracing and forwarding objects."); /* now we are done, but each collector has a private stack that is empty */ trace_task = (Vector_Block*)collector->trace_stack; vector_stack_clear(trace_task); pool_put_entry(metadata->free_task_pool, trace_task); collector->trace_stack = NULL; return; }
static void collector_trace_rootsets(Collector* collector) { GC* gc = collector->gc; GC_Metadata* metadata = gc->metadata; #ifdef GC_GEN_STATS GC_Gen_Collector_Stats* stats = (GC_Gen_Collector_Stats*)collector->stats; #endif unsigned int num_active_collectors = gc->num_active_collectors; atomic_cas32( &num_finished_collectors, 0, num_active_collectors); Space* space = collector->collect_space; collector->trace_stack = free_task_pool_get_entry(metadata); /* find root slots saved by 1. active mutators, 2. exited mutators, 3. last cycle collectors */ Vector_Block* root_set = pool_iterator_next(metadata->gc_rootset_pool); /* first step: copy all root objects to trace tasks. */ TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: copy root objects to trace stack ......"); while(root_set){ POINTER_SIZE_INT* iter = vector_block_iterator_init(root_set); while(!vector_block_iterator_end(root_set,iter)){ REF *p_ref = (REF *)*iter; iter = vector_block_iterator_advance(root_set,iter); if(!*p_ref) continue; /* root ref cann't be NULL, but remset can be */ Partial_Reveal_Object *p_obj = read_slot(p_ref); #ifdef GC_GEN_STATS gc_gen_collector_update_rootset_ref_num(stats); #endif if(obj_belongs_to_nos(p_obj)){ collector_tracestack_push(collector, p_ref); } } root_set = pool_iterator_next(metadata->gc_rootset_pool); } /* put back the last trace_stack task */ pool_put_entry(metadata->mark_task_pool, collector->trace_stack); /* second step: iterate over the trace tasks and forward objects */ collector->trace_stack = free_task_pool_get_entry(metadata); TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: finish copying root objects to trace stack."); TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: trace and forward objects ......"); retry: Vector_Block* trace_task = pool_get_entry(metadata->mark_task_pool); while(trace_task){ POINTER_SIZE_INT* iter = vector_block_iterator_init(trace_task); while(!vector_block_iterator_end(trace_task,iter)){ REF *p_ref = (REF *)*iter; iter = vector_block_iterator_advance(trace_task,iter); assert(*p_ref); /* a task can't be NULL, it was checked before put into the task stack */ #ifdef PREFETCH_SUPPORTED /* DO PREFETCH */ if( mark_prefetch ) { if(!vector_block_iterator_end(trace_task, iter)) { REF *pref= (REF*) *iter; PREFETCH( read_slot(pref)); } } #endif /* in sequential version, we only trace same object once, but we were using a local hashset for that, which couldn't catch the repetition between multiple collectors. This is subject to more study. */ /* FIXME:: we should not let root_set empty during working, other may want to steal it. degenerate my stack into root_set, and grab another stack */ /* a task has to belong to collected space, it was checked before put into the stack */ trace_object(collector, p_ref); if(collector->result == FALSE) break; /* force return */ } vector_stack_clear(trace_task); pool_put_entry(metadata->free_task_pool, trace_task); if(collector->result == FALSE){ gc_task_pool_clear(metadata->mark_task_pool); break; /* force return */ } trace_task = pool_get_entry(metadata->mark_task_pool); } atomic_inc32(&num_finished_collectors); while(num_finished_collectors != num_active_collectors){ if( pool_is_empty(metadata->mark_task_pool)) continue; /* we can't grab the task here, because of a race condition. If we grab the task, and the pool is empty, other threads may fall to this barrier and then pass. */ atomic_dec32(&num_finished_collectors); goto retry; } TRACE2("gc.process", "GC: collector["<<((POINTER_SIZE_INT)collector->thread_handle)<<"]: finish tracing and forwarding objects."); /* now we are done, but each collector has a private stack that is empty */ trace_task = (Vector_Block*)collector->trace_stack; vector_stack_clear(trace_task); pool_put_entry(metadata->free_task_pool, trace_task); collector->trace_stack = NULL; return; }
// Wait on a flag. void wait_flag(volatile uint32_t *flag, uint32_t expect) { atomic_inc32((uint32_t *)flag); while (*flag != expect); }