From 9479b7f3300658156c467980c28d3436a728bc0c Mon Sep 17 00:00:00 2001 From: Simon Robertshaw Date: Thu, 14 Mar 2013 11:30:24 +0000 Subject: Move requestbroker into new folder, make request process function a method of Request diff --git a/SConscript b/SConscript index e12300f..69aa6fe 100644 --- a/SConscript +++ b/SConscript @@ -260,6 +260,7 @@ if(GetOption('win')): sources+=Glob("src/*/*.cpp") sources+=Glob("src/simulation/elements/*.cpp") sources+=Glob("src/simulation/tools/*.cpp") +sources+=Glob("src/client/requestbroker/*.cpp") #for source in sources: # print str(source) diff --git a/src/client/Client.cpp b/src/client/Client.cpp index 6267d91..ecb6466 100644 --- a/src/client/Client.cpp +++ b/src/client/Client.cpp @@ -42,7 +42,7 @@ #include "search/Thumbnail.h" #include "preview/Comment.h" #include "ClientListener.h" -#include "RequestBroker.h" +#include "requestbroker/RequestBroker.h" #include "cajun/reader.h" #include "cajun/writer.h" diff --git a/src/client/RequestBroker.cpp b/src/client/RequestBroker.cpp deleted file mode 100644 index c3b7fdf..0000000 --- a/src/client/RequestBroker.cpp +++ /dev/null @@ -1,422 +0,0 @@ -#include -#include -#include -#include -#include "RequestBroker.h" -#include "RequestListener.h" -#include "Client.h" -#include "HTTP.h" -#include "GameSave.h" -#include "search/Thumbnail.h" -#include "simulation/SaveRenderer.h" - -//Asynchronous Thumbnail render & request processing - -RequestBroker::RequestBroker() -{ - thumbnailQueueRunning = false; - - //listenersMutex = PTHREAD_MUTEX_INITIALIZER; - pthread_mutex_init (&listenersMutex, NULL); - - - pthread_mutex_init (&runningMutex, NULL); - - - pthread_mutex_init (&requestQueueMutex, NULL); - - - pthread_mutex_init (&completeQueueMutex, NULL); -} - -RequestBroker::~RequestBroker() -{ - for(std::deque >::iterator iter = imageCache.begin(), end = imageCache.end(); iter != end; ++iter) - { - delete (*iter).second; - } -} - -void RequestBroker::assureRunning() -{ - pthread_mutex_lock(&runningMutex); - bool running = thumbnailQueueRunning; - thumbnailQueueRunning = true; - pthread_mutex_unlock(&runningMutex); - - if(!running) - { -#ifdef DEBUG - std::cout << typeid(*this).name() << " Starting background thread for new " << __FUNCTION__ << " request" << std::endl; -#endif - pthread_create(&thumbnailQueueThread, 0, &RequestBroker::thumbnailQueueProcessHelper, this); - } -} - -void RequestBroker::Shutdown() -{ - pthread_mutex_lock(&runningMutex); - if(thumbnailQueueRunning) - { - thumbnailQueueRunning = false; - pthread_mutex_unlock(&runningMutex); - pthread_join(thumbnailQueueThread, NULL); - } - else - pthread_mutex_unlock(&runningMutex); - - std::vector::iterator req = activeRequests.begin(); - while(req != activeRequests.end()) - { - (*req)->Cleanup(); - delete (*req); - req++; - } -} - -void RequestBroker::RenderThumbnail(GameSave * gameSave, int width, int height, RequestListener * tListener) -{ - RenderThumbnail(gameSave, true, true, width, height, tListener); -} - -void RequestBroker::RenderThumbnail(GameSave * gameSave, bool decorations, bool fire, int width, int height, RequestListener * tListener) -{ - ListenerHandle handle = AttachRequestListener(tListener); - - ThumbRenderRequest * r = new ThumbRenderRequest(new GameSave(*gameSave), decorations, fire, width, height, handle); - - pthread_mutex_lock(&requestQueueMutex); - requestQueue.push_back(r); - pthread_mutex_unlock(&requestQueueMutex); - - assureRunning(); -} - -void RequestBroker::RetrieveThumbnail(int saveID, int saveDate, int width, int height, RequestListener * tListener) -{ - std::stringstream urlStream; - urlStream << "http://" << STATICSERVER << "/" << saveID; - if(saveDate) - { - urlStream << "_" << saveDate; - } - urlStream << "_small.pti"; - - RetrieveImage(urlStream.str(), width, height, tListener); -} - -void RequestBroker::RetrieveAvatar(std::string username, int width, int height, RequestListener * tListener) -{ - std::stringstream urlStream; - urlStream << "http://" << STATICSERVER << "/avatars/" << username << ".pti"; - - RetrieveImage(urlStream.str(), width, height, tListener); -} - -void RequestBroker::RetrieveImage(std::string imageUrl, int width, int height, RequestListener * tListener) -{ - ListenerHandle handle = AttachRequestListener(tListener); - - ImageRequest * r = new ImageRequest(imageUrl, width, height, handle); - - pthread_mutex_lock(&requestQueueMutex); - requestQueue.push_back(r); - pthread_mutex_unlock(&requestQueueMutex); - - assureRunning(); -} - -void * RequestBroker::thumbnailQueueProcessHelper(void * ref) -{ - ((RequestBroker*)ref)->thumbnailQueueProcessTH(); - return NULL; -} - -void RequestBroker::FlushThumbQueue() -{ - pthread_mutex_lock(&completeQueueMutex); - while(completeQueue.size()) - { - if(CheckRequestListener(completeQueue.front()->Listener)) - { - completeQueue.front()->Listener.second->OnResponseReady(completeQueue.front()->ResultObject); - } - else - { -#ifdef DEBUG - std::cout << typeid(*this).name() << " Listener lost, discarding request" << std::endl; -#endif - completeQueue.front()->Cleanup(); - } - delete completeQueue.front(); - completeQueue.pop(); - } - pthread_mutex_unlock(&completeQueueMutex); -} - -void RequestBroker::thumbnailQueueProcessTH() -{ - time_t lastAction = time(NULL); - pthread_mutex_lock(&runningMutex); - thumbnailQueueRunning = true; - pthread_mutex_unlock(&runningMutex); - while(true) - { - //Shutdown after 2 seconds of idle - if(time(NULL) - lastAction > 2) - { -#ifdef DEBUG - std::cout << typeid(*this).name() << " Idle shutdown" << std::endl; -#endif - break; - } - - - pthread_mutex_lock(&runningMutex); - bool running = thumbnailQueueRunning; - pthread_mutex_unlock(&runningMutex); - if(!running) - { -#ifdef DEBUG - std::cout << typeid(*this).name() << " Requested shutdown" << std::endl; -#endif - break; - } - - if(activeRequests.size()) - { - std::vector::iterator req = activeRequests.begin(); - while(req != activeRequests.end()) - { - ProcessResponse resultStatus = OK; - Request * r = *req; - switch(r->Type) - { - case Request::ThumbnailRender: - resultStatus = processThumbnailRender(*(ThumbRenderRequest*)r); - break; - case Request::Image: - resultStatus = processImage(*(ImageRequest*)r); - break; - } - if(resultStatus == Duplicate || resultStatus == Failed || resultStatus == Finished) - { - req = activeRequests.erase(req); - } - else - { - req++; - } - } - lastAction = time(NULL); - } - - //Move any items from the request queue to the processing queue - pthread_mutex_lock(&requestQueueMutex); - std::vector::iterator newReq = requestQueue.begin(); - while(newReq != requestQueue.end()) - { - if(activeRequests.size() > 5) - { - break; - } - else - { - activeRequests.push_back(*newReq); - newReq = requestQueue.erase(newReq); - } - } - pthread_mutex_unlock(&requestQueueMutex); - } - pthread_mutex_lock(&runningMutex); - thumbnailQueueRunning = false; - pthread_mutex_unlock(&runningMutex); -} - -RequestBroker::ProcessResponse RequestBroker::processThumbnailRender(ThumbRenderRequest & request) -{ -#ifdef DEBUG - std::cout << typeid(*this).name() << " Processing render request" << std::endl; -#endif - Thumbnail * thumbnail = SaveRenderer::Ref().Render(request.Save, request.Decorations, request.Fire); - delete request.Save; - request.Save = NULL; - - if(thumbnail) - { - thumbnail->Resize(request.Width, request.Height); - request.ResultObject = (void*)thumbnail; - requestComplete(&request); - return Finished; - } - else - { - return Failed; - } - return Failed; -} - -RequestBroker::ProcessResponse RequestBroker::processImage(ImageRequest & request) -{ - VideoBuffer * image = NULL; - - //Have a look at the thumbnail cache - for(std::deque >::iterator iter = imageCache.begin(), end = imageCache.end(); iter != end; ++iter) - { - if((*iter).first == request.URL) - { - image = (*iter).second; -#ifdef DEBUG - std::cout << typeid(*this).name() << " " << request.URL << " found in cache" << std::endl; -#endif - } - } - - if(!image) - { - if(request.HTTPContext) - { - if(http_async_req_status(request.HTTPContext)) - { - pixel * imageData; - char * data; - int status, data_size, imgw, imgh; - data = http_async_req_stop(request.HTTPContext, &status, &data_size); - - if (status == 200 && data) - { - imageData = Graphics::ptif_unpack(data, data_size, &imgw, &imgh); - free(data); - - if(imageData) - { - //Success! - image = new VideoBuffer(imageData, imgw, imgh); - free(imageData); - } - else - { - //Error thumbnail - image = new VideoBuffer(32, 32); - image->SetCharacter(14, 14, 'x', 255, 255, 255, 255); - } - - if(imageCache.size() >= THUMB_CACHE_SIZE) - { - //Remove unnecessary from thumbnail cache - delete imageCache.front().second; - imageCache.pop_front(); - } - imageCache.push_back(std::pair(request.URL, image)); - } - else - { - #ifdef DEBUG - std::cout << typeid(*this).name() << " Request for " << request.URL << " failed with status " << status << std::endl; - #endif - if(data) - free(data); - - return Failed; - } - } - } - else - { - //Check for ongoing requests - for(std::vector::iterator iter = activeRequests.begin(), end = activeRequests.end(); iter != end; ++iter) - { - if((*iter)->Type != Request::Image) - continue; - ImageRequest * otherReq = (ImageRequest*)(*iter); - if(otherReq->URL == request.URL && otherReq != &request) - { - #ifdef DEBUG - std::cout << typeid(*this).name() << " Request for " << request.URL << " found, appending." << std::endl; - #endif - //Add the current listener to the item already being requested - (*iter)->Children.push_back(&request); - return Duplicate; - } - } - - //If it's not already being requested, request it - #ifdef DEBUG - std::cout << typeid(*this).name() << " Creating new request for " << request.URL << std::endl; - #endif - request.HTTPContext = http_async_req_start(NULL, (char *)request.URL.c_str(), NULL, 0, 0); - request.RequestTime = time(NULL); - } - } - - if(image) - { - - //Create a copy, to seperate from the cache - VideoBuffer * myVB = new VideoBuffer(*image); - myVB->Resize(request.Width, request.Height, true); - request.ResultObject = (void*)myVB; - requestComplete(&request); - for(std::vector::iterator childIter = request.Children.begin(), childEnd = request.Children.end(); childIter != childEnd; ++childIter) - { - if((*childIter)->Type == Request::Image) - { - ImageRequest * childReq = (ImageRequest*)*childIter; - VideoBuffer * tempImage = new VideoBuffer(*image); - tempImage->Resize(childReq->Width, childReq->Height, true); - childReq->ResultObject = (void*)tempImage; - requestComplete(*childIter); - } - } - return Finished; - } - - return OK; -} - -void RequestBroker::requestComplete(Request * completedRequest) -{ - pthread_mutex_lock(&completeQueueMutex); - completeQueue.push(completedRequest); - pthread_mutex_unlock(&completeQueueMutex); -} - - -void RequestBroker::RetrieveThumbnail(int saveID, int width, int height, RequestListener * tListener) -{ - RetrieveThumbnail(saveID, 0, width, height, tListener); -} - -bool RequestBroker::CheckRequestListener(ListenerHandle handle) -{ - pthread_mutex_lock(&listenersMutex); - int count = std::count(validListeners.begin(), validListeners.end(), handle); - pthread_mutex_unlock(&listenersMutex); - - return count; -} - -ListenerHandle RequestBroker::AttachRequestListener(RequestListener * tListener) -{ - ListenerHandle handle = ListenerHandle(tListener->ListenerRand, tListener); - pthread_mutex_lock(&listenersMutex); - validListeners.push_back(handle); - pthread_mutex_unlock(&listenersMutex); - return handle; -} - -void RequestBroker::DetachRequestListener(RequestListener * tListener) -{ - pthread_mutex_lock(&listenersMutex); - - std::vector::iterator iter = validListeners.begin(); - while (iter != validListeners.end()) - { - if(*iter == ListenerHandle(tListener->ListenerRand, tListener)) - iter = validListeners.erase(iter); - else - ++iter; - } - - pthread_mutex_unlock(&listenersMutex); -} \ No newline at end of file diff --git a/src/client/RequestBroker.h b/src/client/RequestBroker.h deleted file mode 100644 index 9244e91..0000000 --- a/src/client/RequestBroker.h +++ /dev/null @@ -1,155 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#undef GetUserName //God dammit microsoft! - -#include "Singleton.h" - -class GameSave; -class VideoBuffer; -class RequestListener; -typedef std::pair ListenerHandle; -class RequestBroker: public Singleton -{ -private: - - enum ProcessResponse { Finished, OK, Canceled, Failed, Duplicate }; - - class Request - { - public: - enum RequestType { ThumbnailRender, Image }; - RequestType Type; - void * ResultObject; - ListenerHandle Listener; - std::vector Children; - Request(RequestType type, ListenerHandle listener) - { - Type = type; - Listener = listener; - ResultObject = NULL; - } - virtual ~Request() - { - std::vector::iterator iter = Children.begin(); - while(iter != Children.end()) - { - delete (*iter); - iter++; - } - } - virtual void Cleanup() - { - std::vector::iterator iter = Children.begin(); - while(iter != Children.end()) - { - (*iter)->Cleanup(); - iter++; - } - } - }; - - class ThumbRenderRequest: public Request - { - public: - int Width, Height; - bool Decorations; - bool Fire; - GameSave * Save; - ThumbRenderRequest(GameSave * save, bool decorations, bool fire, int width, int height, ListenerHandle listener): - Request(ThumbnailRender, listener) - { - Save = save; - Width = width; - Height = height; - Decorations = decorations; - Fire = fire; - } - virtual ~ThumbRenderRequest() - { - if(Save) - delete Save; - } - virtual void Cleanup() - { - Request::Cleanup(); - if(ResultObject) - { - delete ((VideoBuffer*)ResultObject); - ResultObject = NULL; - } - } - }; - - class ImageRequest: public Request - { - public: - int Width, Height; - std::string URL; - int RequestTime; - void * HTTPContext; - ImageRequest(std::string url, int width, int height, ListenerHandle listener): - Request(Image, listener) - { - URL = url; - HTTPContext = NULL; - Width = width; - Height = height; - } - virtual ~ImageRequest() {} - virtual void Cleanup() - { - Request::Cleanup(); - if(ResultObject) - { - delete ((VideoBuffer*)ResultObject); - ResultObject = NULL; - } - } - }; - - pthread_mutex_t listenersMutex; - pthread_mutex_t runningMutex; - pthread_mutex_t requestQueueMutex; - pthread_mutex_t completeQueueMutex; - - pthread_t thumbnailQueueThread; - bool thumbnailQueueRunning; - - std::vector validListeners; - - std::deque > imageCache; - - std::queue completeQueue; - std::vector requestQueue; - std::vector activeRequests; - - static void * thumbnailQueueProcessHelper(void * ref); - void thumbnailQueueProcessTH(); - void assureRunning(); - - ProcessResponse processThumbnailRender(ThumbRenderRequest & request); - ProcessResponse processImage(ImageRequest & request); - - void requestComplete(Request * completedRequest); - -public: - RequestBroker(); - virtual ~RequestBroker(); - void Shutdown(); - - void FlushThumbQueue(); - void RetrieveImage(std::string imageUrl, int width, int height, RequestListener * tListener); - void RenderThumbnail(GameSave * gameSave, bool decorations, bool fire, int width, int height, RequestListener * tListener); - void RenderThumbnail(GameSave * gameSave, int width, int height, RequestListener * tListener); - void RetrieveThumbnail(int saveID, int saveDate, int width, int height, RequestListener * tListener); - void RetrieveThumbnail(int saveID, int width, int height, RequestListener * tListener); - void RetrieveAvatar(std::string username, int width, int height, RequestListener * tListener); - - bool CheckRequestListener(ListenerHandle handle); - ListenerHandle AttachRequestListener(RequestListener * tListener); - void DetachRequestListener(RequestListener * tListener); -}; \ No newline at end of file diff --git a/src/client/RequestListener.h b/src/client/RequestListener.h deleted file mode 100644 index 1f53b2a..0000000 --- a/src/client/RequestListener.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -class RequestListener -{ -public: - int ListenerRand; - RequestListener() { ListenerRand = rand(); } - virtual ~RequestListener() {} - - virtual void OnResponseReady(void * response) {} -}; diff --git a/src/client/requestbroker/ImageRequest.cpp b/src/client/requestbroker/ImageRequest.cpp new file mode 100644 index 0000000..6e4f66c --- /dev/null +++ b/src/client/requestbroker/ImageRequest.cpp @@ -0,0 +1,147 @@ +#include +#include +#include "ImageRequest.h" +#include "graphics/Graphics.h" +#include "client/HTTP.h" + +ImageRequest::ImageRequest(std::string url, int width, int height, ListenerHandle listener): + Request(Image, listener) +{ + URL = url; + HTTPContext = NULL; + Width = width; + Height = height; +} + +ImageRequest::~ImageRequest() +{ + +} + +RequestBroker::ProcessResponse ImageRequest::Process(RequestBroker & rb) +{ + VideoBuffer * image = NULL; + + //Have a look at the thumbnail cache + for(std::deque >::iterator iter = rb.imageCache.begin(), end = rb.imageCache.end(); iter != end; ++iter) + { + if((*iter).first == URL) + { + image = (*iter).second; +#ifdef DEBUG + std::cout << typeid(*this).name() << " " << URL << " found in cache" << std::endl; +#endif + } + } + + if(!image) + { + if(HTTPContext) + { + if(http_async_req_status(HTTPContext)) + { + pixel * imageData; + char * data; + int status, data_size, imgw, imgh; + data = http_async_req_stop(HTTPContext, &status, &data_size); + + if (status == 200 && data) + { + imageData = Graphics::ptif_unpack(data, data_size, &imgw, &imgh); + free(data); + + if(imageData) + { + //Success! + image = new VideoBuffer(imageData, imgw, imgh); + free(imageData); + } + else + { + //Error thumbnail + image = new VideoBuffer(32, 32); + image->SetCharacter(14, 14, 'x', 255, 255, 255, 255); + } + + if(rb.imageCache.size() >= THUMB_CACHE_SIZE) + { + //Remove unnecessary from thumbnail cache + delete rb.imageCache.front().second; + rb.imageCache.pop_front(); + } + rb.imageCache.push_back(std::pair(URL, image)); + } + else + { + #ifdef DEBUG + std::cout << typeid(*this).name() << " Request for " << URL << " failed with status " << status << std::endl; + #endif + if(data) + free(data); + + return RequestBroker::Failed; + } + } + } + else + { + //Check for ongoing requests + for(std::vector::iterator iter = rb.activeRequests.begin(), end = rb.activeRequests.end(); iter != end; ++iter) + { + if((*iter)->Type != Request::Image) + continue; + ImageRequest * otherReq = (ImageRequest*)(*iter); + if(otherReq->URL == URL && otherReq != this) + { + #ifdef DEBUG + std::cout << typeid(*this).name() << " Request for " << URL << " found, appending." << std::endl; + #endif + //Add the current listener to the item already being requested + (*iter)->Children.push_back(this); + return RequestBroker::Duplicate; + } + } + + //If it's not already being requested, request it + #ifdef DEBUG + std::cout << typeid(*this).name() << " Creating new request for " << URL << std::endl; + #endif + HTTPContext = http_async_req_start(NULL, (char *)URL.c_str(), NULL, 0, 0); + RequestTime = time(NULL); + } + } + + if(image) + { + + //Create a copy, to seperate from the cache + VideoBuffer * myVB = new VideoBuffer(*image); + myVB->Resize(Width, Height, true); + ResultObject = (void*)myVB; + rb.requestComplete(this); + for(std::vector::iterator childIter = Children.begin(), childEnd = Children.end(); childIter != childEnd; ++childIter) + { + if((*childIter)->Type == Request::Image) + { + ImageRequest * childReq = (ImageRequest*)*childIter; + VideoBuffer * tempImage = new VideoBuffer(*image); + tempImage->Resize(childReq->Width, childReq->Height, true); + childReq->ResultObject = (void*)tempImage; + rb.requestComplete(*childIter); + } + } + return RequestBroker::Finished; + } + + return RequestBroker::OK; +} + +void ImageRequest::Cleanup() +{ + Request::Cleanup(); + if(ResultObject) + { + delete ((VideoBuffer*)ResultObject); + ResultObject = NULL; + } +} \ No newline at end of file diff --git a/src/client/requestbroker/ImageRequest.h b/src/client/requestbroker/ImageRequest.h new file mode 100644 index 0000000..9a2cf34 --- /dev/null +++ b/src/client/requestbroker/ImageRequest.h @@ -0,0 +1,14 @@ +#include "RequestBroker.h" + +class ImageRequest: public RequestBroker::Request +{ +public: + int Width, Height; + std::string URL; + int RequestTime; + void * HTTPContext; + ImageRequest(std::string url, int width, int height, ListenerHandle listener); + virtual RequestBroker::ProcessResponse Process(RequestBroker & rb); + virtual ~ImageRequest(); + virtual void Cleanup(); +}; \ No newline at end of file diff --git a/src/client/requestbroker/RequestBroker.cpp b/src/client/requestbroker/RequestBroker.cpp new file mode 100644 index 0000000..62e5fd7 --- /dev/null +++ b/src/client/requestbroker/RequestBroker.cpp @@ -0,0 +1,300 @@ +#include +#include +#include +#include +#include +#include "RequestBroker.h" +#include "RequestListener.h" +#include "ThumbRenderRequest.h" +#include "ImageRequest.h" +#include "client/Client.h" +#include "client/GameSave.h" + +//Asynchronous Thumbnail render & request processing + +RequestBroker::RequestBroker() +{ + thumbnailQueueRunning = false; + + //listenersMutex = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_init (&listenersMutex, NULL); + + + pthread_mutex_init (&runningMutex, NULL); + + + pthread_mutex_init (&requestQueueMutex, NULL); + + + pthread_mutex_init (&completeQueueMutex, NULL); +} + +RequestBroker::~RequestBroker() +{ + for(std::deque >::iterator iter = imageCache.begin(), end = imageCache.end(); iter != end; ++iter) + { + delete (*iter).second; + } +} + +void RequestBroker::assureRunning() +{ + pthread_mutex_lock(&runningMutex); + bool running = thumbnailQueueRunning; + thumbnailQueueRunning = true; + pthread_mutex_unlock(&runningMutex); + + if(!running) + { +#ifdef DEBUG + std::cout << typeid(*this).name() << " Starting background thread for new " << __FUNCTION__ << " request" << std::endl; +#endif + pthread_create(&thumbnailQueueThread, 0, &RequestBroker::thumbnailQueueProcessHelper, this); + } +} + +void RequestBroker::Shutdown() +{ + pthread_mutex_lock(&runningMutex); + if(thumbnailQueueRunning) + { + thumbnailQueueRunning = false; + pthread_mutex_unlock(&runningMutex); + pthread_join(thumbnailQueueThread, NULL); + } + else + pthread_mutex_unlock(&runningMutex); + + std::vector::iterator req = activeRequests.begin(); + while(req != activeRequests.end()) + { + (*req)->Cleanup(); + delete (*req); + req++; + } +} + +void RequestBroker::RenderThumbnail(GameSave * gameSave, int width, int height, RequestListener * tListener) +{ + RenderThumbnail(gameSave, true, true, width, height, tListener); +} + +void RequestBroker::RenderThumbnail(GameSave * gameSave, bool decorations, bool fire, int width, int height, RequestListener * tListener) +{ + ListenerHandle handle = AttachRequestListener(tListener); + + ThumbRenderRequest * r = new ThumbRenderRequest(new GameSave(*gameSave), decorations, fire, width, height, handle); + + pthread_mutex_lock(&requestQueueMutex); + requestQueue.push_back(r); + pthread_mutex_unlock(&requestQueueMutex); + + assureRunning(); +} + +void RequestBroker::RetrieveThumbnail(int saveID, int saveDate, int width, int height, RequestListener * tListener) +{ + std::stringstream urlStream; + urlStream << "http://" << STATICSERVER << "/" << saveID; + if(saveDate) + { + urlStream << "_" << saveDate; + } + urlStream << "_small.pti"; + + RetrieveImage(urlStream.str(), width, height, tListener); +} + +void RequestBroker::RetrieveAvatar(std::string username, int width, int height, RequestListener * tListener) +{ + std::stringstream urlStream; + urlStream << "http://" << STATICSERVER << "/avatars/" << username << ".pti"; + + RetrieveImage(urlStream.str(), width, height, tListener); +} + +void RequestBroker::RetrieveImage(std::string imageUrl, int width, int height, RequestListener * tListener) +{ + ListenerHandle handle = AttachRequestListener(tListener); + + ImageRequest * r = new ImageRequest(imageUrl, width, height, handle); + + pthread_mutex_lock(&requestQueueMutex); + requestQueue.push_back(r); + pthread_mutex_unlock(&requestQueueMutex); + + assureRunning(); +} + +void * RequestBroker::thumbnailQueueProcessHelper(void * ref) +{ + ((RequestBroker*)ref)->thumbnailQueueProcessTH(); + return NULL; +} + +void RequestBroker::FlushThumbQueue() +{ + pthread_mutex_lock(&completeQueueMutex); + while(completeQueue.size()) + { + if(CheckRequestListener(completeQueue.front()->Listener)) + { + std::cout << typeid(*this).name() << " Calling listener: " << completeQueue.front()->Listener.second << std::endl; + std::cout.flush(); + completeQueue.front()->Listener.second->OnResponseReady(completeQueue.front()->ResultObject); + } + else + { +#ifdef DEBUG + std::cout << typeid(*this).name() << " Listener lost, discarding request" << std::endl; +#endif + completeQueue.front()->Cleanup(); + } + delete completeQueue.front(); + completeQueue.pop(); + } + pthread_mutex_unlock(&completeQueueMutex); +} + +void RequestBroker::thumbnailQueueProcessTH() +{ + time_t lastAction = time(NULL); + pthread_mutex_lock(&runningMutex); + thumbnailQueueRunning = true; + pthread_mutex_unlock(&runningMutex); + while(true) + { + //Shutdown after 2 seconds of idle + if(time(NULL) - lastAction > 2) + { +#ifdef DEBUG + std::cout << typeid(*this).name() << " Idle shutdown" << std::endl; +#endif + break; + } + + + pthread_mutex_lock(&runningMutex); + bool running = thumbnailQueueRunning; + pthread_mutex_unlock(&runningMutex); + if(!running) + { +#ifdef DEBUG + std::cout << typeid(*this).name() << " Requested shutdown" << std::endl; +#endif + break; + } + + if(activeRequests.size()) + { + std::vector::iterator req = activeRequests.begin(); + while(req != activeRequests.end()) + { + ProcessResponse resultStatus = OK; + Request * r = *req; + resultStatus = r->Process(*this); + if(resultStatus == Duplicate || resultStatus == Failed || resultStatus == Finished) + { + req = activeRequests.erase(req); + } + else + { + req++; + } + } + lastAction = time(NULL); + } + + //Move any items from the request queue to the processing queue + pthread_mutex_lock(&requestQueueMutex); + std::vector::iterator newReq = requestQueue.begin(); + while(newReq != requestQueue.end()) + { + if(activeRequests.size() > 5) + { + break; + } + else + { + activeRequests.push_back(*newReq); + newReq = requestQueue.erase(newReq); + } + } + pthread_mutex_unlock(&requestQueueMutex); + } + pthread_mutex_lock(&runningMutex); + thumbnailQueueRunning = false; + pthread_mutex_unlock(&runningMutex); +} + +void RequestBroker::requestComplete(Request * completedRequest) +{ + pthread_mutex_lock(&completeQueueMutex); + completeQueue.push(completedRequest); + pthread_mutex_unlock(&completeQueueMutex); +} + + +void RequestBroker::RetrieveThumbnail(int saveID, int width, int height, RequestListener * tListener) +{ + RetrieveThumbnail(saveID, 0, width, height, tListener); +} + +bool RequestBroker::CheckRequestListener(ListenerHandle handle) +{ + pthread_mutex_lock(&listenersMutex); + int count = std::count(validListeners.begin(), validListeners.end(), handle); + pthread_mutex_unlock(&listenersMutex); + + return count; +} + +ListenerHandle RequestBroker::AttachRequestListener(RequestListener * tListener) +{ + ListenerHandle handle = ListenerHandle(tListener->ListenerRand, tListener); + pthread_mutex_lock(&listenersMutex); + validListeners.push_back(handle); + pthread_mutex_unlock(&listenersMutex); + return handle; +} + +void RequestBroker::DetachRequestListener(RequestListener * tListener) +{ + pthread_mutex_lock(&listenersMutex); + + std::vector::iterator iter = validListeners.begin(); + while (iter != validListeners.end()) + { + if(*iter == ListenerHandle(tListener->ListenerRand, tListener)) + iter = validListeners.erase(iter); + else + ++iter; + } + + pthread_mutex_unlock(&listenersMutex); +} + +RequestBroker::Request::Request(RequestType type, ListenerHandle listener) +{ + Type = type; + Listener = listener; + ResultObject = NULL; +} +RequestBroker::Request::~Request() +{ + std::vector::iterator iter = Children.begin(); + while(iter != Children.end()) + { + delete (*iter); + iter++; + } +} +void RequestBroker::Request::Cleanup() +{ + std::vector::iterator iter = Children.begin(); + while(iter != Children.end()) + { + (*iter)->Cleanup(); + iter++; + } +} \ No newline at end of file diff --git a/src/client/requestbroker/RequestBroker.h b/src/client/requestbroker/RequestBroker.h new file mode 100644 index 0000000..003ac37 --- /dev/null +++ b/src/client/requestbroker/RequestBroker.h @@ -0,0 +1,76 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#undef GetUserName //God dammit microsoft! + +#include "Singleton.h" + +class GameSave; +class VideoBuffer; +class RequestListener; +typedef std::pair ListenerHandle; +class RequestBroker: public Singleton +{ + friend class ImageRequest; + friend class ThumbRenderRequest; +public: + class Request; +private: + + pthread_mutex_t listenersMutex; + pthread_mutex_t runningMutex; + pthread_mutex_t requestQueueMutex; + pthread_mutex_t completeQueueMutex; + + pthread_t thumbnailQueueThread; + bool thumbnailQueueRunning; + + std::vector validListeners; + + std::deque > imageCache; + + std::queue completeQueue; + std::vector requestQueue; + std::vector activeRequests; + + static void * thumbnailQueueProcessHelper(void * ref); + void thumbnailQueueProcessTH(); + void assureRunning(); + + void requestComplete(Request * completedRequest); + +public: + RequestBroker(); + virtual ~RequestBroker(); + void Shutdown(); + + void FlushThumbQueue(); + void RetrieveImage(std::string imageUrl, int width, int height, RequestListener * tListener); + void RenderThumbnail(GameSave * gameSave, bool decorations, bool fire, int width, int height, RequestListener * tListener); + void RenderThumbnail(GameSave * gameSave, int width, int height, RequestListener * tListener); + void RetrieveThumbnail(int saveID, int saveDate, int width, int height, RequestListener * tListener); + void RetrieveThumbnail(int saveID, int width, int height, RequestListener * tListener); + void RetrieveAvatar(std::string username, int width, int height, RequestListener * tListener); + + bool CheckRequestListener(ListenerHandle handle); + ListenerHandle AttachRequestListener(RequestListener * tListener); + void DetachRequestListener(RequestListener * tListener); + enum ProcessResponse { Finished, OK, Canceled, Failed, Duplicate }; + class Request + { + public: + enum RequestType { ThumbnailRender, Image }; + RequestType Type; + void * ResultObject; + ListenerHandle Listener; + std::vector Children; + Request(RequestType type, ListenerHandle listener); + virtual ProcessResponse Process(RequestBroker & rb) { return Failed; } + virtual ~Request(); + virtual void Cleanup(); + }; +}; \ No newline at end of file diff --git a/src/client/requestbroker/RequestListener.h b/src/client/requestbroker/RequestListener.h new file mode 100644 index 0000000..1f53b2a --- /dev/null +++ b/src/client/requestbroker/RequestListener.h @@ -0,0 +1,11 @@ +#pragma once + +class RequestListener +{ +public: + int ListenerRand; + RequestListener() { ListenerRand = rand(); } + virtual ~RequestListener() {} + + virtual void OnResponseReady(void * response) {} +}; diff --git a/src/client/requestbroker/ThumbRenderRequest.cpp b/src/client/requestbroker/ThumbRenderRequest.cpp new file mode 100644 index 0000000..e239883 --- /dev/null +++ b/src/client/requestbroker/ThumbRenderRequest.cpp @@ -0,0 +1,56 @@ +#include +#include "ThumbRenderRequest.h" +#include "client/GameSave.h" +#include "graphics/Graphics.h" +#include "search/Thumbnail.h" +#include "simulation/SaveRenderer.h" + +ThumbRenderRequest::ThumbRenderRequest(GameSave * save, bool decorations, bool fire, int width, int height, ListenerHandle listener): + RequestBroker::Request(ThumbnailRender, listener) +{ + Save = save; + Width = width; + Height = height; + Decorations = decorations; + Fire = fire; +} + +RequestBroker::ProcessResponse ThumbRenderRequest::Process(RequestBroker & rb) +{ +#ifdef DEBUG + std::cout << typeid(*this).name() << " Processing render request" << std::endl; +#endif + Thumbnail * thumbnail = SaveRenderer::Ref().Render(Save, Decorations, Fire); + + delete Save; + Save = NULL; + + if(thumbnail) + { + thumbnail->Resize(Width, Height); + ResultObject = (void*)thumbnail; + rb.requestComplete((Request*)this); + return RequestBroker::Finished; + } + else + { + return RequestBroker::Failed; + } + return RequestBroker::Failed; +} + +ThumbRenderRequest::~ThumbRenderRequest() +{ + if(Save) + delete Save; +} + +void ThumbRenderRequest::Cleanup() +{ + Request::Cleanup(); + if(ResultObject) + { + delete ((VideoBuffer*)ResultObject); + ResultObject = NULL; + } +} \ No newline at end of file diff --git a/src/client/requestbroker/ThumbRenderRequest.h b/src/client/requestbroker/ThumbRenderRequest.h new file mode 100644 index 0000000..97b3982 --- /dev/null +++ b/src/client/requestbroker/ThumbRenderRequest.h @@ -0,0 +1,16 @@ +#include "RequestBroker.h" + +class GameSave; +class ThumbRenderRequest: public RequestBroker::Request +{ +public: + int Width, Height; + bool Decorations; + bool Fire; + GameSave * Save; + ThumbRenderRequest(GameSave * save, bool decorations, bool fire, int width, int height, ListenerHandle listener); + virtual RequestBroker::ProcessResponse Process(RequestBroker & rb); + virtual ~ThumbRenderRequest(); + virtual void Cleanup(); +}; + diff --git a/src/interface/AvatarButton.cpp b/src/interface/AvatarButton.cpp index 0a55ffa..e385c77 100644 --- a/src/interface/AvatarButton.cpp +++ b/src/interface/AvatarButton.cpp @@ -5,7 +5,7 @@ #include "Format.h" #include "Engine.h" #include "client/Client.h" -#include "client/RequestBroker.h" +#include "client/requestbroker/RequestBroker.h" #include "graphics/Graphics.h" #include "ContextMenu.h" #include "Keys.h" diff --git a/src/interface/AvatarButton.h b/src/interface/AvatarButton.h index d946ddf..f7a7a6c 100644 --- a/src/interface/AvatarButton.h +++ b/src/interface/AvatarButton.h @@ -6,7 +6,7 @@ #include "Component.h" #include "graphics/Graphics.h" #include "interface/Colour.h" -#include "client/RequestListener.h" +#include "client/requestbroker/RequestListener.h" namespace ui { diff --git a/src/interface/SaveButton.cpp b/src/interface/SaveButton.cpp index 87b80a6..ddd99af 100644 --- a/src/interface/SaveButton.cpp +++ b/src/interface/SaveButton.cpp @@ -5,7 +5,7 @@ #include "client/SaveInfo.h" #include "graphics/Graphics.h" #include "Engine.h" -#include "client/RequestBroker.h" +#include "client/requestbroker/RequestBroker.h" #include "simulation/SaveRenderer.h" #include "Format.h" #include "ContextMenu.h" diff --git a/src/interface/SaveButton.h b/src/interface/SaveButton.h index e03d48a..0583d77 100644 --- a/src/interface/SaveButton.h +++ b/src/interface/SaveButton.h @@ -6,7 +6,7 @@ #include "Component.h" #include "client/SaveFile.h" #include "client/SaveInfo.h" -#include "client/RequestListener.h" +#include "client/requestbroker/RequestListener.h" #include "graphics/Graphics.h" #include "interface/Colour.h" diff --git a/src/save/LocalSaveActivity.cpp b/src/save/LocalSaveActivity.cpp index 58c9a63..455b6da 100644 --- a/src/save/LocalSaveActivity.cpp +++ b/src/save/LocalSaveActivity.cpp @@ -3,7 +3,7 @@ #include "interface/Textbox.h" #include "interface/Button.h" #include "search/Thumbnail.h" -#include "client/RequestBroker.h" +#include "client/requestbroker/RequestBroker.h" #include "dialogues/ErrorMessage.h" #include "dialogues/ConfirmPrompt.h" #include "client/Client.h" diff --git a/src/save/LocalSaveActivity.h b/src/save/LocalSaveActivity.h index dd33fc4..51b1ec9 100644 --- a/src/save/LocalSaveActivity.h +++ b/src/save/LocalSaveActivity.h @@ -2,7 +2,7 @@ #include "Activity.h" #include "client/SaveFile.h" -#include "client/RequestListener.h" +#include "client/requestbroker/RequestListener.h" namespace ui { diff --git a/src/save/ServerSaveActivity.cpp b/src/save/ServerSaveActivity.cpp index 1481b6d..94d609a 100644 --- a/src/save/ServerSaveActivity.cpp +++ b/src/save/ServerSaveActivity.cpp @@ -3,7 +3,7 @@ #include "interface/Textbox.h" #include "interface/Button.h" #include "interface/Checkbox.h" -#include "client/RequestBroker.h" +#include "client/requestbroker/RequestBroker.h" #include "dialogues/ErrorMessage.h" #include "dialogues/ConfirmPrompt.h" #include "client/Client.h" diff --git a/src/save/ServerSaveActivity.h b/src/save/ServerSaveActivity.h index 9e37748..6143074 100644 --- a/src/save/ServerSaveActivity.h +++ b/src/save/ServerSaveActivity.h @@ -2,7 +2,7 @@ #include "Activity.h" #include "client/SaveInfo.h" -#include "client/RequestListener.h" +#include "client/requestbroker/RequestListener.h" #include "tasks/TaskListener.h" namespace ui -- cgit v0.9.2-21-gd62e