/* * cacheManager implementation * * Copyright (C) Thomas Escher, Kai Lingemann * * Released under the GPL version 3. * */ #include "scanserver/cache/cacheManager.h" #include #include #include using namespace boost::interprocess; using std::runtime_error; using std::vector; using std::string; #include using std::cout; using std::cerr; using std::endl; #ifdef _MSC_VER #include #else #include // mlock for avoiding swaps #endif CacheManager::CacheManager(SegmentManager* sm, const char* shm_name, std::size_t cache_size) : m_segment_manager(sm), m_shm_name(shm_name) { // remove any existing shared memory that wasn't cleaned up shared_memory_object::remove(m_shm_name.c_str()); try { m_msm = new managed_shared_memory(create_only, m_shm_name.c_str(), cache_size); #ifndef WIN32 cout << " Locking cache memory... " << std::flush; int ret = mlock(m_msm->get_address(), m_msm->get_size()); if(ret == 0) cout << "success."; else if(ret == -EPERM) cout << "unsuccessful, no permissions."; else if(ret == -ENOMEM) cout << "unsuccessful, RLIMIT_MEMLOCK too small."; else cout << "unsuccessful, error=" << ret << "."; cout << endl; #endif } catch(interprocess_exception& e) { throw std::runtime_error(std::string("Could not create shared memory: ") + e.what()); } } CacheManager::~CacheManager() { // clean up objects for(vector::iterator it = m_objects.begin(); it != m_objects.end(); ++it) m_segment_manager->destroy_ptr(*it); // remove cache data shared memory delete m_msm; shared_memory_object::remove(m_shm_name.c_str()); } CacheObject* CacheManager::createCacheObject() { CacheObject* obj = m_segment_manager->construct(anonymous_instance)(); m_objects.push_back(obj); return obj; } bool CacheManager::loadCacheObject(CacheObject* obj) { if(obj->m_handler) { return obj->m_handler->load(); } else { throw runtime_error("No CacheHandler set for loading"); } } unsigned char* CacheManager::allocateCacheObject(CacheObject* obj, unsigned int size) { // remove old data if this isn't a cache miss call but a direct allocate call if(obj->m_handle != 0) { if(size == obj->m_size) { // INFO //cout << "CacheManager::allocateCacheObject reusing space" << endl; // space fits? leave it be return reinterpret_cast(m_msm->get_address_from_handle(obj->m_handle)); } else { // reset CO m_msm->destroy_ptr(m_msm->get_address_from_handle(obj->m_handle)); obj->m_size = 0; obj->m_handle = 0; } } // try to allocate it initially try { return load(obj, size); } catch(bad_alloc& e) { // flush behaviour below } // create a list of COs to remove from memory // TODO: create the list from the heuristic vector loaded = m_loaded; // try to exclusively lock COs to remove them from memory for(vector::iterator it = loaded.begin(); it != loaded.end(); ++it) { CacheObject* target = *it; scoped_lock lock(target->m_mutex_in_use, try_to_lock); if(lock) { unload(target); // try to allocate it try { return load(obj, size); } catch(bad_alloc& e) { // continue flushing } } } // when flushing didn't work, there's nothing to do anymore throw runtime_error("CacheManager could not allocate enough memory for CacheObject. All available memory is locked for used CacheObjects and you need to increase the cache memory size"); } void CacheManager::invalidateCacheObject(CacheObject* obj) { // remove its data if(obj->m_handle != 0) { // reset CO m_msm->destroy_ptr(m_msm->get_address_from_handle(obj->m_handle)); obj->m_size = 0; obj->m_handle = 0; } // invalidate the handler too obj->m_handler->invalidate(); } unsigned char* CacheManager::load(CacheObject* obj, unsigned int size) { // INFO //cout << " CM::load (" << size << ")" << endl; // allocate unsigned char* data = m_msm->construct(anonymous_instance)[size](); obj->m_size = size; obj->m_handle = m_msm->get_handle_from_address(data); // mark it as loaded m_loaded.push_back(obj); return data; } void CacheManager::unload(CacheObject* obj) { if(obj->m_handle == 0) return; // INFO //cout << " CM::unload" << endl; // save the CO by its handler unsigned char* data = reinterpret_cast(m_msm->get_address_from_handle(obj->m_handle)); obj->m_handler->save(data, obj->m_size); // TODO: exceptions? // reset CO m_msm->destroy_ptr(data); obj->m_size = 0; obj->m_handle = 0; // mark it as unloaded for(vector::iterator it = m_loaded.begin(); it != m_loaded.end(); ++it) { if(obj == *it) { m_loaded.erase(it); break; } } }