curl-library
C++ header used for web page downloading from multiple threads.
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