I perform curl_multi_perform in a background thread of my GUI app. I am 
downloading data from HTTPS server. Usually, all is working, however after
internet connection is lost and restored again, I receive CURLE_COULDNT_
RESOLVE_HOST and no new download is ever started again (the server is OK -
if I stop app and start it again, all is working again until the connection
lost)

I am using curl with iOS ssl library.
My code is attached (MyStringAnsi is more or less std::string)
#include "./DataDownloader.h"

#if defined(_MSC_VER) && defined(__clang__)
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#endif


#ifdef _MSC_VER
	#pragma comment(lib, "ws2_32.lib")	
	#pragma comment(lib, "libcurl.lib")

	#include <winsock2.h>			
#else
    #include <unistd.h>
#endif


#include <cstdlib>
#include <thread>
#include <chrono>
#include <curl/curl.h>


//===================================================================================
// Data downloader
//===================================================================================


const std::vector<char> DataDownloader::EMPTY_DATA = std::vector<char>();

/// <summary>
/// ctor
/// </summary>
DataDownloader::DataDownloader() : internetConnectionAvailable(true),
    dlBeginCallback(nullptr), dlEndedCallback(nullptr),
    connectionFailureCallback(nullptr), connectionBackCallback(nullptr),
    lastID(0), running(false)
{
	curl_global_init(CURL_GLOBAL_DEFAULT);
	this->curlm = curl_multi_init();
    
    this->lastFailureTime = std::chrono::time_point<std::chrono::high_resolution_clock>::max();
}

/// <summary>
/// dtor
/// </summary>
DataDownloader::~DataDownloader()
{
	for (auto j : this->jobs)
	{
		j.second->Kill();
	}
	

	if (this->dlThread.joinable())
	{
		this->dlThread.join();
	}

	

	curl_multi_cleanup(this->curlm);
	curl_global_cleanup();
}

/// <summary>
/// Manually enable / disable internet connection
/// </summary>
/// <param name="val"></param>
void DataDownloader::SetIsInternetConnectionAvailable(bool val)
{
    this->internetConnectionAvailable = val;
}

/// <summary>
/// Test if we manualy enabled / disabled internet connection is active
/// </summary>
/// <returns></returns>
bool DataDownloader::IsInternetConnectionAvailable() const
{
    return this->internetConnectionAvailable;
}

/// <summary>
/// Test if we can download data based on result of last download.
/// If last download ended with failure (some CURL error), and time since this failure
/// is < N ms, returns false
/// otherwise returns true - new download can be "added" and tries if it succeed
///
/// Warning:
/// If download failes from server A, it does not mean that it will fail from server B.
/// However, this is not used and failure is globall for all servers. One faild = all failed 
/// </summary>
/// <returns></returns>
bool DataDownloader::CanDownloadAfterFailure() const
{
    if (this->lastFailureTime.load() == std::chrono::time_point<std::chrono::high_resolution_clock>::max())
    {
        return true;
    }
    
    auto curTime = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(curTime - this->lastFailureTime.load()).count();
   
    if (duration < 300)
    {
        return false;
    }
    
    return true;
}


/// <summary>
/// Check if download job exist
/// </summary>
/// <param name="downloadID">download job ID</param>
/// <returns>true / false</returns>
bool DataDownloader::Exist(DownloadID downloadID) const
{
	if (this->jobs.size() == 0)
	{
		return false;
	}

	auto it = this->jobs.find(downloadID);

	if (it == this->jobs.end())
	{
		return false;
	}

	return true;
}

/// <summary>
/// Test if any of jobs has the given URL
/// </summary>
/// <param name="url">downloaded URL</param>
/// <returns>true / false</returns>
bool DataDownloader::ExistURL(const MyStringAnsi & url) const
{
	if (this->jobs.size() == 0)
	{
		return false;
	}

	for (auto & j : this->jobs)
	{
		if (j.second->url == url)
		{
			return true;
		}
	}

	return false;
}


void DataDownloader::SetDownloadBeginCallback(std::function<void()> dlBeginCallback)
{
    this->dlBeginCallback = dlBeginCallback;
}


