Last active
June 23, 2020 04:17
-
-
Save rubentorresbonet/18a1eef20843a4197296 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* AsynchronousPictureLoader.cpp | |
* | |
* Created on: 13.08.2014 | |
* Author: ruben.torres | |
*/ | |
#include <curl/curl.h> | |
#include <thread> | |
#include "AsynchronousPictureLoader.h" | |
// ------------------------------------------------------------------------------ | |
using namespace std; | |
// ------------------------------------------------------------------------------ | |
AsynchronousPictureLoader* AsynchronousPictureLoader::classInstance = nullptr; | |
once_flag AsynchronousPictureLoader::singletonOnceFlag; | |
// ------------------------------------------------------------------------------ | |
void AsynchronousPictureLoader::destroy() { | |
stopLoop = true; | |
queueConditionVariable.notify_one(); | |
} | |
// ------------------------------------------------------------------------------ | |
AsynchronousPictureLoader::~AsynchronousPictureLoader() | |
{ | |
} | |
// ------------------------------------------------------------------------------ | |
AsynchronousPictureLoader* | |
AsynchronousPictureLoader::instance() | |
{ | |
std::call_once(AsynchronousPictureLoader::singletonOnceFlag, [] () { | |
classInstance = new AsynchronousPictureLoader(); | |
curl_global_init(CURL_GLOBAL_ALL); | |
// 2. Run its loop in its own thread. | |
thread runLoopThread(&AsynchronousPictureLoader::runLoop, classInstance); | |
runLoopThread.detach(); | |
}); | |
return classInstance; | |
} | |
// ------------------------------------------------------------------------------ | |
void | |
AsynchronousPictureLoader::addTask(const std::string url, const std::string cachePath, std::function<void(std::string)> callback) | |
{ | |
// 1. Secure our queue access. | |
lock_guard<std::mutex> taskLock(queueMutex); | |
// 2. Insert the new task. | |
queue.emplace(make_tuple(url, cachePath, callback)); | |
// 3. Notify the loop thread. | |
queueConditionVariable.notify_one(); | |
} | |
// ------------------------------------------------------------------------------ | |
void | |
AsynchronousPictureLoader::runLoop() | |
{ | |
std::unique_lock<std::mutex> loopLock(waitMutex); | |
while (1) | |
{ | |
// 1. Wait for an event if the queue is empty. | |
unique_lock<std::mutex> taskLock(queueMutex); | |
unsigned int queueSize = queue.size(); | |
taskLock.unlock(); | |
if (queueSize == 0) { | |
queueConditionVariable.wait(loopLock); | |
} | |
// 2. Check if daddy told us to stop. | |
if (stopLoop) { | |
return; | |
} | |
// 3. Get the next task. | |
taskLock.lock(); | |
AsynchronousPictureTask task = queue.front(); | |
queue.pop(); | |
taskLock.unlock(); | |
// 4. Start download. | |
processDownload(task); | |
} | |
} | |
// ------------------------------------------------------------------------------ | |
static size_t | |
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) | |
{ | |
size_t realsize = size * nmemb; | |
struct AsynchronousPictureLoader::MemoryStruct *mem = (struct AsynchronousPictureLoader::MemoryStruct *)userp; | |
mem->memory = (char*)realloc(mem->memory, mem->size + realsize + 1); | |
if(mem->memory == NULL) { | |
/* out of memory! */ | |
printf("not enough memory (realloc returned NULL)\n"); | |
return 0; | |
} | |
memcpy(&(mem->memory[mem->size]), contents, realsize); | |
mem->size += realsize; | |
mem->memory[mem->size] = 0; | |
return realsize; | |
} | |
// ------------------------------------------------------------------------------ | |
void | |
AsynchronousPictureLoader::processDownload(const AsynchronousPictureTask &task) | |
{ | |
string url = std::get<0>(task); | |
string cachePath = std::get<1>(task); | |
auto callback = std::get<2>(task); | |
CURL *curl_handle; | |
CURLcode res; | |
struct AsynchronousPictureLoader::MemoryStruct chunk; | |
chunk.memory = (char*)malloc(1); /* will be grown as needed by the realloc above */ | |
chunk.size = 0; /* no data at this point */ | |
curl_handle = curl_easy_init(); | |
curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str()); | |
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); | |
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); | |
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); | |
curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 2L ); | |
curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1 ); | |
res = curl_easy_perform(curl_handle); | |
CCImage* image = nullptr; | |
if(res != CURLE_OK) { | |
CCLOG("RBN: processDownload: error: %s", curl_easy_strerror(res)); | |
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); | |
} | |
else { | |
// Do whatever you want with the image. | |
callback(url); | |
} | |
curl_easy_cleanup(curl_handle); | |
if(chunk.memory) { | |
free(chunk.memory); | |
} | |
} | |
// ------------------------------------------------------------------------------ | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* AsynchronousPictureLoader.h | |
* | |
* Created on: 13.08.2014 | |
* Author: ruben.torres | |
*/ | |
#pragma once | |
#include <condition_variable> | |
#include <queue> | |
#include <mutex> | |
#include <thread> | |
class AsynchronousPictureLoader { | |
public: | |
static AsynchronousPictureLoader *instance(); | |
/** | |
* Add a task to our download queue and inform the caller back whenever it was successfully (or not) completed. | |
*/ | |
void addTask(const std::string url, const std::string cachePath, std::function<void(std::string)>); | |
void destroy(); | |
struct MemoryStruct { | |
char *memory; | |
size_t size; | |
}; | |
private: | |
AsynchronousPictureLoader() = default; | |
~AsynchronousPictureLoader(); | |
// The main run loop: poll for tasks and download them. | |
void runLoop(); | |
bool stopLoop = false; | |
// Synchronization. | |
std::condition_variable queueConditionVariable; | |
// Download. | |
typedef std::tuple<std::string, std::string, std::function<void(bool)>> AsynchronousPictureTask; | |
void processDownload(const AsynchronousPictureTask &task); | |
std::queue<AsynchronousPictureTask> queue; | |
std::mutex queueMutex; | |
std::mutex waitMutex; | |
// Singleton. | |
static AsynchronousPictureLoader *classInstance; | |
static std::once_flag singletonOnceFlag; | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment