ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

connectionmanager.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #define FORBIDDEN_SYMBOL_ALLOW_ALL
00024 
00025 #include <curl/curl.h>
00026 #include "backends/networking/curl/connectionmanager.h"
00027 #include "backends/networking/curl/networkreadstream.h"
00028 #include "common/debug.h"
00029 #include "common/system.h"
00030 #include "common/timer.h"
00031 
00032 namespace Common {
00033 
00034 DECLARE_SINGLETON(Networking::ConnectionManager);
00035 
00036 }
00037 
00038 namespace Networking {
00039 
00040 ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false), _frame(0) {
00041     curl_global_init(CURL_GLOBAL_ALL);
00042     _multi = curl_multi_init();
00043 }
00044 
00045 ConnectionManager::~ConnectionManager() {
00046     stopTimer();
00047 
00048     //terminate all requests
00049     _handleMutex.lock();
00050     for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end(); ++i) {
00051         Request *request = i->request;
00052         RequestCallback callback = i->onDeleteCallback;
00053         if (request)
00054             request->finish();
00055         delete request;
00056         if (callback)
00057             (*callback)(request);
00058     }
00059     _requests.clear();
00060 
00061     //cleanup
00062     curl_multi_cleanup(_multi);
00063     curl_global_cleanup();
00064     _multi = nullptr;
00065     _handleMutex.unlock();
00066 }
00067 
00068 void ConnectionManager::registerEasyHandle(CURL *easy) const {
00069     curl_multi_add_handle(_multi, easy);
00070 }
00071 
00072 Request *ConnectionManager::addRequest(Request *request, RequestCallback callback) {
00073     _addedRequestsMutex.lock();
00074     _addedRequests.push_back(RequestWithCallback(request, callback));
00075     if (!_timerStarted)
00076         startTimer();
00077     _addedRequestsMutex.unlock();
00078     return request;
00079 }
00080 
00081 Common::String ConnectionManager::urlEncode(Common::String s) const {
00082     if (!_multi)
00083         return "";
00084 #if LIBCURL_VERSION_NUM >= 0x070F04
00085     char *output = curl_easy_escape(_multi, s.c_str(), s.size());
00086 #else
00087     char *output = curl_escape(s.c_str(), s.size());
00088 #endif
00089     if (output) {
00090         Common::String result = output;
00091         curl_free(output);
00092         return result;
00093     }
00094     return "";
00095 }
00096 
00097 uint32 ConnectionManager::getCloudRequestsPeriodInMicroseconds() {
00098     return TIMER_INTERVAL * CLOUD_PERIOD;
00099 }
00100 
00101 //private goes here:
00102 
00103 void connectionsThread(void *ignored) {
00104     ConnMan.handle();
00105 }
00106 
00107 void ConnectionManager::startTimer(int interval) {
00108     Common::TimerManager *manager = g_system->getTimerManager();
00109     if (manager->installTimerProc(connectionsThread, interval, 0, "Networking::ConnectionManager's Timer")) {
00110         _timerStarted = true;
00111     } else {
00112         warning("Failed to install Networking::ConnectionManager's timer");
00113     }
00114 }
00115 
00116 void ConnectionManager::stopTimer() {
00117     debug(9, "timer stopped");
00118     Common::TimerManager *manager = g_system->getTimerManager();
00119     manager->removeTimerProc(connectionsThread);
00120     _timerStarted = false;
00121 }
00122 
00123 bool ConnectionManager::hasAddedRequests() {
00124     _addedRequestsMutex.lock();
00125     bool hasNewRequests = !_addedRequests.empty();
00126     _addedRequestsMutex.unlock();
00127     return hasNewRequests;
00128 }
00129 
00130 void ConnectionManager::handle() {
00131     //lock mutex here (in case another handle() would be called before this one ends)
00132     _handleMutex.lock();
00133     ++_frame;
00134     if (_frame % CLOUD_PERIOD == 0)
00135         interateRequests();
00136     if (_frame % CURL_PERIOD == 0)
00137         processTransfers();
00138 
00139     if (_requests.empty() && !hasAddedRequests())
00140         stopTimer();
00141     _handleMutex.unlock();
00142 }
00143 
00144 void ConnectionManager::interateRequests() {
00145     //add new requests
00146     _addedRequestsMutex.lock();
00147     for (Common::Array<RequestWithCallback>::iterator i = _addedRequests.begin(); i != _addedRequests.end(); ++i) {
00148         _requests.push_back(*i);
00149     }
00150     _addedRequests.clear();
00151     _addedRequestsMutex.unlock();
00152 
00153     //call handle() of all running requests (so they can do their work)
00154     debug(9, "handling %d request(s)", _requests.size());
00155     for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end();) {
00156         Request *request = i->request;
00157         if (request) {
00158             if (request->state() == PROCESSING)
00159                 request->handle();
00160             else if (request->state() == RETRY)
00161                 request->handleRetry();
00162         }
00163 
00164         if (!request || request->state() == FINISHED) {
00165             delete (i->request);
00166             if (i->onDeleteCallback)
00167                 (*i->onDeleteCallback)(i->request); //that's not a mistake (we're passing an address and that method knows there is no object anymore)
00168             _requests.erase(i);
00169             continue;
00170         }
00171 
00172         ++i;
00173     }
00174 }
00175 
00176 void ConnectionManager::processTransfers() {
00177     if (!_multi) return;
00178 
00179     //check libcurl's transfers and notify requests of messages from queue (transfer completion or failure)
00180     int transfersRunning;
00181     curl_multi_perform(_multi, &transfersRunning);
00182 
00183     int messagesInQueue;
00184     CURLMsg *curlMsg;
00185     while ((curlMsg = curl_multi_info_read(_multi, &messagesInQueue))) {
00186         CURL *easyHandle = curlMsg->easy_handle;
00187 
00188         NetworkReadStream *stream;
00189         curl_easy_getinfo(easyHandle, CURLINFO_PRIVATE, &stream);
00190         if (stream)
00191             stream->finished();
00192 
00193         if (curlMsg->msg == CURLMSG_DONE) {
00194             debug(9, "ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result));
00195         } else {
00196             warning("ConnectionManager: FAILURE (CURLMsg (%d))", curlMsg->msg);
00197         }
00198 
00199         curl_multi_remove_handle(_multi, easyHandle);
00200     }
00201 }
00202 
00203 } // End of namespace Cloud


Generated on Sat Mar 23 2019 05:01:24 for ResidualVM by doxygen 1.7.1
curved edge   curved edge