-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcurl_share.hpp
254 lines (229 loc) · 7.94 KB
/
curl_share.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#ifndef __curl_cpp_curl_share_HPP__
# define __curl_cpp_curl_share_HPP__
# include "curl.hpp"
# include "utils/shared_mutex.hpp"
# include "return-exception/ret-exception.hpp"
# include <curl/curl.h>
# include <memory>
namespace curl {
/**
* @example curl_share.cc
*
* All easy handler must be removed before Share_base
* can be destroyed.
*
* To use in a thread-safe manner, you must call
* add_lock with non-nullptr.
*/
class Share_base {
protected:
curl_t::Share_t curl_share;
public:
/**
* @param share must be != nullptr
*
* After this ctor call, share.get() == nullptr,
* this class will take over the ownership.
*/
Share_base(curl_t::Share_t &&share) noexcept;
/**
* Since libcurl doesn't provide a dup
* function for curl_share, neither will Share_base.
*/
Share_base(const Share_base&) = delete;
/**
* @param other bool(other) can be false, but then this new instance
* of Share_base will be unusable.
*/
Share_base(Share_base &&other) noexcept;
/**
* Since libcurl doesn't provide a dup
* function for curl_share, neither will Share_base.
*/
Share_base& operator = (const Share_base&) = delete;
/**
* @param other if bool(other) is true, then it will be unusable after this operation.
* <br>Otherwise, this object will also be destroyed.
* @param this this object can also be an unusable object which bool(*this) is false.
* <br>Calling this function on an unusable object will make that object again
* usable.
*
* **NOTE that if this object still holds easy handler and bool(other) is false,
* then it is undefined behavior.**
*
*/
Share_base& operator = (Share_base &&other) noexcept;
/**
* @return true if this object is usable, false otherwise.
*/
operator bool () const noexcept;
/**
* Defines data to share among Easy_t handles.
*/
enum class Options {
none = CURL_LOCK_DATA_NONE,
cookie = CURL_LOCK_DATA_COOKIE,
/**
* share cached DNS hosts.
*
* If the libcurl has cookies disabled, then
* enable_sharing(Options::cookie) will return 0.
*
* NOTE that Multi_t interface share this by default
* without setting this option.
*/
dns = CURL_LOCK_DATA_DNS,
/**
* SSL session IDs will be shared across the easy handles
* using this shared object.
*
* This will reduce the time spent in the SSL handshake
* when reconnecting to the same server.
*
* If curl_t::has_ssl_session_sharing_support() == false, setting
* this option shares nothing.
*
* NOTE SSL session IDs are reused within the same easy handle by default.
*/
ssl_session = CURL_LOCK_DATA_SSL_SESSION,
/**
* If curl_t::has_connection_cache_sharing_support() == false, setting
* this option shares nothing.
*
* NOTE that Multi_t interface share this by default
* without setting this option.
*/
connection_cache = CURL_LOCK_DATA_CONNECT,
/**
* Share Public Suffix List.
*
* If curl_t::has_psl_sharing_support() == false, setting
* this option shares nothing.
*
* NOTE that Multi_t interface share this by default
* without setting this option.
*/
psl = CURL_LOCK_DATA_PSL,
};
/**
* Possible value of data: same as enum class Options
*
* Possible value of access:
* - CURL_LOCK_ACCESS_SHARED,
* - CURL_LOCK_ACCESS_SINGLE,
* The mutex you used must be pthread_rwlock_t or std::shared_mutex.
*/
using lock_function_t = void (*)(CURL *handle, curl_lock_data data, curl_lock_access access, void *userptr);
/**
* Possible value of data: same as enum class Options
*
* Possible value of access:
* - CURL_LOCK_ACCESS_SHARED,
* - CURL_LOCK_ACCESS_SINGLE,
* The mutex you used must be pthread_rwlock_t or std::shared_mutex.
*/
using unlock_function_t = void (*)(CURL *handle, curl_lock_data data, void *userptr);
/**
* Setting lock_func and unlock_func to nullptr disable locking.
*/
void add_lock(lock_function_t lock_func, unlock_function_t unlock_func, void *userptr) noexcept;
/**
* @param option must be one of enum class Options.
* <br>Cannot be or-ed value.
*
* All sharing enable/disable must be done when no easy is added
* or all easy is removed.
*/
auto enable_sharing(Options option) noexcept -> Ret_except<int, std::bad_alloc>;
/**
* @param option must be one of enum class Options.
* <br>Cannot be or-ed value.
*
* All sharing enable/disable must be done when no easy is added
* or all easy is removed.
*/
void disable_sharing(Options option) noexcept;
void add_easy(Easy_ref_t &easy) noexcept;
void remove_easy(Easy_ref_t &easy) noexcept;
};
/**
* @tparam Shared_mutex_t For shared or single lock, their unlock function must be
* the same function -- Shared_mutex_t::unlock().
* <br>If Shared_mutex_t lock, lock_shared or unlock
* throw an exception, it would terminte the program.
* <br>If Shared_mutex_t has type Ret_except_t (Ret == void), then
* Shared_mutex_t(Ret_except_t&) would be called.
* <br>Pass void to disable locking, which make
* multithreaded use unsafe.
*/
template <class Shared_mutex_t = utils::shared_mutex>
class Share: public Share_base {
static constexpr const std::size_t mutex_num = 5;
Shared_mutex_t mutexes[mutex_num];
auto get_mutex(Options option) noexcept -> Shared_mutex_t*
{
switch (option) {
case Options::none:
return nullptr;
case Options::cookie:
return &mutexes[0];
case Options::dns:
return &mutexes[1];
case Options::ssl_session:
return &mutexes[2];
case Options::connection_cache:
return &mutexes[3];
case Options::psl:
return &mutexes[4];
default:
return nullptr;
}
}
public:
using Share_base::Share_base;
/**
* @param base any usable Share_base object.
*/
Share(Share_base &&base) noexcept:
Share_base{std::move(base)}
{}
void enable_multithreaded_share() noexcept
{
add_lock([](CURL *handle, curl_lock_data data, curl_lock_access access, void *userptr) noexcept {
auto &share = *static_cast<Share*>(userptr);
auto *mutex = share.get_mutex(static_cast<Share_base::Options>(data));
if (!mutex)
return;
if (access == CURL_LOCK_ACCESS_SHARED)
mutex->lock_shared();
else
mutex->lock();
}, [](CURL *handle, curl_lock_data data, void *userptr) noexcept {
auto &share = *static_cast<Share*>(userptr);
auto *mutex = share.get_mutex(static_cast<Share_base::Options>(data));
if (mutex)
mutex->unlock();
}, this);
}
void disable_multithreaded_share() noexcept
{
add_lock(nullptr, nullptr, nullptr);
}
};
/**
* Sepecialiation of Share that does no locking, thus
* is multithread unsafe.
*
* This provides the same interface as any other Share<T>,
* so that your template can simply pass void to
* disable locking.
*/
template <>
class Share<void>: public Share_base {
public:
using Share_base::Share_base;
constexpr void enable_multithreaded_share() const noexcept {}
constexpr void disable_multithreaded_share() const noexcept {}
};
} /* namespace curl */
#endif