void DataDownloader::SetDownloadEndedCallback(std::function<void()> dlEndedCallback)
{
    this->dlEndedCallback = dlEndedCallback;
}

void DataDownloader::SetNoInternetConnectionFailureCallback(std::function<void()> connectionFailureCallback,
                                                            std::function<void()> connectionBackCallback)
{
    this->connectionFailureCallback = connectionFailureCallback;
    this->connectionBackCallback = connectionBackCallback;
}

/// <summary>
/// Get download job by its DownloadID
/// </summary>
/// <param name="downloadID">download job ID</param>
/// <returns>download job or nullptr</returns>
std::shared_ptr<DownloadJob> DataDownloader::GetDownloadJob(DownloadID downloadID) const
{
	auto it = this->jobs.find(downloadID);

	if (it == this->jobs.end())
	{
		return nullptr;
	}

	return it->second;
}

/// <summary>
/// Add new download to queue and start it immediately
/// If there is no internet connection, download wont be added at all !!!
/// If last download failed, new download will be added only after N ms timeout
/// If download with the same URL exist, do not add and return nullptr
/// The name of download will be the same as url
/// </summary>
/// <param name="url">URL for download</param>
/// <returns>new DownloadJob or nullptr</returns>
std::shared_ptr<DownloadJob> DataDownloader::AddDownload(const MyStringAnsi & url)
{
	return this->AddDownload(url, url);
}

/// <summary>
/// Add new download to queue and start it immediately
/// If there is no internet connection, download wont be added at all !!!
/// If last download failed, new download will be added only after N ms timeout
/// If download with the same URL exist, do not add and return nullptr
/// </summary>
/// <param name="url">URL for download</param>
/// <param name="name">name of download</param>
/// <returns>new DownloadJob or nullptr</returns>
std::shared_ptr<DownloadJob> DataDownloader::AddDownload(const MyStringAnsi & url, const MyStringAnsi & name)
{
	return this->AddDownload(url, name, nullptr, DATA_TYPE::BINARY);
}

/// <summary>
/// Add new download to queue and start it immediately
/// If there is no internet connection, download wont be added at all !!!
/// If last download failed, new download will be added only after N ms timeout
/// If download with the same URL exist, do not add and return nullptr
/// Can specify onFinish callback, that is called after data are downloaded
/// (no matter if successfully or not)
/// </summary>
/// <param name="url">URL for download</param>
/// <param name="name">name of download</param>
/// <param name="onFinish">onFinish callback (have pointer to dowloadJob)</param>
/// <param name="dataType">specify data type. For text data, 0 is appended to downloaded data</param>
/// <returns>new DownloadJob or nullptr</returns>
std::shared_ptr<DownloadJob> DataDownloader::AddDownload(const MyStringAnsi & url, const MyStringAnsi & name,
	std::function<void(std::shared_ptr<DownloadJob>)> onFinish, DATA_TYPE dataType)
{
	return this->AddDownload(url, name, onFinish, nullptr, dataType);
}


/// <summary>
/// Add new download to queue and start it immediately
/// If there is no internet connection, download wont be added at all !!!
/// If last download failed, new download will be added only after N ms timeout
/// If download with the same URL exist, do not add and return nullptr
/// Can specify onFinish callback, that is called after data are downloaded
/// (no matter if successfully or not)
/// Can specify onFinishMainThread callback, that is called after data are downloaded
/// (no matter if successfully or not) on main thread -> for this to work, Update method must
/// be called
/// </summary>
/// <param name="url">URL for download</param>
/// <param name="name">name of download</param>
/// <param name="onFinish">onFinish callback (have pointer to dowloadJob)</param>
/// <param name="onFinishMainThread">onFinishMainThread callback (have pointer to dowloadJob)</param>
/// <param name="dataType">specify data type. For text data, 0 is appended to downloaded data</param>
/// <returns>new DownloadJob or nullptr</returns>
std::shared_ptr<DownloadJob> DataDownloader::AddDownload(const MyStringAnsi & url, const MyStringAnsi & name,
	std::function<void(std::shared_ptr<DownloadJob>)> onFinish,
	std::function<void(std::shared_ptr<DownloadJob>)> onFinishMainThread, DATA_TYPE dataType)
{
    if (this->IsInternetConnectionAvailable() == false)
    {
        return nullptr;
    }
    
    if (this->CanDownloadAfterFailure() == false)
    {
        return nullptr;
    }
    
	if (this->activeURLs.find(url) != this->activeURLs.end())
	{
		return nullptr;
	}

	std::shared_ptr<DownloadJob> job = std::shared_ptr<DownloadJob>(new DownloadJob(url, name, lastID, this));
	job->onFinish = onFinish;
	job->onFinishMainThread = onFinishMainThread;
	job->dataType = dataType;

	lastID++;

	//https://solarianprogrammer.com/2012/10/17/cpp-11-async-tutorial/
	//http://stackoverflow.com/questions/13669094/how-to-in-c11-use-stdasync-on-a-member-function


	this->jobs[job->id] = job;
	this->activeURLs.insert(url);

	job->Start();

	return job;
}

/// <summary>
/// Update called from main thread
/// It will call onFinishMainThread callbacks and remove finished downloads
/// </summary>
void DataDownloader::UpdateMainThread()
{
	if (this->jobs.size() == 0)
	{
		return;
	}

	for (auto j : this->jobs)
	{
		if ((j.second->IsFinished()) && (j.second->onFinishMainThread != nullptr))
		{			
			j.second->onFinishMainThread(j.second);
		}
	}

	this->RemoveFinishedDownloads();

}

/// <summary>
/// Remve finished downloads
/// </summary>
void DataDownloader::RemoveFinishedDownloads()
{
	if (this->jobs.size() == 0)
	{
		return;
	}

	std::list<DownloadID> finished;

	for (auto j : this->jobs)
	{		
		if (j.second->IsFinished())
		{			
			finished.push_back(j.first);
		}
	}

	for (auto dlId : finished)
	{
		auto job = this->GetDownloadJob(dlId);

		job->RemoveFromDataDownloader();		
	}
}

/// <summary>
/// Check if something is downloading
/// </summary>
/// <returns></returns>
bool DataDownloader::IsDownloading() const
{
	return this->running;
}

/// <summary>
/// Check if given download is downloading
/// </summary>
/// <param name="downloadID"></param>
/// <returns></returns>
bool DataDownloader::IsDownloading(DownloadID downloadID) const
{
	return (this->GetDownloadJob(downloadID)->IsFinished() ? false : true);
}

/// <summary>
/// Try to manually start downloads if something failed
/// in automatic starts
/// </summary>
void DataDownloader::TryStart()
{
	std::unique_lock<std::mutex> lck(dlThreadLock);

	if (this->running.load())
	{
		return;
	}

	if (this->jobQueue.size() == 0)
	{
		return;
	}

	this->Start();
}

void DataDownloader::WaitToFinish(DownloadID downloadID)
{
	auto j = this->GetDownloadJob(downloadID);

	unsigned timeOutMiliSeconds = 10;
	while (j->IsFinished() == false)
	{
#ifdef WIN32
		Sleep(timeOutMiliSeconds);
#else
		usleep(timeOutMiliSeconds * 1000);
#endif
        this->TryStart();
	}
    
    
    j->RemoveFromDataDownloader();
	
}

/// <summary>
/// Start downloading in background thread
/// </summary>
void DataDownloader::Start()
{
    this->running.store(true);
    
	if (this->dlThread.joinable())
	{
		this->dlThread.join();
	}
    
	this->dlThread = std::thread(&DataDownloader::UpdateBackgroundThread, this);
}



void DataDownloader::DownloadFailedBackgroundThread(std::shared_ptr<DownloadJob> job, CURLMsg * msg)
{
    switch (msg->data.result)
    {
        case CURLE_COULDNT_CONNECT:
        case CURLE_COULDNT_RESOLVE_HOST:
        case CURLE_COULDNT_RESOLVE_PROXY:
        case CURLE_OPERATION_TIMEDOUT:
            printf("No internet connection\n");
            if (this->connectionFailureCallback != nullptr)
            {
                this->connectionFailureCallback();
            }
            break;
        default:
            break;
    }
    
    printf("DL error: %d - %s <%s = %lu>\n", msg->data.result, curl_easy_strerror(msg->data.result), job->url.c_str(), job->data.size());
    
    this->lastFailureTime = std::chrono::high_resolution_clock::now();
}

void DataDownloader::DownloadOKBackgroundThread(std::shared_ptr<DownloadJob> job, CURLMsg * msg)
{
    if (this->lastFailureTime.load() != std::chrono::time_point<std::chrono::high_resolution_clock>::max())
    {
        if (this->connectionBackCallback != nullptr)
        {
            this->connectionBackCallback();
        }
        this->lastFailureTime = std::chrono::time_point<std::chrono::high_resolution_clock>::max();
    }
    printf("DL ok: <%s = %lu>\n", job->url.c_str(), job->data.size());

}

/// <summary>
/// Update cURL
/// </summary>
/// <returns>EXIT_FAILURE or EXIT_SUCCESS</returns>
int DataDownloader::UpdateBackgroundThread()
{
    if (this->dlBeginCallback)
    {
        this->dlBeginCallback();
    }

    
	this->running.store(true);

	int remainHandles = -1;
	int queueSize = -1;
	int descriptorsCount = -1;
	long timeOutMs = -1;
	fd_set R, W, E;
	struct timeval T;

	
	
	while (remainHandles)
	{		
		std::unique_lock<std::mutex> lck(dlThreadLock);

        CURLMcode mErr = curl_multi_perform(this->curlm, &remainHandles);
        if (mErr != CURLM_OK)
        {
            printf("curl_multi_perform error %i\n", mErr);
        }

		if (remainHandles)
		{
			FD_ZERO(&R);
			FD_ZERO(&W);
			FD_ZERO(&E);

			if (curl_multi_fdset(this->curlm, &R, &W, &E, &descriptorsCount))
			{
				printf("E: curl_multi_fdset\n");
				return EXIT_FAILURE;
			}

			if (curl_multi_timeout(this->curlm, &timeOutMs))
			{
				printf("E: curl_multi_timeout\n");
				return EXIT_FAILURE;
			}
			if (timeOutMs == -1)
			{
				timeOutMs = 100;
			}

			if (descriptorsCount == -1)
			{
#ifdef WIN32
				Sleep(timeOutMs);
#else
				usleep(static_cast<useconds_t>(timeOutMs * 1000));
#endif
			}
			else
			{
				T.tv_sec = timeOutMs / 1000;
				T.tv_usec = (timeOutMs % 1000) * 1000;

				if (select(descriptorsCount + 1, &R, &W, &E, &T) < 0)
				{
					printf("E: select(%i,,,,%li): %i: %s\n", descriptorsCount + 1, timeOutMs, errno, strerror(errno));
					return EXIT_FAILURE;
				}
			}
		}


		while (CURLMsg * msg = curl_multi_info_read(this->curlm, &queueSize))
		{
			if (msg->msg == CURLMSG_DONE) 
			{				
				
				CURL *e = msg->easy_handle;

				//Find job with the curl handle
				std::shared_ptr<DownloadJob> job = nullptr;
				for (auto j : this->jobs)
				{
					if (j.second == nullptr)
					{
						continue;
					}
					if (j.second->curl == e)
					{
						job = j.second;
						break;
					}
				}

				if (job != nullptr)
				{										
					job->resCode = static_cast<int>(msg->data.result);

                    if (job->resCode != 0)
                    {
                        this->DownloadFailedBackgroundThread(job, msg);
                    }
                    else //resCode == 0
                    {
                        this->DownloadOKBackgroundThread(job, msg);

						if (job->dataType == DATA_TYPE::TEXT)
						{
							//append trailing 0 to data
							job->data.push_back(0);
						}
                    }
                
					
					if ((job->onFinish != nullptr) && (job->shouldCancel == false))
					{
						//onFinish callback called only if download is complete
						//not canceled
						job->onFinish(job);
					}

					job->finished.store(true);

					//unlock it, so we can "kill" job
					//during killing job, mutex is locked again, because we are editing multi api
					lck.unlock();
					job->Kill();
					lck.lock();
				}
			}
			else 
			{
				printf("E: CURLMsg (%d)\n", msg->msg);
			}			
		}
		
		if (this->jobQueue.size() != 0)
		{						
			curl_multi_add_handle(this->curlm, this->jobQueue.front()->curl);
			this->jobQueue.pop_front();
			remainHandles++;
		}
		
	}

	this->running.store(false);

    if (this->dlEndedCallback)
    {
        this->dlEndedCallback();
    }

    
	return EXIT_SUCCESS;
}

