cURL / Mailing Lists / curl-library / Single Mail

curl-library

C++ header used for web page downloading from multiple threads.

From: Damien Levac <damien.levac_at_gmail.com>
Date: Sun, 13 Apr 2014 18:27:36 -0400

Hi all,

I have this code below that runs fine in single threaded application but
fails as soon as multiple threads are in play. Any idea what I am doing
incorrectly?

(Not sure if C++ is off-topic?? :s)

Thanks in advance!

 1.
    /**
 2.
      * @file curl.hpp
 3.
      * @author Damien Levac
 4.
      * @date 2014-04-07
 5.
      */
 6.
 7.
    #ifndef CURL_
 8.
    # define CURL_
 9.
    # include <mutex>
10.
    # include <vector>
11.
    # include <string>
12.
    # include <thread>
13.
    # include <cassert>
14.
    # include <cstddef>
15.
    # include <curl/curl.h>
16.
    # include <openssl/err.h>
17.
18.
    /**
19.
      * @brief OpenSSL multithreading callback functions and utilities.
20.
      */
21.
    namespace openssl {
22.
    /**
23.
    * @brief Vector holding the mutexes to be used by the callback
24.
    * locking function.
25.
    */
26.
    static std::vector<std::mutex *> mutexes_;
27.
28.
    /**
29.
    * @brief Used to lock and unlock the mutexes.
30.
    *
31.
    * @param[in] mode Used to determine whether the mutex needs to be
32.
    * locked or unlocked.
33.
    * @param[in] n The mutex number to be either locked or unlocked.
34.
    * @param[in] file The filename from which this function is called.
35.
    * @param[in] line The line number from the file from which this
36.
    * function is called.
37.
    */
38.
    # pragma GCC diagnostic ignored "-Wunused-parameter"
39.
    static void
40.
    lock_(int mode, int n, char const * file, int line)
41.
    {
42.
    if (mode & CRYPTO_LOCK) {
43.
    mutexes_[n]->lock();
44.
    } else {
45.
    mutexes_[n]->unlock();
46.
    }
47.
    }
48.
49.
    /**
50.
    * @brief Used to recover a unique thread id.
51.
    *
52.
    * @return The id of thread.
53.
    */
54.
    static unsigned long
55.
    thread_id_(void)
56.
    {
57.
    // TODO The hash is not guaranteed to be unique!
58.
    return std::hash<std::thread::id>()(std::this_thread::get_id());
59.
    }
60.
61.
    /**
62.
    * @brief Called inside a new thread to setup openssl for safe
63.
    * multithreading operation within that thread.
64.
    */
65.
    static void
66.
    thread_setup_(void)
67.
    {
68.
    assert(CRYPTO_num_locks() >= 0);
69.
    for (int i = 0; i < CRYPTO_num_locks(); ++i) {
70.
    mutexes_.push_back(new std::mutex);
71.
    }
72.
73.
    CRYPTO_set_id_callback(thread_id_);
74.
    CRYPTO_set_locking_callback(lock_);
75.
    }
76.
77.
    /**
78.
    * @brief Called inside a thread who is not going to use openssl in a
79.
    * multithreading fashion anymore. Usually used just before the thread
80.
    * is joined.
81.
    */
82.
    static void
83.
    thread_cleanup_(void)
84.
    {
85.
    CRYPTO_set_id_callback(NULL);
86.
    CRYPTO_set_locking_callback(NULL);
87.
88.
    for (std::mutex *m: mutexes_) {
89.
    delete m;
90.
    }
91.
    }
92.
    }
93.
94.
    /**
95.
      * @brief A curl object to download web pages.
96.
      */
97.
    class curl {
98.
    public:
99.
    explicit curl(void);
100.
    ~curl(void);
101.
102.
    bool load(std::string &output, std::string const &url);
103.
104.
    private:
105.
    CURL *curl_;
106.
107.
    int writer_(char *data, std::size_t size, std::size_t nmemb,
108.
    std::string *s);
109.
    };
110.
111.
    /**
112.
      * @brief Setup curl and openssl appropriately.
113.
      */
114.
    inline
115.
    curl::curl(void)
116.
    {
117.
    openssl::thread_setup_();
118.
119.
    curl_ = curl_easy_init();
120.
    if (curl_) {
121.
    curl_easy_setopt(curl_, CURLOPT_FOLLOWLOCATION, false);
122.
    curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, &curl::writer_);
123.
    curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, true);
124.
    }
125.
    }
126.
127.
    /**
128.
      * @brief Cleanup curl and openssl.
129.
      */
130.
    inline
131.
    curl::~curl(void)
132.
    {
133.
    if (curl_) {
134.
    curl_easy_cleanup(curl_);
135.
    }
136.
137.
    openssl::thread_cleanup_();
138.
    }
139.
140.
    /**
141.
      * @brief Download a webpage from a given url.
142.
      *
143.
      * @param[out] output The webpage as a std::string.
144.
      * @param[in] url The url to the webpage to be downloaded.
145.
      *
146.
      * @return True if the download of a webpage (not necesarily the one we
147.
      * are looking for, watch out for redirection) succeeded or false
148.
      * otherwise.
149.
      */
150.
    inline bool
151.
    curl::load(std::string &output, std::string const &url)
152.
    {
153.
    CURLcode code = CURLE_FAILED_INIT;
154.
155.
    if (curl_) {
156.
    curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
157.
    curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &output);
158.
    code = curl_easy_perform(curl_);
159.
    }
160.
161.
    return code == CURLE_OK;
162.
    }
163.
164.
    /**
165.
      * @brief Helper needed to write the data downloaded online.
166.
      *
167.
      * @param data A pointer to the data to be copied into the output
    string.
168.
      * @param size The size in byte of a character.
169.
      * @param nmemb The number of character to copy.
170.
      * @param s The output string.
171.
      * @return The number of bytes written to the output string.
172.
      */
173.
    inline int
174.
    curl::writer_(char *data, std::size_t size, std::size_t nmemb,
    std::string *s)
175.
    {
176.
    if (s != NULL) {
177.
    s->append(data, size * nmemb);
178.
    return size * nmemb;
179.
    }
180.
181.
    return 0;
182.
    }
183.
184.
    #endif
185.

-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette: http://curl.haxx.se/mail/etiquette.html
Received on 2014-04-14