#include "stdafx.h" #include "v8datamodel/CacheableContentProvider.h" #include "V8DataModel/DataModel.h" namespace RBX { const char* const sCacheableContentProvider = "CacheableContentProvider"; CacheableContentProvider::CacheableContentProvider(CacheSizeEnforceMethod enforceMethod, unsigned long size) :pendingRequests(0) ,immediateMode(false) { setName(sCacheableContentProvider); lruCache.reset(new ConcurrentControlledLRUCache >(size, 300, enforceMethod)); } CacheableContentProvider::~CacheableContentProvider() { } void CacheableContentProvider::onHeartbeat(const Heartbeat& event) { lruCache->onHeartbeat(); } void CacheableContentProvider::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider) { if(contentJob){ contentJob->abort(); TaskScheduler::singleton().remove(contentJob); contentJob.reset(); } Super::onServiceProvider(oldProvider, newProvider); onServiceProviderHeartbeatInstance(oldProvider, newProvider); // hooks up heartbeat if (newProvider) { contentJob = shared_ptr(new ContentProviderJob(shared_from(DataModel::get(this)), getName().c_str(), boost::bind(&CacheableContentProvider::ProcessTaskHelper, weak_from(this), _1, _2), boost::bind(&CacheableContentProvider::ErrorTaskHelper, weak_from(this), _1))); TaskScheduler::singleton().add(contentJob); if(immediateMode){ contentJob ->setExecutionMode(ContentProviderJob::ImmediateMode); } } } void CacheableContentProvider::setCacheSize(int size) { lruCache->resize(size); } void CacheableContentProvider::setImmediateMode() { if(contentJob){ contentJob->setExecutionMode(ContentProviderJob::ImmediateMode); } immediateMode = true; } bool CacheableContentProvider::isAssetContent(ContentId id) { if (ContentProvider* contentProvider = ServiceProvider::create(this)) { id.convertToLegacyContent(contentProvider->getBaseUrl()); return id.isAsset(); } else return false; } bool CacheableContentProvider::clearContent() { return lruCache->evictAll(); } bool CacheableContentProvider::hasContent(const ContentId& id) { if(id.isNull()){ return false; } if(isAssetContent(id)){ if(ContentProvider* contentProvider = ServiceProvider::create(this)){ return contentProvider->hasContent(id); } return false; } if(getContentStatus(id.toString()) == AsyncHttpQueue::Succeeded){ return true; } return false; } bool CacheableContentProvider::isAssetFailed(const ContentId& id) { ContentId localId = id; if (ContentProvider* contentProvider = ServiceProvider::find(this)) { localId.convertAssetId(contentProvider->getBaseUrl(), DataModel::get(this)->getUniverseId()); localId.convertToLegacyContent(contentProvider->getBaseUrl()); } boost::mutex::scoped_lock lock(failedCacheMutex); return failedCache.find(localId.toString()) != failedCache.end(); } boost::shared_ptr CacheableContentProvider::fetchContent(const ContentId& id) { if(id.isNull()) return shared_ptr(); //if(isAssetContent(id)) // return shared_ptr(); { boost::mutex::scoped_lock lock(failedCacheMutex); if(failedCache.find(id.toString()) != failedCache.end()){ return shared_ptr(); } } shared_ptr item; if (lruCache->fetch(id.toString(), &item, true)){ if(item->requestResult == AsyncHttpQueue::Succeeded){ return item->data; } } return shared_ptr(); } boost::shared_ptr CacheableContentProvider::requestContent(const ContentId& id, float priority, bool markUsed, AsyncHttpQueue::RequestResult& result) { if(id.isNull()){ result = AsyncHttpQueue::Failed; return shared_ptr(); } { boost::mutex::scoped_lock lock(failedCacheMutex); if(failedCache.find(id.toString()) != failedCache.end()){ result = AsyncHttpQueue::Failed; return shared_ptr(); } } shared_ptr cachedItem; if (lruCache->fetch(id.toString(), &cachedItem, false)) { result = cachedItem->requestResult; if(result == AsyncHttpQueue::Succeeded){ if(markUsed){ lruCache->markEvictable(id.toString()); } } return cachedItem->data; } // mark img as waiting if(ContentProvider* contentProvider = ServiceProvider::create(this)){ cachedItem.reset(new CachedItem()); cachedItem->requestResult = AsyncHttpQueue::Waiting; lruCache->insert(id.toString(), cachedItem); { ++pendingRequests; //Make a request to content provider contentProvider->getContent(id, priority, boost::bind(&CacheableContentProvider::LoadContentCallbackHelper, weak_from(this), _1, _2, _3, id.toString()), AsyncHttpQueue::AsyncInline); } } result = AsyncHttpQueue::Waiting; return shared_ptr(); } boost::shared_ptr CacheableContentProvider::blockingRequestContent(const ContentId& id, bool markUsed) { AsyncHttpQueue::RequestResult reqResult = AsyncHttpQueue::Waiting; while(reqResult == AsyncHttpQueue::Waiting) { boost::shared_ptr asset = requestContent(id, ContentProvider::PRIORITY_MFC, true, reqResult); if (reqResult == AsyncHttpQueue::Succeeded) { return asset; } else if (reqResult == AsyncHttpQueue::Failed) { return shared_ptr(); } boost::this_thread::sleep(boost::posix_time::milliseconds(20)); } return shared_ptr(); } void CacheableContentProvider::LoadContentCallbackHelper(boost::weak_ptr cacheableContentProvider, AsyncHttpQueue::RequestResult result, std::istream* filestream, shared_ptr data, std::string id) { if(boost::shared_ptr ccp = cacheableContentProvider.lock()) ccp->LoadContentCallback(result, filestream, data, id); } void CacheableContentProvider::LoadContentCallback(AsyncHttpQueue::RequestResult result, std::istream* filestream, shared_ptr data, std::string id) { if(contentJob) contentJob->addTask(id, result, filestream, data); } TaskScheduler::StepResult CacheableContentProvider::ProcessTaskHelper(boost::weak_ptr weakCcp, const std::string& id, shared_ptr data) { shared_ptr ccp = weakCcp.lock(); if(!ccp) { return TaskScheduler::Done; } if(ccp->getContentStatus(id) != AsyncHttpQueue::Waiting) { //another callback took care of our loading, stop the requests --ccp->pendingRequests; return TaskScheduler::Stepped; } return ccp->ProcessTask(id, data); } void CacheableContentProvider::ErrorTaskHelper(boost::weak_ptr weakCcp, const std::string& id) { if(boost::shared_ptr ccp = weakCcp.lock()) ccp->ErrorTask(id); } void CacheableContentProvider::ErrorTask(const std::string& id) { markContentFailed(id); } void CacheableContentProvider::markContentFailed(const std::string& id) { { boost::mutex::scoped_lock lock(failedCacheMutex); failedCache.insert(id); } lruCache->remove(id); --pendingRequests; } void CacheableContentProvider::updateContent(const std::string& id, boost::shared_ptr cachedItem) { lruCache->insert(id, cachedItem); } AsyncHttpQueue::RequestResult CacheableContentProvider::getContentStatus(const std::string& id) { shared_ptr cachedItem; if (lruCache->fetch(id, &cachedItem, false)) { return cachedItem->requestResult; } return AsyncHttpQueue::Waiting; } }