//===================================================================================
// Static callbacks
//===================================================================================

/// <summary>
/// cURL write data callback
/// </summary>
/// <param name="data">downloaded data</param>
/// <param name="size">size of single value</param>
/// <param name="nmemb">number of values</param>
/// <param name="j">void pointer to DownloadJob</param>
/// <returns>byte size of downloaded data (size * nmemb)</returns>
size_t DownloadJob::curlWriteData(char *data, size_t size, size_t nmemb, void * j)
{
	if (j == nullptr)
	{
		return 0;
	}

	DownloadJob * job = static_cast<DownloadJob *>(j);
	
	job->data.insert(job->data.end(), data, data + size * nmemb);

	return size * nmemb;
}

/// <summary>
/// cURL download progress info
/// </summary>
/// <param name="j">void pointer to DownloadJob</param>
/// <param name="dltotal"></param>
/// <param name="dlnow"></param>
/// <param name="ultotal"></param>
/// <param name="ulnow"></param>
/// <returns>1 for download cancel, 0 for continue</returns>
size_t DownloadJob::curlDownloadInfo(void * j,
	curl_off_t dltotal, curl_off_t dlnow,
	curl_off_t ultotal, curl_off_t ulnow)
{
	if (j == nullptr)
	{
		return 0;
	}

	DownloadJob * job = static_cast<DownloadJob *>(j);

	if (job->shouldCancel)
	{
		return 1;
	}

	return 0;
}


//===================================================================================
// Download job
//===================================================================================


DownloadJob::DownloadJob(const MyStringAnsi & url, const MyStringAnsi & name, DownloadID id, DataDownloader * dd) :
	id(id),
	url(url),
	name(name),
	data(DataDownloader::EMPTY_DATA),
	finished(false),
	dd(dd),
	curl(nullptr),
	resCode(-1),
	shouldCancel(false),
	onFinish(nullptr),
	onFinishMainThread(nullptr),
	dataType(DataDownloader::BINARY)
{
};

/// <summary>
/// ctor
/// </summary>
DownloadJob::~DownloadJob() 
{ 
	this->RemoveFromDataDownloader();
	
}

/// <summary>
/// Remove job from data downloader
/// If download is not finished, it will wait in while
/// loop with sleeps
/// </summary>
void DownloadJob::RemoveFromDataDownloader()
{
	if (dd->Exist(this->id) == false)
	{
		return;
	}
	while (this->finished.load() == false)
	{
#ifdef WIN32
		Sleep(10);
#else
		usleep(10 * 1000);
#endif		
	}

	//std::lock_guard<std::mutex> lock(memCacheLock); ?

	dd->jobs.erase(this->id);
	dd->activeURLs.erase(this->url);
}

