void remove_unused_entities_from(EntityCollection& entities) { typedef typename EntityCollection::value_type EntityType; vector<EntityType*> to_remove; for (auto& entity : entities) { if (!is_referenced(entity)) { RENDERER_LOG_DEBUG("entity " FMT_ENTITY " is not referenced and will be removed.", entity.get_path().c_str(), entity.get_uid()); to_remove.push_back(&entity); remove_unreferenced_entity(entity); } } for (EntityType* entity : to_remove) { RENDERER_LOG_DEBUG("removing entity " FMT_ENTITY "...", entity->get_path().c_str(), entity->get_uid()); entities.remove(entity); } }
/** * This function will select a page (based on second chance) * and swap it out to file system. However if the selected page * was not dirty then we just need to mark it swapped again and * can return immediatly. * We stop the initiator thread as long as we're swapping * data out to the file system. The thread is restarted in the * write callback function (above). * * @param initiator ID of the thread who caused the swapping to happen * @return SWAPPING_PENDING in case we need to write the page to the disk * SWAPPING_COMPLETE in case the page was not dirty * OUT_OF_SWAP_SPACE if our swap space is already full * NO_PAGE_AVAILABLE if second_chance_select did not find a page */ int swap_out(L4_ThreadId_t initiator) { page_queue_item* page = second_chance_select(&active_pages_head); if(page == NULL) { return NO_PAGE_AVAILABLE; } assert(!is_referenced(page)); // decide where in the swap file our page will be if(page->swap_offset < 0 && (page->swap_offset = allocate_swap_entry()) < 0) return OUT_OF_SWAP_SPACE; dprintf(1, "swap_out: Second chance selected page: thread:0x%X vaddr:0x%X swap_offset:0x%X\n", page->tid, page->virtual_address, page->swap_offset); if(is_dirty(page)) { dprintf(1, "Selected page is dirty, need to write to swap space\n"); page->to_swap = PAGESIZE; page->initiator = initiator; TAILQ_INSERT_TAIL(&swapping_pages_head, page, entries); file_table_entry* swap_fd = get_process(root_thread_g)->filetable[SWAP_FD]; assert(swap_fd != NULL); data_ptr physical_page = pager_physical_lookup(page->tid, page->virtual_address); assert(physical_page != NULL); // write page in swap file assert(PAGESIZE % BATCH_SIZE == 0); for(int write_offset=0; write_offset < page->to_swap; write_offset += BATCH_SIZE) { nfs_write( &swap_fd->file->nfs_handle, page->swap_offset + write_offset, BATCH_SIZE, physical_page + write_offset, &swap_write_callback, (int)page ); } return SWAPPING_PENDING; } else { assert(page->swap_offset >= 0); page_table_entry* pte = pager_table_lookup(page->tid, page->virtual_address); frame_free(CLEAR_LOWER_BITS(pte->address)); mark_swapped(pte, page->swap_offset); free(page); return SWAPPING_COMPLETE; } }
void remove_unreferenced_entity(Entity& entity) { assert(!is_referenced(entity)); for (auto& referenced_entity : m_outgoing_refs[&entity]) { // Remove references from referenced entities toward this entity. IncomingRefVec& incoming_refs = m_incoming_refs[referenced_entity.m_entity]; erase_if( incoming_refs, [&entity](const IncomingRef& ref) { return ref.m_entity == &entity; }); if (incoming_refs.empty()) m_incoming_refs.erase(referenced_entity.m_entity); if (!is_referenced(*referenced_entity.m_entity)) { RENDERER_LOG_DEBUG("entity " FMT_ENTITY " is not referenced and will be removed.", referenced_entity.m_entity->get_path().c_str(), referenced_entity.m_entity->get_uid()); remove_unreferenced_entity(*referenced_entity.m_entity); RENDERER_LOG_DEBUG("removing entity " FMT_ENTITY "...", referenced_entity.m_entity->get_path().c_str(), referenced_entity.m_entity->get_uid()); assert(referenced_entity.m_map || referenced_entity.m_vector); if (referenced_entity.m_map != nullptr) referenced_entity.m_map->remove(referenced_entity.m_entity); else referenced_entity.m_vector->remove(referenced_entity.m_entity); } } // Remove references from this entity toward other entities. m_outgoing_refs.erase(&entity); }
bool RegisterAllocator::has_free(int count, Assembler::Register* next_table, Assembler::Register next, bool spill) { const Register current = next; do { next = next_table[next]; // Check to see whether or not the register is available for allocation. if (!is_referenced(next) && (spill || !is_mapping_something(next))) { count--; if (count == 0) return true; } } while(next != current); // Couldn't allocate a register without spilling. return false; }
Assembler::Register RegisterAllocator::spill(Assembler::Register* next_table, Assembler::Register& next) { // Use a round-robin strategy to spill the registers. const Register current = next; do { next = next_table[next]; // Check to see whether or not the register is available for spilling. if (!is_referenced(next)) { // Spill the register. spill(next); // Return the register. return next; } } while(next != current); // Couldn't allocate a register without spilling. return Assembler::no_reg; }
Assembler::Register RegisterAllocator::allocate(Assembler::Register reg) { // For now we allow registers that aren't handled by the register allocator // to be allocated. This might seem a bit strange. if (reg < (Assembler::Register)Assembler::number_of_registers) { GUARANTEE(!is_referenced(reg), "Cannot allocate referenced register"); // Setup a reference to the newly allocated register. reference(reg); // Spill the register. spill(reg); //cse wipe_notation_of(reg); } // Return the register. return reg; }
/** * Second chance select implementation for choosing pages to * swap out. This will loop through the queue and find the * oldest unreferenced page. * Note: Since we use software emulated reference bits, in case * everything is mapped in the HW page table this degenerates * to pure FIFO. * * @param page_queue * @return oldest unreferenced page (removed from the queue) */ static page_queue_item* second_chance_select(struct pages_head* page_queue) { page_queue_item* page; // do a second chance search for a page over all currently active pages for(page = page_queue->tqh_first; page != NULL; page = page_queue->tqh_first) { if(is_referenced(page)) { TAILQ_REMOVE(page_queue, page, entries); // remove oldest page dereference(page); TAILQ_INSERT_TAIL(page_queue, page, entries); // insert at front } else { // remove the page from the working set TAILQ_REMOVE(page_queue, page, entries); return page; } } return NULL; // no pages in queue (bad) }
Assembler::Register RegisterAllocator::allocate_or_fail(Assembler::Register* next_table, Assembler::Register& next) { #if ENABLE_CSE //try to free the free register without notation firstly. Register next_with_notation = Assembler::no_reg; #endif // Use a round-robin strategy to allocate the registers. const Register current = next; do { next = next_table[next]; // Check to see whether or not the register is available for allocation. if (!is_referenced(next) && !is_mapping_something(next)) { // Setup a reference to the newly allocated register. #if ENABLE_CSE if (!is_notated(next)) { REFERENCE_AND_RETURN(next); } else if ( next_with_notation == Assembler::no_reg) { next_with_notation = next; } #else REFERENCE_AND_RETURN(next); #endif } } while(next != current); // Couldn't allocate a register without spilling. #if ENABLE_CSE if ( next_with_notation != Assembler::no_reg ) { REFERENCE_AND_RETURN(next_with_notation); } #endif return Assembler::no_reg; }
/** * Write callback for the NFS write function in case we swap out. * Note that because we cannot write 4096 bytes in one chunk * we split the writes up in 512 byte chunks. So this function * is called multiple times by the NFS library. * * @param token pointer to the page queue item we are swapping out */ static void swap_write_callback(uintptr_t token, int status, fattr_t *attr) { page_queue_item* page = (page_queue_item*) token; page_table_entry* pte = pager_table_lookup(page->tid, page->virtual_address); file_table_entry* swap_fd = get_process(root_thread_g)->filetable[SWAP_FD]; switch (status) { case NFS_OK: { // update file attributes swap_fd->file->status.st_size = attr->size; swap_fd->file->status.st_atime = attr->atime.useconds / 1000; page->to_swap -= BATCH_SIZE; // swapping complete, can we now finally free the frame? if(page->to_swap == 0) { if(!is_referenced(page)) { dprintf(1, "page is swapped out\n"); if(!page->process_deleted) { frame_free(CLEAR_LOWER_BITS(pte->address)); mark_swapped(pte, page->swap_offset); // restart the thread who ran out of memory if(!L4_IsNilThread(page->initiator)) send_ipc_reply(page->initiator, L4_PAGEFAULT, 0); } else {} // page was killed, nothing to do TAILQ_REMOVE(&swapping_pages_head, page, entries); free(page); } else { dprintf(1, "page is swapped out but referenced in the mean time\n"); // page has been referenced inbetween swapping out // we need to restart the whole swap procedure TAILQ_REMOVE(&swapping_pages_head, page, entries); if(!L4_IsNilThread(page->initiator)) { send_ipc_reply(page->initiator, L4_PAGEFAULT, 0); } if(!page->process_deleted) { page->initiator = L4_nilthread; TAILQ_INSERT_TAIL(&active_pages_head, page, entries); } else { free(page); } } } } break; case NFSERR_NOSPC: dprintf(0, "System ran out of memory _and_ swap space (this is bad).\n"); TAILQ_REMOVE(&swapping_pages_head, page, entries); free(page); assert(FALSE); break; default: dprintf(0, "%s: Bad NFS status (%d) from callback.\n", __FUNCTION__, status); TAILQ_REMOVE(&swapping_pages_head, page, entries); free(page); assert(FALSE); // We could probably try to restart swapping here but since it failed before // we don't see much point in this. break; } }
Assembler::Register RegisterAllocator::allocate_float_register() { #if ENABLE_ARM_VFP #if 0 // This change improve the caching of locals in registers. // But the more value that are cached in registers the more // values need to be written into memory at method call. // Find the best fit { const Register start = Register(_next_float_allocate & ~1); Register next = next_vfp_register(start, (Assembler::number_of_float_registers - 8)); do { const bool f0 = !is_referenced(Register(next+0)) && !is_mapping_something(Register(next+0)); const bool f1 = !is_referenced(Register(next+1)) && !is_mapping_something(Register(next+1)); if ((f0 + f1) == 1) { Register r = next; if ( f0 == false) { r = Register(next + 1); } reference(r); return r; } next = next_vfp_register(next, 2); } while (next != start); } #endif // Find a free register { const Register start = _next_float_allocate; Register next = start; do { if( !is_referenced(next) && !is_mapping_something(next)) { reference(next); _next_float_allocate = next_vfp_register(next, 1); return next; } next = next_vfp_register(next, 1); } while (next != start); } // Nothing free spill registers { const Register start = _next_float_spill; Register next = start; do { if (is_referenced(next)) { continue; } #if ENABLE_INLINE if (Compiler::current()->conforming_frame()->not_null() && Compiler::current()->conforming_frame()->is_mapping_something(next)) { continue; } #endif // ENABLE_INLINE spill(next); reference(next); _next_float_spill = next_vfp_register(next, 1); return next; } while ((next = next_vfp_register(next, 1)) != start); } GUARANTEE(false, "Cannot allocate VFP registers for a float"); return Assembler::no_reg; #else // !ENABLE_ARM_VFP return allocate(_next_register_table, _next_float_allocate, _next_float_spill); #endif // ENABLE_ARM_VFP }
Assembler::Register RegisterAllocator::allocate_double_register() { { const Register start = Register( next_vfp_register( _next_float_allocate, 1) & ~1); Register next = start; do { if (!is_referenced(Register(next + 0)) && !is_referenced(Register(next + 1)) && !is_mapping_something(Register(next + 0)) && !is_mapping_something(Register(next + 1))) { reference(Register(next + 0)); reference(Register(next + 1)); _next_float_allocate = next_vfp_register(next, 2); return next; } next = next_vfp_register(next, 2); } while (next != start); } #if 0 // This change improve the caching of locals in registers. // But the more value that are cached in registers the more // values need to be written into memory at method call. // Try and find a half filled register pair { const Register start = Register((_next_float_spill) & ~1); Register next = next_vfp_register(start, 2); Register end = next_vfp_register(start, 8); do { if (is_referenced(Register(next+0)) || is_referenced(Register(next+1))) { continue; } const bool f0 = is_mapping_something(Register(next+0)); const bool f1 = is_mapping_something(Register(next+1)); if ((f0 + f1) == 1) { spill( f0 ? next : Register( next + 1) ); reference(Register(next + 0)); reference(Register(next + 1)); _next_float_spill = next_vfp_register(next, 2); return next; } next = next_vfp_register(next, 2); } while (next != end); } #endif // Nothing free spill registers { const Register start = Register( next_vfp_register( _next_float_spill, 1) & ~1); Register next = start; do { if (is_referenced(Register(next+0)) || is_referenced(Register(next+1))) { continue; } #if ENABLE_INLINE if (Compiler::current()->conforming_frame()->not_null() && (Compiler::current()->conforming_frame()->is_mapping_something(Register(next+0)) || Compiler::current()->conforming_frame()->is_mapping_something(Register(next+1)))) { continue; } #endif spill(Register(next+0)); spill(Register(next+1)); reference(Register(next+0)); reference(Register(next+1)); _next_float_spill = next_vfp_register(next, 2); return next; } while ((next = next_vfp_register(next, 2)) != start); } GUARANTEE(false, "Cannot allocate VFP registers for a double"); return Assembler::no_reg; }