cURL / Mailing Lists / curl-library / Single Mail

curl-library

Re: Running curl in separate SDL thread

From: Trevor Allen <trevor.allen86_at_gmail.com>
Date: Fri, 14 Aug 2009 02:37:04 -0700

Basically what I am trying to do is to use classData to store the data
I need between the curl thread to download a file and the member
variables of the class which aren't in the same thread.

If I place *iData->progressPtr as the PROGRESSDATA variable I get a
pointer error. If I create a local float variable and pass that in,
then it works just fine but my member variables (mData and mProgress)
don't receive the updates since they aren't being used as the data
sources.

Downloader::writer() is the WRITEFUNCTION and Downloader::progress()
function is the PROGRESSFUNCTION.

This is for an updater application for a game, so this class would
handle downloading files as needed.

I hope I provided enough info.

======HEADER========
#ifndef _LOM_DOWNLOADER_
#define _LOM_DOWNLOADER_

#include "../Utility/Filesystem.h"
#include "../Utility/Logger.h"

#define CURL_STATICLIB
#include "curl/curl.h"

#include "SDL/SDL_thread.h"

#include <iostream>
#include <string>
#include <vector>

struct internalData
{
        bool *finishedPtr;
        std::string *urlPtr;
        std::string *dataPtr;
        float *progressPtr;
        vector<string> *errorMessageList;
};

class Downloader
{
public:
        Downloader();
        ~Downloader();

        void download(const char* url);
        bool downloadFinished();
        std::string getData();
        int getSize() { return mSize; }
        
        double getProgress() { return mProgress; }

private:
        static int writer(char *data, size_t size, size_t nmemb, std::string
*writerData);
        static int progress(double *ptr, double dlTotalSize, double
dlCurrentSize, double ul, double ut);
        static int download_func(void *progressPtr);

        SDL_Thread *downloadThread;
        SDL_mutex *progressMutex;

        std::string mData;
        std::string mDownloadURL;

        float mProgress;
        bool mIsFinished;

        int mSize;

        std::vector<string> mErrorMessages;

        internalData classData;
                
};
#endif

======CPP===========
#include "Downloader.h"

Downloader::Downloader() : downloadThread(NULL),
                                                        mDownloadURL(""),
                                                        mProgress(0.0),
                                                        mIsFinished(false),
                                                        mSize(0),
                                                        mData("")
{

        // Set up the class data stuff so we can properly access it.
        classData.urlPtr = &mDownloadURL;
        classData.dataPtr = &mData;
        classData.progressPtr = &mProgress;
        classData.finishedPtr = &mIsFinished;
        classData.errorMessageList = &mErrorMessages;

}

Downloader::~Downloader()
{
        for(unsigned int i = 0; i < mErrorMessages.size(); i++)
                Logger::logFile << mErrorMessages[i] << endl;
}

void Downloader::download(const char* url)
{
        mDownloadURL = url;
        if(mIsFinished)
        {
                mData = "";
                mIsFinished = false;
                mProgress = 0.0;
        }
        
        

        if(downloadThread == NULL)
                downloadThread = SDL_CreateThread(Downloader::download_func,
&classData);
        else
                mErrorMessages.push_back("You tard! There's a download already in
progress!!");
        

}

int Downloader::writer(char *data, size_t size, size_t nmemb,
std::string *buffer)
{
        // What we will return
        int result = 0;

        // Check that our buffer is not NULL.
        if (buffer != NULL)
        {
                // Append the data to the buffer
                buffer->append(data, size * nmemb);

                // How much did we write?
                result = size * nmemb;
        }

        return result;
}

int Downloader::progress(double *ptr, double dlTotalSize, double
dlCurrentSize, double ul, double ut)
{
        // Get the progress in percentage by deviding the current bytes
downloaded by the
        // total number of bytes to download.
        double progress = dlCurrentSize / dlTotalSize;
        
        *ptr = progress;
        
        Logger::logFile << progress << endl;
        
        return 0;
}

int Downloader::download_func(void *ptr)
{
        internalData *iData = reinterpret_cast<internalData *>(ptr);
        
        CURL *mCurl;
        mCurl = curl_easy_init();
        float progressN = 0;
        std::string dataN;
        dataN = "";
        
        curl_easy_setopt(mCurl, CURLOPT_VERBOSE, 1) ;
        curl_easy_setopt(mCurl, CURLOPT_HEADER, 0);
        curl_easy_setopt(mCurl, CURLOPT_FOLLOWLOCATION, 1);
        curl_easy_setopt(mCurl, CURLOPT_NOPROGRESS, 0);
        curl_easy_setopt(mCurl, CURLOPT_PROGRESSFUNCTION, progress);
        curl_easy_setopt(mCurl, CURLOPT_PROGRESSDATA, iData->progressPtr);
        curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, writer);
        curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, &dataN);
        
        Logger::logFile << "(INF) Requesting URL '" << iData->urlPtr->c_str()
<< "'..." << endl;
        curl_easy_setopt(mCurl, CURLOPT_URL, iData->urlPtr->c_str());

        *iData->finishedPtr = false;
        CURLcode returnCode = curl_easy_perform(mCurl);
        *iData->finishedPtr = true;
        
        if(returnCode == CURLE_OK)
                *iData->finishedPtr = true;
        else;
                iData->errorMessageList->push_back(curl_easy_strerror(returnCode));

        Logger::logFile << "(INF) File has been downloaded." << endl;
        
        curl_easy_cleanup(mCurl);
        curl_global_cleanup();

        return 0;
}

bool Downloader::downloadFinished()
{
        if(mIsFinished)
        {
                if(downloadThread != NULL)
                {
                        SDL_KillThread(downloadThread);
                        downloadThread = NULL;
                }
        }

        return mIsFinished;
}

std::string Downloader::getData()
{
        if(mIsFinished)
                return mData;
        else
                return "";
}
On Aug 14, 2009, at 2:29 AM, Kamil Dudka wrote:

> On Fri August 14 2009 11:22:01 Trevor Allen wrote:
>> I hope I send this back right. So if I wanted to set the WRITEDATA
>> to
>> the std::string contained within iData, my best bet would be to use a
>> wrapper function? I can get it to work by using a local std::string
>> and float. Unfortunately pointers aren't my strongest skill yet.
>
> It would be best to attach the whole example here on the list if
> there are
> no sensitive (passwords, keys, etc.) data inside.
>
> Kamil
>
Received on 2009-08-14