/// <summary>
/// Start downloading
/// </summary>
/// <returns></returns>
int DownloadJob::Start()
{
		
	if (dd->jobs.find(this->id) == dd->jobs.end())
	{
		return EXIT_FAILURE;
	}

	this->curl = curl_easy_init();
	if (this->curl == nullptr)
	{
		return EXIT_FAILURE;
	}

	

	curl_easy_setopt(this->curl, CURLOPT_URL, this->url.c_str());
	curl_easy_setopt(this->curl, CURLOPT_FOLLOWLOCATION, 1L);
	//curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); //Prevent "longjmp causes uninitialized stack frame" bug
	//curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "deflate");	
	curl_easy_setopt(this->curl, CURLOPT_WRITEFUNCTION, DownloadJob::curlWriteData);
	curl_easy_setopt(this->curl, CURLOPT_WRITEDATA, this);

	//enable cancellation: https://curl.haxx.se/libcurl/c/progressfunc.html - see CURLOPT_XFERINFOFUNCTION 
	curl_easy_setopt(this->curl, CURLOPT_XFERINFOFUNCTION, DownloadJob::curlDownloadInfo);
	curl_easy_setopt(this->curl, CURLOPT_XFERINFODATA, this);
	curl_easy_setopt(this->curl, CURLOPT_NOPROGRESS, 0L);
    
	//abort if slower than 30 bytes/sec during 10 seconds
	curl_easy_setopt(this->curl, CURLOPT_LOW_SPEED_LIMIT, 30L);
	curl_easy_setopt(this->curl, CURLOPT_LOW_SPEED_TIME, 5L);
	

    //https://stackoverflow.com/questions/1341644/curl-and-https-cannot-resolve-host
    //curl_easy_setopt(this->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
    //curl_easy_setopt(this->curl, CURLOPT_VERBOSE, 1);
                     
	//enable https: https://curl.haxx.se/libcurl/c/https.html
	if (this->url.Find("https://";) != -1)
	{
		curl_easy_setopt(this->curl, CURLOPT_SSL_VERIFYPEER, 0L);
		curl_easy_setopt(this->curl, CURLOPT_SSL_VERIFYHOST, 0L);
	}
	
	if (dd->running.load())
	{		
		dd->jobQueue.push_back(dd->jobs[this->id]);			
	}
	else 
	{
		printf("not-running\n");
		curl_multi_add_handle(dd->curlm, this->curl);
		dd->Start();		
	}

	return EXIT_SUCCESS;
}

/// <summary>
/// Kill cURL
/// If download is running, cancel it first
/// Canceled download will call again Kill method
/// </summary>
void DownloadJob::Kill()
{	
	if (this->curl == nullptr)
	{
		return;
	}

	if (dd->Exist(this->id) == false)
	{
		return;
	}
	
	if (this->finished.load() == false)
	{
		this->Cancel();
	}
	else
	{		
		std::lock_guard<std::mutex> lck(dd->dlThreadLock);
		if (this->curl == nullptr)
		{
			return;
		}
		curl_multi_remove_handle(dd->curlm, this->curl);
		curl_easy_cleanup(this->curl);	
		this->curl = nullptr;
	}

}

/// <summary>
/// Set cancel flag
/// </summary>
void DownloadJob::Cancel()
{
	this->shouldCancel = true;	
}
#ifndef _DATA_DOWNLOADER_H_
#define _DATA_DOWNLOADER_H_

#include <unordered_map>
#include <vector>
#include <memory>
#include <future>
#include <atomic>
#include <thread>
#include <mutex>
#include <list>
#include <set>
#include <functional>
#include <chrono>

#include "./Strings/MyString.h"

class DownloadJob;
struct CURLMsg;

typedef size_t DownloadID;
typedef void CURL;
typedef void CURLM;

#ifdef _WIN32
typedef long long curl_off_t;
#elif __ANDROID_API__
#include "curl/curlbuild.h" // typedef is here
#else
typedef long curl_off_t;
#endif


//===================================================================================

class DataDownloader 
{
public:
	typedef enum DATA_TYPE 
	{
		BINARY = 0,
		TEXT = 1
	} DATA_TYPE;

	DataDownloader();
	~DataDownloader();

    void SetIsInternetConnectionAvailable(bool val);
    bool IsInternetConnectionAvailable() const;
    
    
	std::shared_ptr<DownloadJob> AddDownload(const MyStringAnsi & url);
	std::shared_ptr<DownloadJob> AddDownload(const MyStringAnsi & url, const MyStringAnsi & name);
	std::shared_ptr<DownloadJob> AddDownload(const MyStringAnsi & url, const MyStringAnsi & name,
		std::function<void(std::shared_ptr<DownloadJob>)> onFinish, DATA_TYPE dataType);
	std::shared_ptr<DownloadJob> AddDownload(const MyStringAnsi & url, const MyStringAnsi & name,
		std::function<void(std::shared_ptr<DownloadJob>)> onFinish, 
		std::function<void(std::shared_ptr<DownloadJob>)> onFinishMainThread, DATA_TYPE dataType);
	
    void SetDownloadBeginCallback(std::function<void()> dlBeginCallback);
    void SetDownloadEndedCallback(std::function<void()> dlEndedCallback);
    void SetNoInternetConnectionFailureCallback(std::function<void()> connectionFailureCallback,
                                                std::function<void()> connectionBackCallback);
    
	bool IsDownloading() const;
	bool IsDownloading(DownloadID downloadID) const;

	void TryStart();
	void WaitToFinish(DownloadID downloadID);

	bool Exist(DownloadID downloadID) const;
	bool ExistURL(const MyStringAnsi & url) const;
		
	std::shared_ptr<DownloadJob> GetDownloadJob(DownloadID downloadID) const;
	

	void UpdateMainThread();
	

	friend class DownloadJob;

protected:
	static const std::vector<char> EMPTY_DATA;
	
    bool internetConnectionAvailable;
    std::function<void()> dlBeginCallback;
    std::function<void()> dlEndedCallback;
    std::function<void()> connectionFailureCallback;
    std::function<void()> connectionBackCallback;
    
	CURLM * curlm;
	std::thread dlThread;
	std::mutex dlThreadLock;

	std::atomic<bool> running;	
	std::unordered_map<DownloadID, std::shared_ptr<DownloadJob>> jobs;
	std::set<MyStringAnsi> activeURLs;
	std::list<std::shared_ptr<DownloadJob>> jobQueue;
	DownloadID lastID;
    
    //nemelo by to byt atomic?
    std::atomic<std::chrono::time_point<std::chrono::high_resolution_clock>> lastFailureTime;

	void Start();
	int UpdateBackgroundThread();
    void DownloadFailedBackgroundThread(std::shared_ptr<DownloadJob> job, CURLMsg * msg);
    void DownloadOKBackgroundThread(std::shared_ptr<DownloadJob> job, CURLMsg * msg);
    
	void RemoveFinishedDownloads();
    bool CanDownloadAfterFailure() const;
};

//===================================================================================



class DownloadJob
{
public:
	const DownloadID id;
	
	~DownloadJob();
	
	void RemoveFromDataDownloader();

	int GetResultStatus() const { return this->resCode; }

	bool IsFinished() const { return (finished.load()); }
	void Cancel();
	const MyStringAnsi & GetName() const { return this->name; }
    const MyStringAnsi & GetURL() const { return this->url; }
	const std::vector<char> & GetData() const { return this->data; }

	friend class DataDownloader;
	//friend static int curlWriteData(char *data, size_t size, size_t nmemb, void * j);

protected:	
	DownloadJob(const MyStringAnsi & url, const MyStringAnsi & name, DownloadID id, DataDownloader * dd);
	

	MyStringAnsi url;
	MyStringAnsi name;
	std::vector<char> data;
	std::atomic<bool> finished;
	DataDownloader * dd;
	CURL * curl;
	int resCode;
	bool shouldCancel;
	std::function<void(std::shared_ptr<DownloadJob>)> onFinish;
	std::function<void(std::shared_ptr<DownloadJob>)> onFinishMainThread;
	DataDownloader::DATA_TYPE dataType;

	static size_t curlWriteData(char *data, size_t size, size_t nmemb, void * j);
	static size_t curlDownloadInfo(void *j, curl_off_t dltotal, curl_off_t dlnow,
		curl_off_t ultotal, curl_off_t ulnow);

	int Start();
	void Kill();

};

#endif
-------------------------------------------------------------------
Unsubscribe: https://cool.haxx.se/list/listinfo/curl-library
Etiquette:   https://curl.haxx.se/mail/etiquette.html

Reply via email to