curb 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +33 -0
- data/Rakefile +22 -0
- data/ext/curb.c +282 -231
- data/ext/curb.h +4 -4
- data/ext/curb_easy.c +676 -228
- data/ext/curb_easy.h +6 -0
- data/ext/curb_errors.c +5 -5
- data/ext/curb_errors.h +1 -1
- data/ext/curb_macros.h +14 -14
- data/ext/curb_multi.c +639 -132
- data/ext/curb_multi.h +3 -1
- data/ext/curb_postfield.c +47 -21
- data/ext/curb_postfield.h +1 -0
- data/ext/curb_upload.c +32 -9
- data/ext/curb_upload.h +2 -0
- data/ext/extconf.rb +40 -0
- data/lib/curl/easy.rb +154 -13
- data/lib/curl/multi.rb +69 -9
- data/lib/curl.rb +193 -0
- data/tests/helper.rb +222 -36
- data/tests/leak_trace.rb +237 -0
- data/tests/tc_curl_download.rb +6 -2
- data/tests/tc_curl_easy.rb +450 -1
- data/tests/tc_curl_easy_request_target.rb +41 -0
- data/tests/tc_curl_multi.rb +573 -59
- data/tests/tc_curl_native_coverage.rb +130 -0
- data/tests/tc_curl_postfield.rb +161 -0
- data/tests/tc_fiber_scheduler.rb +342 -7
- data/tests/tc_ftp_options.rb +26 -0
- data/tests/tc_gc_compact.rb +223 -0
- data/tests/tc_test_server_methods.rb +110 -0
- metadata +16 -4
data/ext/curb_multi.c
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
#include "curb_multi.h"
|
|
28
28
|
|
|
29
29
|
#include <errno.h>
|
|
30
|
+
#include <fcntl.h>
|
|
30
31
|
#include <stdarg.h>
|
|
31
32
|
|
|
32
33
|
/*
|
|
@@ -40,6 +41,12 @@
|
|
|
40
41
|
#define curb_debugf(...) ((void)0)
|
|
41
42
|
#endif
|
|
42
43
|
|
|
44
|
+
#ifdef RBIMPL_ATTR_MAYBE_UNUSED
|
|
45
|
+
#define CURB_MAYBE_UNUSED_DECL RBIMPL_ATTR_MAYBE_UNUSED()
|
|
46
|
+
#else
|
|
47
|
+
#define CURB_MAYBE_UNUSED_DECL
|
|
48
|
+
#endif
|
|
49
|
+
|
|
43
50
|
#ifdef _WIN32
|
|
44
51
|
// for O_RDWR and O_BINARY
|
|
45
52
|
#include <fcntl.h>
|
|
@@ -62,6 +69,8 @@ static void *curl_multi_wait_wrapper(void *p) {
|
|
|
62
69
|
|
|
63
70
|
extern VALUE mCurl;
|
|
64
71
|
static VALUE idCall;
|
|
72
|
+
static ID id_deferred_exception_ivar;
|
|
73
|
+
static ID id_deferred_exception_source_id_ivar;
|
|
65
74
|
|
|
66
75
|
#ifdef RDOC_NEVER_DEFINED
|
|
67
76
|
mCurl = rb_define_module("Curl");
|
|
@@ -72,6 +81,7 @@ VALUE cCurlMulti;
|
|
|
72
81
|
static long cCurlMutiDefaulttimeout = 100; /* milliseconds */
|
|
73
82
|
static char cCurlMutiAutoClose = 0;
|
|
74
83
|
|
|
84
|
+
static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int result);
|
|
75
85
|
static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy);
|
|
76
86
|
static void rb_curl_multi_read_info(VALUE self, CURLM *mptr);
|
|
77
87
|
static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running);
|
|
@@ -79,6 +89,12 @@ static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_runnin
|
|
|
79
89
|
static int detach_easy_entry(st_data_t key, st_data_t val, st_data_t arg);
|
|
80
90
|
static void rb_curl_multi_detach_all(ruby_curl_multi *rbcm);
|
|
81
91
|
static void curl_multi_mark(void *ptr);
|
|
92
|
+
static void ruby_curl_multi_ensure_handle(ruby_curl_multi *rbcm);
|
|
93
|
+
static int rb_curl_multi_has_easy(ruby_curl_multi *rbcm, ruby_curl_easy *rbce);
|
|
94
|
+
static void rb_curl_multi_remove_request_reference(VALUE self, VALUE easy);
|
|
95
|
+
static VALUE ruby_curl_multi_mark_closed(VALUE self);
|
|
96
|
+
static VALUE ruby_curl_multi_alloc(VALUE klass);
|
|
97
|
+
static VALUE ruby_curl_multi_initialize(VALUE self);
|
|
82
98
|
|
|
83
99
|
static VALUE callback_exception(VALUE did_raise, VALUE exception) {
|
|
84
100
|
// TODO: we could have an option to enable exception reporting
|
|
@@ -101,13 +117,117 @@ static VALUE callback_exception(VALUE did_raise, VALUE exception) {
|
|
|
101
117
|
return exception;
|
|
102
118
|
}
|
|
103
119
|
|
|
120
|
+
static VALUE take_easy_callback_error_if_any(VALUE easy) {
|
|
121
|
+
ruby_curl_easy *rbce = NULL;
|
|
122
|
+
|
|
123
|
+
if (NIL_P(easy) || !RB_TYPE_P(easy, T_DATA)) {
|
|
124
|
+
return Qnil;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
TypedData_Get_Struct(easy, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
128
|
+
return rb_curl_easy_take_callback_error(rbce);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static VALUE build_aborted_by_callback_exception(VALUE exception) {
|
|
132
|
+
VALUE message = rb_funcall(exception, rb_intern("message"), 0);
|
|
133
|
+
VALUE aborted_exception = rb_exc_new_str(eCurlErrAbortedByCallback, message);
|
|
134
|
+
VALUE backtrace = rb_funcall(exception, rb_intern("backtrace"), 0);
|
|
135
|
+
rb_funcall(aborted_exception, rb_intern("set_backtrace"), 1, backtrace);
|
|
136
|
+
return aborted_exception;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
static void stash_multi_exception_if_unset(VALUE self, VALUE exception, VALUE source_easy) {
|
|
140
|
+
if (rb_ivar_defined(self, id_deferred_exception_ivar)) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
rb_ivar_set(self, id_deferred_exception_ivar, exception);
|
|
145
|
+
if (!NIL_P(source_easy)) {
|
|
146
|
+
rb_ivar_set(self, id_deferred_exception_source_id_ivar, rb_obj_id(source_easy));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static void clear_multi_deferred_exception_source_id_if_any(VALUE self) {
|
|
151
|
+
if (rb_ivar_defined(self, id_deferred_exception_source_id_ivar)) {
|
|
152
|
+
rb_funcall(self, rb_intern("remove_instance_variable"), 1, ID2SYM(id_deferred_exception_source_id_ivar));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
static VALUE clear_multi_deferred_exception_if_any(VALUE self) {
|
|
157
|
+
VALUE exception = Qnil;
|
|
158
|
+
|
|
159
|
+
if (rb_ivar_defined(self, id_deferred_exception_ivar)) {
|
|
160
|
+
exception = rb_funcall(self, rb_intern("remove_instance_variable"), 1, ID2SYM(id_deferred_exception_ivar));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return exception;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
static void rb_curl_multi_yield_if_given(VALUE self, VALUE block) {
|
|
167
|
+
if (block == Qnil) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
rb_funcall(block, idCall, 1, self);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
static void raise_multi_deferred_exception_if_idle(VALUE self) {
|
|
175
|
+
ruby_curl_multi *rbcm = NULL;
|
|
176
|
+
|
|
177
|
+
if (!rb_ivar_defined(self, id_deferred_exception_ivar)) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
182
|
+
if (rbcm && rbcm->active > 0) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
VALUE exception = clear_multi_deferred_exception_if_any(self);
|
|
187
|
+
rb_exc_raise(exception);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
static VALUE take_status_callback_exception_if_any(VALUE did_raise) {
|
|
191
|
+
VALUE exception = rb_hash_aref(did_raise, rb_easy_hkey("error"));
|
|
192
|
+
|
|
193
|
+
if (FIX2INT(rb_hash_size(did_raise)) > 0 && exception != Qnil) {
|
|
194
|
+
rb_hash_clear(did_raise);
|
|
195
|
+
return build_aborted_by_callback_exception(exception);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return Qnil;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
static void raise_completion_callback_error_if_any(VALUE did_raise, VALUE easy_callback_error) {
|
|
202
|
+
if (!NIL_P(easy_callback_error)) {
|
|
203
|
+
rb_exc_raise(easy_callback_error);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
VALUE exception = take_status_callback_exception_if_any(did_raise);
|
|
207
|
+
if (!NIL_P(exception)) {
|
|
208
|
+
rb_exc_raise(exception);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
struct multi_handle_complete_args {
|
|
213
|
+
VALUE self;
|
|
214
|
+
CURL *easy_handle;
|
|
215
|
+
int result;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
static VALUE rb_curl_mutli_handle_complete_protected(VALUE argp) {
|
|
219
|
+
struct multi_handle_complete_args *args = (struct multi_handle_complete_args *)argp;
|
|
220
|
+
rb_curl_mutli_handle_complete(args->self, args->easy_handle, args->result);
|
|
221
|
+
return Qnil;
|
|
222
|
+
}
|
|
223
|
+
|
|
104
224
|
static int detach_easy_entry(st_data_t key, st_data_t val, st_data_t arg) {
|
|
105
225
|
ruby_curl_multi *rbcm = (ruby_curl_multi *)arg;
|
|
106
226
|
VALUE easy = (VALUE)val;
|
|
107
227
|
ruby_curl_easy *rbce = NULL;
|
|
108
228
|
|
|
109
229
|
if (RB_TYPE_P(easy, T_DATA)) {
|
|
110
|
-
|
|
230
|
+
TypedData_Get_Struct(easy, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
111
231
|
}
|
|
112
232
|
|
|
113
233
|
if (!rbce) {
|
|
@@ -150,7 +270,34 @@ static void rb_curl_multi_detach_all(ruby_curl_multi *rbcm) {
|
|
|
150
270
|
rbcm->running = 0;
|
|
151
271
|
}
|
|
152
272
|
|
|
153
|
-
|
|
273
|
+
static int rb_curl_multi_has_easy(ruby_curl_multi *rbcm, ruby_curl_easy *rbce) {
|
|
274
|
+
st_data_t value = 0;
|
|
275
|
+
|
|
276
|
+
if (!rbcm || !rbce || !rbcm->attached) {
|
|
277
|
+
return 0;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return st_lookup(rbcm->attached, (st_data_t)rbce, &value);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
static void rb_curl_multi_remove_request_reference(VALUE self, VALUE easy) {
|
|
284
|
+
VALUE requests;
|
|
285
|
+
|
|
286
|
+
if (NIL_P(self) || NIL_P(easy)) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
requests = rb_funcall(self, rb_intern("requests"), 0);
|
|
291
|
+
if (!RB_TYPE_P(requests, T_HASH)) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
rb_hash_delete(requests, rb_obj_id(easy));
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/* TypedData-compatible free function */
|
|
299
|
+
static void curl_multi_free(void *ptr) {
|
|
300
|
+
ruby_curl_multi *rbcm = (ruby_curl_multi *)ptr;
|
|
154
301
|
if (!rbcm) {
|
|
155
302
|
return;
|
|
156
303
|
}
|
|
@@ -165,6 +312,27 @@ void curl_multi_free(ruby_curl_multi *rbcm) {
|
|
|
165
312
|
free(rbcm);
|
|
166
313
|
}
|
|
167
314
|
|
|
315
|
+
static size_t curl_multi_memsize(const void *ptr) {
|
|
316
|
+
(void)ptr;
|
|
317
|
+
return sizeof(ruby_curl_multi);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const rb_data_type_t ruby_curl_multi_data_type = {
|
|
321
|
+
"Curl::Multi",
|
|
322
|
+
{
|
|
323
|
+
curl_multi_mark,
|
|
324
|
+
curl_multi_free,
|
|
325
|
+
curl_multi_memsize,
|
|
326
|
+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
|
|
327
|
+
NULL, /* compact */
|
|
328
|
+
#endif
|
|
329
|
+
},
|
|
330
|
+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
|
|
331
|
+
NULL, NULL, /* parent, data */
|
|
332
|
+
RUBY_TYPED_FREE_IMMEDIATELY
|
|
333
|
+
#endif
|
|
334
|
+
};
|
|
335
|
+
|
|
168
336
|
static void ruby_curl_multi_init(ruby_curl_multi *rbcm) {
|
|
169
337
|
rbcm->handle = curl_multi_init();
|
|
170
338
|
if (!rbcm->handle) {
|
|
@@ -173,6 +341,7 @@ static void ruby_curl_multi_init(ruby_curl_multi *rbcm) {
|
|
|
173
341
|
|
|
174
342
|
rbcm->active = 0;
|
|
175
343
|
rbcm->running = 0;
|
|
344
|
+
rbcm->closed = 0;
|
|
176
345
|
|
|
177
346
|
if (rbcm->attached) {
|
|
178
347
|
st_free_table(rbcm->attached);
|
|
@@ -187,20 +356,36 @@ static void ruby_curl_multi_init(ruby_curl_multi *rbcm) {
|
|
|
187
356
|
}
|
|
188
357
|
}
|
|
189
358
|
|
|
359
|
+
static void ruby_curl_multi_ensure_handle(ruby_curl_multi *rbcm) {
|
|
360
|
+
if (!rbcm->handle) {
|
|
361
|
+
if (rbcm->closed) {
|
|
362
|
+
raise_curl_multi_error_exception(CURLM_BAD_HANDLE);
|
|
363
|
+
}
|
|
364
|
+
ruby_curl_multi_init(rbcm);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
190
368
|
/*
|
|
191
369
|
* call-seq:
|
|
192
370
|
* Curl::Multi.new => #<Curl::Easy...>
|
|
193
371
|
*
|
|
194
372
|
* Create a new Curl::Multi instance
|
|
195
373
|
*/
|
|
196
|
-
VALUE
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
rb_raise(rb_eNoMemError, "Failed to allocate memory for Curl::Multi");
|
|
200
|
-
}
|
|
374
|
+
static VALUE ruby_curl_multi_alloc(VALUE klass) {
|
|
375
|
+
VALUE self;
|
|
376
|
+
ruby_curl_multi *rbcm;
|
|
201
377
|
|
|
378
|
+
self = TypedData_Make_Struct(klass, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
202
379
|
MEMZERO(rbcm, ruby_curl_multi, 1);
|
|
203
380
|
|
|
381
|
+
return self;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
static VALUE ruby_curl_multi_initialize(VALUE self) {
|
|
385
|
+
ruby_curl_multi *rbcm;
|
|
386
|
+
|
|
387
|
+
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
388
|
+
|
|
204
389
|
ruby_curl_multi_init(rbcm);
|
|
205
390
|
|
|
206
391
|
/*
|
|
@@ -209,7 +394,7 @@ VALUE ruby_curl_multi_new(VALUE klass) {
|
|
|
209
394
|
* identify these objects using rb_gc_mark(value). If the structure doesn't reference
|
|
210
395
|
* other Ruby objects, you can simply pass 0 as a function pointer.
|
|
211
396
|
*/
|
|
212
|
-
return
|
|
397
|
+
return self;
|
|
213
398
|
}
|
|
214
399
|
|
|
215
400
|
/*
|
|
@@ -277,7 +462,8 @@ static VALUE ruby_curl_multi_max_connects(VALUE self, VALUE count) {
|
|
|
277
462
|
#ifdef HAVE_CURLMOPT_MAXCONNECTS
|
|
278
463
|
ruby_curl_multi *rbcm;
|
|
279
464
|
|
|
280
|
-
|
|
465
|
+
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
466
|
+
ruby_curl_multi_ensure_handle(rbcm);
|
|
281
467
|
|
|
282
468
|
curl_multi_setopt(rbcm->handle, CURLMOPT_MAXCONNECTS, NUM2LONG(count));
|
|
283
469
|
#endif
|
|
@@ -296,7 +482,8 @@ static VALUE ruby_curl_multi_max_host_connections(VALUE self, VALUE count) {
|
|
|
296
482
|
#ifdef HAVE_CURLMOPT_MAX_HOST_CONNECTIONS
|
|
297
483
|
ruby_curl_multi *rbcm;
|
|
298
484
|
|
|
299
|
-
|
|
485
|
+
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
486
|
+
ruby_curl_multi_ensure_handle(rbcm);
|
|
300
487
|
|
|
301
488
|
curl_multi_setopt(rbcm->handle, CURLMOPT_MAX_HOST_CONNECTIONS, NUM2LONG(count));
|
|
302
489
|
#endif
|
|
@@ -330,7 +517,8 @@ static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE method) {
|
|
|
330
517
|
value = NUM2LONG(method);
|
|
331
518
|
}
|
|
332
519
|
|
|
333
|
-
|
|
520
|
+
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
521
|
+
ruby_curl_multi_ensure_handle(rbcm);
|
|
334
522
|
curl_multi_setopt(rbcm->handle, CURLMOPT_PIPELINING, value);
|
|
335
523
|
#endif
|
|
336
524
|
return method == Qtrue ? 1 : 0;
|
|
@@ -350,8 +538,9 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
|
350
538
|
ruby_curl_easy *rbce;
|
|
351
539
|
ruby_curl_multi *rbcm;
|
|
352
540
|
|
|
353
|
-
|
|
354
|
-
|
|
541
|
+
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
542
|
+
TypedData_Get_Struct(easy, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
543
|
+
ruby_curl_multi_ensure_handle(rbcm);
|
|
355
544
|
|
|
356
545
|
/* setup the easy handle */
|
|
357
546
|
ruby_curl_easy_setup( rbce );
|
|
@@ -378,6 +567,7 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
|
378
567
|
}
|
|
379
568
|
}
|
|
380
569
|
|
|
570
|
+
rbce->multi_attachment_generation++;
|
|
381
571
|
st_insert(rbcm->attached, (st_data_t)rbce, (st_data_t)easy);
|
|
382
572
|
|
|
383
573
|
/* track a reference to associated multi handle */
|
|
@@ -403,7 +593,7 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
|
403
593
|
VALUE ruby_curl_multi_remove(VALUE self, VALUE rb_easy_handle) {
|
|
404
594
|
ruby_curl_multi *rbcm;
|
|
405
595
|
|
|
406
|
-
|
|
596
|
+
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
407
597
|
|
|
408
598
|
rb_curl_multi_remove(rbcm, rb_easy_handle);
|
|
409
599
|
|
|
@@ -414,7 +604,7 @@ static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
|
|
414
604
|
CURLMcode result;
|
|
415
605
|
ruby_curl_easy *rbce;
|
|
416
606
|
|
|
417
|
-
|
|
607
|
+
TypedData_Get_Struct(easy, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
418
608
|
result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
|
|
419
609
|
if (result != 0) {
|
|
420
610
|
raise_curl_multi_error_exception(result);
|
|
@@ -424,6 +614,7 @@ static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
|
|
424
614
|
rbcm->active--;
|
|
425
615
|
}
|
|
426
616
|
|
|
617
|
+
rbce->multi = Qnil;
|
|
427
618
|
ruby_curl_easy_cleanup( easy, rbce );
|
|
428
619
|
|
|
429
620
|
rb_curl_multi_forget_easy(rbcm, rbce);
|
|
@@ -454,101 +645,311 @@ static void flush_stderr_if_any(ruby_curl_easy *rbce) {
|
|
|
454
645
|
}
|
|
455
646
|
}
|
|
456
647
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
648
|
+
/* Helper to locate the Ruby Easy VALUE from the attached table using the
|
|
649
|
+
* underlying CURL* handle when CURLINFO_PRIVATE is unavailable or stale. */
|
|
650
|
+
struct find_easy_ctx { CURL *handle; VALUE easy; };
|
|
651
|
+
static int find_easy_by_handle_i(st_data_t key, st_data_t val, st_data_t arg) {
|
|
652
|
+
ruby_curl_easy *rbce = (ruby_curl_easy *)key;
|
|
653
|
+
struct find_easy_ctx *ctx = (struct find_easy_ctx *)arg;
|
|
654
|
+
if (rbce && rbce->curl == ctx->handle) {
|
|
655
|
+
ctx->easy = (VALUE)val;
|
|
656
|
+
return ST_STOP;
|
|
657
|
+
}
|
|
658
|
+
return ST_CONTINUE;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
static VALUE find_easy_by_handle(ruby_curl_multi *rbcm, CURL *easy_handle) {
|
|
662
|
+
if (!rbcm || !rbcm->attached) return Qnil;
|
|
663
|
+
struct find_easy_ctx ctx; ctx.handle = easy_handle; ctx.easy = Qnil;
|
|
664
|
+
st_foreach(rbcm->attached, find_easy_by_handle_i, (st_data_t)&ctx);
|
|
665
|
+
return ctx.easy;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
static VALUE find_easy_value_for_handle(ruby_curl_multi *rbcm, CURL *easy_handle) {
|
|
669
|
+
VALUE easy = Qnil;
|
|
460
670
|
ruby_curl_easy *rbce = NULL;
|
|
461
|
-
VALUE callargs;
|
|
462
671
|
|
|
463
|
-
CURLcode
|
|
672
|
+
CURLcode private_rc = curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, (char **)&rbce);
|
|
673
|
+
if (private_rc == CURLE_OK && rbce) {
|
|
674
|
+
easy = rbce->self;
|
|
675
|
+
}
|
|
464
676
|
|
|
465
|
-
|
|
677
|
+
if (NIL_P(easy) || !RB_TYPE_P(easy, T_DATA)) {
|
|
678
|
+
easy = find_easy_by_handle(rbcm, easy_handle);
|
|
679
|
+
}
|
|
466
680
|
|
|
467
|
-
|
|
681
|
+
return easy;
|
|
682
|
+
}
|
|
468
683
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
684
|
+
struct multi_complete_callback_args {
|
|
685
|
+
VALUE self;
|
|
686
|
+
VALUE easy;
|
|
687
|
+
ruby_curl_multi *rbcm;
|
|
688
|
+
ruby_curl_easy *rbce;
|
|
689
|
+
int result;
|
|
690
|
+
st_table *attached_snapshot;
|
|
691
|
+
};
|
|
472
692
|
|
|
473
|
-
|
|
474
|
-
|
|
693
|
+
static int snapshot_attached_easy_i(st_data_t key, st_data_t val, st_data_t arg) {
|
|
694
|
+
st_table *snapshot = (st_table *)arg;
|
|
695
|
+
ruby_curl_easy *rbce = (ruby_curl_easy *)key;
|
|
475
696
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
curl_slist_free_all(rbce->curl_headers);
|
|
479
|
-
rbce->curl_headers = NULL;
|
|
697
|
+
if (!rbce) {
|
|
698
|
+
return ST_CONTINUE;
|
|
480
699
|
}
|
|
481
700
|
|
|
482
|
-
|
|
483
|
-
|
|
701
|
+
st_insert(snapshot, key, (st_data_t)rbce->multi_attachment_generation);
|
|
702
|
+
return ST_CONTINUE;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
static st_table *capture_attached_easy_snapshot(ruby_curl_multi *rbcm) {
|
|
706
|
+
st_table *snapshot = st_init_numtable();
|
|
707
|
+
|
|
708
|
+
if (!snapshot) {
|
|
709
|
+
rb_raise(rb_eNoMemError, "Failed to allocate multi callback snapshot table");
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (rbcm && rbcm->attached) {
|
|
713
|
+
st_foreach(rbcm->attached, snapshot_attached_easy_i, (st_data_t)snapshot);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
return snapshot;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
struct collect_new_attached_easies_ctx {
|
|
720
|
+
st_table *snapshot;
|
|
721
|
+
VALUE easies;
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
static int collect_new_attached_easies_i(st_data_t key, st_data_t val, st_data_t arg) {
|
|
725
|
+
st_data_t existing = 0;
|
|
726
|
+
ruby_curl_easy *rbce = (ruby_curl_easy *)key;
|
|
727
|
+
struct collect_new_attached_easies_ctx *ctx = (struct collect_new_attached_easies_ctx *)arg;
|
|
728
|
+
|
|
729
|
+
if (!rbce) {
|
|
730
|
+
return ST_CONTINUE;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
if (!ctx->snapshot || !st_lookup(ctx->snapshot, key, &existing) ||
|
|
734
|
+
existing != (st_data_t)rbce->multi_attachment_generation) {
|
|
735
|
+
rb_ary_push(ctx->easies, (VALUE)val);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
return ST_CONTINUE;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
static void rb_curl_multi_remove_added_easies_since_snapshot(VALUE self, ruby_curl_multi *rbcm, st_table *snapshot) {
|
|
742
|
+
struct collect_new_attached_easies_ctx ctx;
|
|
743
|
+
VALUE easies = rb_ary_new();
|
|
744
|
+
long index;
|
|
745
|
+
|
|
746
|
+
if (!rbcm || !snapshot || !rbcm->attached) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
ctx.snapshot = snapshot;
|
|
751
|
+
ctx.easies = easies;
|
|
752
|
+
st_foreach(rbcm->attached, collect_new_attached_easies_i, (st_data_t)&ctx);
|
|
753
|
+
|
|
754
|
+
for (index = 0; index < RARRAY_LEN(easies); index++) {
|
|
755
|
+
VALUE easy = rb_ary_entry(easies, index);
|
|
756
|
+
ruby_curl_easy *rbce = NULL;
|
|
757
|
+
|
|
758
|
+
if (NIL_P(easy) || !RB_TYPE_P(easy, T_DATA)) {
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
TypedData_Get_Struct(easy, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
763
|
+
if (!rbce || rbce->multi != self || !rb_curl_multi_has_easy(rbcm, rbce)) {
|
|
764
|
+
continue;
|
|
765
|
+
}
|
|
484
766
|
|
|
485
|
-
|
|
486
|
-
|
|
767
|
+
rb_curl_multi_remove_request_reference(self, easy);
|
|
768
|
+
rb_curl_multi_remove(rbcm, easy);
|
|
487
769
|
}
|
|
488
770
|
|
|
771
|
+
RB_GC_GUARD(easies);
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
static void stash_and_raise_status_callback_error_if_unmasked(struct multi_complete_callback_args *args, VALUE did_raise, VALUE easy_callback_error) {
|
|
775
|
+
VALUE exception;
|
|
776
|
+
|
|
777
|
+
if (!NIL_P(easy_callback_error)) {
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
exception = take_status_callback_exception_if_any(did_raise);
|
|
782
|
+
if (NIL_P(exception)) {
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
stash_multi_exception_if_unset(args->self, exception, args->easy);
|
|
787
|
+
rb_curl_multi_remove_added_easies_since_snapshot(args->self, args->rbcm, args->attached_snapshot);
|
|
788
|
+
rb_exc_raise(exception);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
static VALUE rb_curl_multi_run_completion_callbacks(VALUE argp) {
|
|
792
|
+
struct multi_complete_callback_args *args = (struct multi_complete_callback_args *)argp;
|
|
793
|
+
ruby_curl_easy *rbce = args->rbce;
|
|
794
|
+
long response_code = -1;
|
|
795
|
+
VALUE easy_callback_error = Qnil;
|
|
796
|
+
VALUE callargs;
|
|
489
797
|
VALUE did_raise = rb_hash_new();
|
|
798
|
+
long redirect_count;
|
|
799
|
+
|
|
800
|
+
easy_callback_error = take_easy_callback_error_if_any(args->easy);
|
|
801
|
+
if (!NIL_P(easy_callback_error)) {
|
|
802
|
+
stash_multi_exception_if_unset(args->self, easy_callback_error, args->easy);
|
|
803
|
+
}
|
|
490
804
|
|
|
491
805
|
if (!rb_easy_nil("complete_proc")) {
|
|
492
|
-
callargs = rb_ary_new3(2, rb_easy_get("complete_proc"), easy);
|
|
493
|
-
rbce->callback_active = 1;
|
|
806
|
+
callargs = rb_ary_new3(2, rb_easy_get("complete_proc"), args->easy);
|
|
807
|
+
args->rbce->callback_active = 1;
|
|
494
808
|
rb_rescue(call_status_handler1, callargs, callback_exception, did_raise);
|
|
495
|
-
rbce->callback_active = 0;
|
|
496
|
-
|
|
809
|
+
args->rbce->callback_active = 0;
|
|
810
|
+
stash_and_raise_status_callback_error_if_unmasked(args, did_raise, easy_callback_error);
|
|
497
811
|
}
|
|
498
812
|
|
|
499
813
|
#ifdef HAVE_CURLINFO_RESPONSE_CODE
|
|
500
|
-
curl_easy_getinfo(rbce->curl, CURLINFO_RESPONSE_CODE, &response_code);
|
|
814
|
+
curl_easy_getinfo(args->rbce->curl, CURLINFO_RESPONSE_CODE, &response_code);
|
|
501
815
|
#else /* use fdsets path for waiting */
|
|
502
816
|
// old libcurl
|
|
503
|
-
curl_easy_getinfo(rbce->curl, CURLINFO_HTTP_CODE, &response_code);
|
|
817
|
+
curl_easy_getinfo(args->rbce->curl, CURLINFO_HTTP_CODE, &response_code);
|
|
504
818
|
#endif
|
|
505
|
-
|
|
506
|
-
curl_easy_getinfo(rbce->curl, CURLINFO_REDIRECT_COUNT, &redirect_count);
|
|
819
|
+
curl_easy_getinfo(args->rbce->curl, CURLINFO_REDIRECT_COUNT, &redirect_count);
|
|
507
820
|
|
|
508
|
-
if (result != 0) {
|
|
821
|
+
if (args->result != 0) {
|
|
509
822
|
if (!rb_easy_nil("failure_proc")) {
|
|
510
|
-
callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
|
|
511
|
-
rbce->callback_active = 1;
|
|
823
|
+
callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), args->easy, rb_curl_easy_error(args->result));
|
|
824
|
+
args->rbce->callback_active = 1;
|
|
512
825
|
rb_rescue(call_status_handler2, callargs, callback_exception, did_raise);
|
|
513
|
-
rbce->callback_active = 0;
|
|
514
|
-
|
|
826
|
+
args->rbce->callback_active = 0;
|
|
827
|
+
stash_and_raise_status_callback_error_if_unmasked(args, did_raise, easy_callback_error);
|
|
515
828
|
}
|
|
516
829
|
} else if (!rb_easy_nil("success_proc") &&
|
|
517
830
|
((response_code >= 200 && response_code < 300) || response_code == 0)) {
|
|
518
831
|
/* NOTE: we allow response_code == 0, in the case of non http requests e.g. reading from disk */
|
|
519
|
-
callargs = rb_ary_new3(2, rb_easy_get("success_proc"), easy);
|
|
520
|
-
rbce->callback_active = 1;
|
|
832
|
+
callargs = rb_ary_new3(2, rb_easy_get("success_proc"), args->easy);
|
|
833
|
+
args->rbce->callback_active = 1;
|
|
521
834
|
rb_rescue(call_status_handler1, callargs, callback_exception, did_raise);
|
|
522
|
-
rbce->callback_active = 0;
|
|
523
|
-
|
|
835
|
+
args->rbce->callback_active = 0;
|
|
836
|
+
stash_and_raise_status_callback_error_if_unmasked(args, did_raise, easy_callback_error);
|
|
524
837
|
|
|
525
838
|
} else if (!rb_easy_nil("redirect_proc") && ((response_code >= 300 && response_code < 400) || redirect_count > 0) ) {
|
|
526
839
|
/* Skip on_redirect callback if follow_location is false AND max_redirects is 0 */
|
|
527
|
-
if (!rbce->follow_location && rbce->max_redirs == 0) {
|
|
840
|
+
if (!args->rbce->follow_location && args->rbce->max_redirs == 0) {
|
|
528
841
|
// Do nothing - skip the callback
|
|
529
842
|
} else {
|
|
530
|
-
rbce->callback_active = 1;
|
|
531
|
-
callargs = rb_ary_new3(3, rb_easy_get("redirect_proc"), easy, rb_curl_easy_error(result));
|
|
532
|
-
rbce->callback_active = 0;
|
|
843
|
+
args->rbce->callback_active = 1;
|
|
844
|
+
callargs = rb_ary_new3(3, rb_easy_get("redirect_proc"), args->easy, rb_curl_easy_error(args->result));
|
|
533
845
|
rb_rescue(call_status_handler2, callargs, callback_exception, did_raise);
|
|
534
|
-
|
|
846
|
+
args->rbce->callback_active = 0;
|
|
847
|
+
stash_and_raise_status_callback_error_if_unmasked(args, did_raise, easy_callback_error);
|
|
535
848
|
}
|
|
536
849
|
} else if (!rb_easy_nil("missing_proc") &&
|
|
537
850
|
(response_code >= 400 && response_code < 500)) {
|
|
538
|
-
rbce->callback_active = 1;
|
|
539
|
-
callargs = rb_ary_new3(3, rb_easy_get("missing_proc"), easy, rb_curl_easy_error(result));
|
|
540
|
-
rbce->callback_active = 0;
|
|
851
|
+
args->rbce->callback_active = 1;
|
|
852
|
+
callargs = rb_ary_new3(3, rb_easy_get("missing_proc"), args->easy, rb_curl_easy_error(args->result));
|
|
541
853
|
rb_rescue(call_status_handler2, callargs, callback_exception, did_raise);
|
|
542
|
-
|
|
854
|
+
args->rbce->callback_active = 0;
|
|
855
|
+
stash_and_raise_status_callback_error_if_unmasked(args, did_raise, easy_callback_error);
|
|
543
856
|
} else if (!rb_easy_nil("failure_proc") &&
|
|
544
857
|
(response_code >= 500 && response_code <= 999)) {
|
|
545
|
-
callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
|
|
546
|
-
rbce->callback_active = 1;
|
|
858
|
+
callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), args->easy, rb_curl_easy_error(args->result));
|
|
859
|
+
args->rbce->callback_active = 1;
|
|
547
860
|
rb_rescue(call_status_handler2, callargs, callback_exception, did_raise);
|
|
548
|
-
rbce->callback_active = 0;
|
|
549
|
-
|
|
861
|
+
args->rbce->callback_active = 0;
|
|
862
|
+
stash_and_raise_status_callback_error_if_unmasked(args, did_raise, easy_callback_error);
|
|
550
863
|
}
|
|
551
864
|
|
|
865
|
+
raise_completion_callback_error_if_any(did_raise, easy_callback_error);
|
|
866
|
+
return Qnil;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
static VALUE rb_curl_multi_finish_completion_callbacks(VALUE argp) {
|
|
870
|
+
struct multi_complete_callback_args *args = (struct multi_complete_callback_args *)argp;
|
|
871
|
+
|
|
872
|
+
if (!args->rbce) {
|
|
873
|
+
return Qnil;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
if (args->rbce->callback_active) {
|
|
877
|
+
args->rbce->callback_active = 0;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
if (args->rbce->multi == args->self && !rb_curl_multi_has_easy(args->rbcm, args->rbce)) {
|
|
881
|
+
args->rbce->multi = Qnil;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
if (args->attached_snapshot) {
|
|
885
|
+
st_free_table(args->attached_snapshot);
|
|
886
|
+
args->attached_snapshot = NULL;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
return Qnil;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int result) {
|
|
893
|
+
VALUE easy = Qnil;
|
|
894
|
+
ruby_curl_easy *rbce = NULL;
|
|
895
|
+
ruby_curl_multi *rbcm = NULL;
|
|
896
|
+
CURLMcode mcode;
|
|
897
|
+
|
|
898
|
+
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
899
|
+
|
|
900
|
+
easy = find_easy_value_for_handle(rbcm, easy_handle);
|
|
901
|
+
if (!NIL_P(easy) && RB_TYPE_P(easy, T_DATA)) {
|
|
902
|
+
TypedData_Get_Struct(easy, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/* If we still cannot identify the easy handle, remove it and bail. */
|
|
906
|
+
if (NIL_P(easy) || !RB_TYPE_P(easy, T_DATA) || !rbce) {
|
|
907
|
+
if (rbcm && rbcm->handle && easy_handle) {
|
|
908
|
+
curl_multi_remove_handle(rbcm->handle, easy_handle);
|
|
909
|
+
}
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
rbce->last_result = result; /* save the last easy result code */
|
|
914
|
+
|
|
915
|
+
/* Ensure any verbose output redirected via CURLOPT_STDERR is flushed
|
|
916
|
+
* before we tear down handler state. */
|
|
917
|
+
flush_stderr_if_any(rbce);
|
|
918
|
+
|
|
919
|
+
/* Detach the easy from libcurl and the Ruby requests table before running
|
|
920
|
+
* status callbacks, but preserve easy.multi until the callbacks finish. */
|
|
921
|
+
mcode = curl_multi_remove_handle(rbcm->handle, rbce->curl);
|
|
922
|
+
if (mcode != CURLM_OK) {
|
|
923
|
+
raise_curl_multi_error_exception(mcode);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
if (rbcm->active > 0) {
|
|
927
|
+
rbcm->active--;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
rb_curl_multi_remove_request_reference(self, easy);
|
|
931
|
+
rb_curl_multi_forget_easy(rbcm, rbce);
|
|
932
|
+
ruby_curl_easy_cleanup(easy, rbce);
|
|
933
|
+
|
|
934
|
+
/* after running a request cleanup the headers, these are set before each request */
|
|
935
|
+
if (rbce->curl_headers) {
|
|
936
|
+
curl_slist_free_all(rbce->curl_headers);
|
|
937
|
+
rbce->curl_headers = NULL;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
/* Flush again after removal to cover any last buffered data. */
|
|
941
|
+
flush_stderr_if_any(rbce);
|
|
942
|
+
|
|
943
|
+
struct multi_complete_callback_args args = {
|
|
944
|
+
self,
|
|
945
|
+
easy,
|
|
946
|
+
rbcm,
|
|
947
|
+
rbce,
|
|
948
|
+
result,
|
|
949
|
+
capture_attached_easy_snapshot(rbcm)
|
|
950
|
+
};
|
|
951
|
+
rb_ensure(rb_curl_multi_run_completion_callbacks, (VALUE)&args,
|
|
952
|
+
rb_curl_multi_finish_completion_callbacks, (VALUE)&args);
|
|
552
953
|
}
|
|
553
954
|
|
|
554
955
|
static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
|
@@ -557,6 +958,9 @@ static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
|
|
557
958
|
CURLcode c_easy_result;
|
|
558
959
|
CURLMsg *c_multi_result; // for picking up messages with the transfer status
|
|
559
960
|
CURL *c_easy_handle;
|
|
961
|
+
ruby_curl_multi *rbcm = NULL;
|
|
962
|
+
|
|
963
|
+
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
560
964
|
|
|
561
965
|
/* Check for finished easy handles and remove from the multi handle.
|
|
562
966
|
* curl_multi_info_read will query for messages from individual handles.
|
|
@@ -572,8 +976,16 @@ static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
|
|
572
976
|
c_easy_handle = c_multi_result->easy_handle;
|
|
573
977
|
c_easy_result = c_multi_result->data.result; /* return code for transfer */
|
|
574
978
|
|
|
575
|
-
|
|
979
|
+
struct multi_handle_complete_args args = { self, c_easy_handle, c_easy_result };
|
|
980
|
+
int state = 0;
|
|
981
|
+
rb_protect(rb_curl_mutli_handle_complete_protected, (VALUE)&args, &state);
|
|
982
|
+
if (state) {
|
|
983
|
+
stash_multi_exception_if_unset(self, rb_errinfo(), find_easy_value_for_handle(rbcm, c_easy_handle));
|
|
984
|
+
rb_set_errinfo(Qnil);
|
|
985
|
+
}
|
|
576
986
|
}
|
|
987
|
+
|
|
988
|
+
raise_multi_deferred_exception_if_idle(self);
|
|
577
989
|
}
|
|
578
990
|
|
|
579
991
|
/* called within ruby_curl_multi_perform */
|
|
@@ -613,6 +1025,7 @@ static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_runnin
|
|
|
613
1025
|
typedef struct {
|
|
614
1026
|
st_table *sock_map; /* key: int fd, value: int 'what' (CURL_POLL_*) */
|
|
615
1027
|
long timeout_ms; /* last timeout set by libcurl timer callback */
|
|
1028
|
+
VALUE io_cache; /* fd -> IO wrapper for fiber-scheduler waits */
|
|
616
1029
|
} multi_socket_ctx;
|
|
617
1030
|
|
|
618
1031
|
#if CURB_SOCKET_DEBUG
|
|
@@ -650,12 +1063,30 @@ static const char *cselect_flags_str(int flags, char *buf, size_t n) {
|
|
|
650
1063
|
#define cselect_flags_str(...) ""
|
|
651
1064
|
#endif
|
|
652
1065
|
|
|
1066
|
+
#if defined(HAVE_RB_FIBER_SCHEDULER_IO_WAIT) && defined(HAVE_RB_FIBER_SCHEDULER_CURRENT)
|
|
653
1067
|
/* Protected call to rb_fiber_scheduler_io_wait to avoid unwinding into C on TypeError. */
|
|
654
|
-
struct fiber_io_wait_args { VALUE scheduler; VALUE io;
|
|
1068
|
+
struct fiber_io_wait_args { VALUE scheduler; VALUE io; VALUE events; VALUE timeout; };
|
|
655
1069
|
static VALUE fiber_io_wait_protected(VALUE argp) {
|
|
656
1070
|
struct fiber_io_wait_args *a = (struct fiber_io_wait_args *)argp;
|
|
657
1071
|
return rb_fiber_scheduler_io_wait(a->scheduler, a->io, a->events, a->timeout);
|
|
658
1072
|
}
|
|
1073
|
+
#endif
|
|
1074
|
+
|
|
1075
|
+
static void multi_socket_forget_fd(multi_socket_ctx *ctx, int fd) {
|
|
1076
|
+
st_data_t key = (st_data_t)fd;
|
|
1077
|
+
st_data_t rec;
|
|
1078
|
+
|
|
1079
|
+
if (!ctx) return;
|
|
1080
|
+
if (ctx->sock_map) st_delete(ctx->sock_map, &key, &rec);
|
|
1081
|
+
if (!NIL_P(ctx->io_cache)) rb_hash_delete(ctx->io_cache, INT2NUM(fd));
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
static int multi_socket_fd_valid_p(int fd) {
|
|
1085
|
+
if (fd < 0) return 0;
|
|
1086
|
+
|
|
1087
|
+
errno = 0;
|
|
1088
|
+
return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
|
|
1089
|
+
}
|
|
659
1090
|
|
|
660
1091
|
static int multi_socket_cb(CURL *easy, curl_socket_t s, int what, void *userp, void *socketp) {
|
|
661
1092
|
multi_socket_ctx *ctx = (multi_socket_ctx *)userp;
|
|
@@ -663,22 +1094,25 @@ static int multi_socket_cb(CURL *easy, curl_socket_t s, int what, void *userp, v
|
|
|
663
1094
|
int fd = (int)s;
|
|
664
1095
|
|
|
665
1096
|
if (!ctx || !ctx->sock_map) return 0;
|
|
1097
|
+
if (fd < 0) return 0;
|
|
666
1098
|
|
|
667
1099
|
if (what == CURL_POLL_REMOVE) {
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
st_delete(ctx->sock_map, &k, &rec);
|
|
1100
|
+
multi_socket_forget_fd(ctx, fd);
|
|
1101
|
+
#if CURB_SOCKET_DEBUG
|
|
671
1102
|
{
|
|
672
1103
|
char b[16];
|
|
673
1104
|
curb_debugf("[curb.socket] sock_cb fd=%d what=%s (removed)", fd, poll_what_str(what, b, sizeof(b)));
|
|
674
1105
|
}
|
|
1106
|
+
#endif
|
|
675
1107
|
} else {
|
|
676
1108
|
/* store current interest mask for this fd */
|
|
677
1109
|
st_insert(ctx->sock_map, (st_data_t)fd, (st_data_t)what);
|
|
1110
|
+
#if CURB_SOCKET_DEBUG
|
|
678
1111
|
{
|
|
679
1112
|
char b[16];
|
|
680
1113
|
curb_debugf("[curb.socket] sock_cb fd=%d what=%s (tracked)", fd, poll_what_str(what, b, sizeof(b)));
|
|
681
1114
|
}
|
|
1115
|
+
#endif
|
|
682
1116
|
}
|
|
683
1117
|
return 0;
|
|
684
1118
|
}
|
|
@@ -702,6 +1136,7 @@ static int rb_fdset_from_sockmap_i(st_data_t key, st_data_t val, st_data_t argp)
|
|
|
702
1136
|
if (fd > a->maxfd) a->maxfd = fd;
|
|
703
1137
|
return ST_CONTINUE;
|
|
704
1138
|
}
|
|
1139
|
+
CURB_MAYBE_UNUSED_DECL
|
|
705
1140
|
static void rb_fdset_from_sockmap(st_table *map, rb_fdset_t *rfds, rb_fdset_t *wfds, rb_fdset_t *efds, int *maxfd_out) {
|
|
706
1141
|
if (!map) { *maxfd_out = -1; return; }
|
|
707
1142
|
struct build_fdset_args a; a.r = rfds; a.w = wfds; a.e = efds; a.maxfd = -1;
|
|
@@ -742,13 +1177,30 @@ static int st_count_i(st_data_t k, st_data_t v, st_data_t argp) {
|
|
|
742
1177
|
return ST_CONTINUE;
|
|
743
1178
|
}
|
|
744
1179
|
|
|
1180
|
+
static VALUE multi_socket_io_for_fd(multi_socket_ctx *ctx, int fd) {
|
|
1181
|
+
VALUE key = INT2NUM(fd);
|
|
1182
|
+
VALUE io = rb_hash_aref(ctx->io_cache, key);
|
|
1183
|
+
if (NIL_P(io)) {
|
|
1184
|
+
io = rb_funcall(rb_cIO, rb_intern("for_fd"), 2, key, rb_str_new_cstr("r+"));
|
|
1185
|
+
rb_funcall(io, rb_intern("autoclose="), 1, Qfalse);
|
|
1186
|
+
rb_hash_aset(ctx->io_cache, key, io);
|
|
1187
|
+
}
|
|
1188
|
+
return io;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
struct io_for_fd_args { multi_socket_ctx *ctx; int fd; };
|
|
1192
|
+
static VALUE multi_socket_io_for_fd_protected(VALUE argp) {
|
|
1193
|
+
struct io_for_fd_args *a = (struct io_for_fd_args *)argp;
|
|
1194
|
+
return multi_socket_io_for_fd(a->ctx, a->fd);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
745
1197
|
static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_socket_ctx *ctx, VALUE block) {
|
|
746
1198
|
/* prime the state: let libcurl act on timeouts to setup sockets */
|
|
747
1199
|
CURLMcode mrc = curl_multi_socket_action(rbcm->handle, CURL_SOCKET_TIMEOUT, 0, &rbcm->running);
|
|
748
1200
|
if (mrc != CURLM_OK) raise_curl_multi_error_exception(mrc);
|
|
749
1201
|
curb_debugf("[curb.socket] drive: initial socket_action timeout -> mrc=%d running=%d", mrc, rbcm->running);
|
|
750
1202
|
rb_curl_multi_read_info(self, rbcm->handle);
|
|
751
|
-
|
|
1203
|
+
rb_curl_multi_yield_if_given(self, block);
|
|
752
1204
|
|
|
753
1205
|
while (rbcm->running) {
|
|
754
1206
|
struct timeval tv = {0, 0};
|
|
@@ -791,9 +1243,7 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
791
1243
|
rb_fdset_t rfds, wfds, efds;
|
|
792
1244
|
rb_fd_init(&rfds); rb_fd_init(&wfds); rb_fd_init(&efds);
|
|
793
1245
|
int maxfd = -1;
|
|
794
|
-
|
|
795
|
-
st_foreach(ctx->sock_map, rb_fdset_from_sockmap_i, (st_data_t)&a2);
|
|
796
|
-
maxfd = a2.maxfd;
|
|
1246
|
+
rb_fdset_from_sockmap(ctx->sock_map, &rfds, &wfds, &efds, &maxfd);
|
|
797
1247
|
int rc = rb_thread_fd_select(maxfd + 1, &rfds, &wfds, &efds, &tv);
|
|
798
1248
|
curb_debugf("[curb.socket] rb_thread_fd_select(multi) rc=%d maxfd=%d", rc, maxfd);
|
|
799
1249
|
if (rc < 0) {
|
|
@@ -814,25 +1264,8 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
814
1264
|
rb_fd_term(&rfds); rb_fd_term(&wfds); rb_fd_term(&efds);
|
|
815
1265
|
handled_wait = 1;
|
|
816
1266
|
} else if (count_tracked == 1) {
|
|
817
|
-
#if defined(HAVE_RB_WAIT_FOR_SINGLE_FD)
|
|
818
|
-
if (wait_fd >= 0) {
|
|
819
|
-
int ev = 0;
|
|
820
|
-
if (wait_what == CURL_POLL_IN) ev = RB_WAITFD_IN;
|
|
821
|
-
else if (wait_what == CURL_POLL_OUT) ev = RB_WAITFD_OUT;
|
|
822
|
-
else if (wait_what == CURL_POLL_INOUT) ev = RB_WAITFD_IN|RB_WAITFD_OUT;
|
|
823
|
-
int rc = rb_wait_for_single_fd(wait_fd, ev, &tv);
|
|
824
|
-
curb_debugf("[curb.socket] rb_wait_for_single_fd rc=%d fd=%d ev=%d", rc, wait_fd, ev);
|
|
825
|
-
if (rc < 0) {
|
|
826
|
-
if (errno != EINTR) rb_raise(rb_eRuntimeError, "wait_for_single_fd(): %s", strerror(errno));
|
|
827
|
-
continue;
|
|
828
|
-
}
|
|
829
|
-
any_ready = (rc != 0);
|
|
830
|
-
did_timeout = (rc == 0);
|
|
831
|
-
handled_wait = 1;
|
|
832
|
-
}
|
|
833
|
-
#endif
|
|
834
1267
|
#if defined(HAVE_RB_FIBER_SCHEDULER_IO_WAIT) && defined(HAVE_RB_FIBER_SCHEDULER_CURRENT)
|
|
835
|
-
|
|
1268
|
+
{
|
|
836
1269
|
VALUE scheduler = rb_fiber_scheduler_current();
|
|
837
1270
|
if (scheduler != Qnil) {
|
|
838
1271
|
int events = 0;
|
|
@@ -845,25 +1278,58 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
845
1278
|
double timeout_s = (double)tv.tv_sec + ((double)tv.tv_usec / 1e6);
|
|
846
1279
|
VALUE timeout = rb_float_new(timeout_s);
|
|
847
1280
|
if (wait_fd < 0) {
|
|
1281
|
+
#ifdef HAVE_RB_THREAD_FD_SELECT
|
|
1282
|
+
rb_thread_fd_select(0, NULL, NULL, NULL, &tv);
|
|
1283
|
+
#else
|
|
848
1284
|
rb_thread_wait_for(tv);
|
|
1285
|
+
#endif
|
|
1286
|
+
did_timeout = 1;
|
|
1287
|
+
} else if (!multi_socket_fd_valid_p(wait_fd)) {
|
|
1288
|
+
multi_socket_forget_fd(ctx, wait_fd);
|
|
849
1289
|
did_timeout = 1;
|
|
850
1290
|
} else {
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
1291
|
+
struct io_for_fd_args io_args = { ctx, wait_fd };
|
|
1292
|
+
int io_state = 0;
|
|
1293
|
+
VALUE io = rb_protect(multi_socket_io_for_fd_protected, (VALUE)&io_args, &io_state);
|
|
1294
|
+
if (io_state || NIL_P(io)) {
|
|
1295
|
+
if (io_state) rb_set_errinfo(Qnil);
|
|
1296
|
+
multi_socket_forget_fd(ctx, wait_fd);
|
|
1297
|
+
did_timeout = 1;
|
|
1298
|
+
any_ready = 0;
|
|
859
1299
|
} else {
|
|
860
|
-
|
|
861
|
-
|
|
1300
|
+
struct fiber_io_wait_args args = { scheduler, io, INT2NUM(events), timeout };
|
|
1301
|
+
int state = 0;
|
|
1302
|
+
VALUE ready = rb_protect(fiber_io_wait_protected, (VALUE)&args, &state);
|
|
1303
|
+
if (state) {
|
|
1304
|
+
rb_set_errinfo(Qnil);
|
|
1305
|
+
did_timeout = 1;
|
|
1306
|
+
any_ready = 0;
|
|
1307
|
+
} else {
|
|
1308
|
+
any_ready = (ready != Qfalse);
|
|
1309
|
+
did_timeout = !any_ready;
|
|
1310
|
+
}
|
|
862
1311
|
}
|
|
863
1312
|
}
|
|
864
1313
|
handled_wait = 1;
|
|
865
1314
|
}
|
|
866
1315
|
}
|
|
1316
|
+
#endif
|
|
1317
|
+
#if defined(HAVE_RB_WAIT_FOR_SINGLE_FD)
|
|
1318
|
+
if (!handled_wait && wait_fd >= 0) {
|
|
1319
|
+
int ev = 0;
|
|
1320
|
+
if (wait_what == CURL_POLL_IN) ev = RB_WAITFD_IN;
|
|
1321
|
+
else if (wait_what == CURL_POLL_OUT) ev = RB_WAITFD_OUT;
|
|
1322
|
+
else if (wait_what == CURL_POLL_INOUT) ev = RB_WAITFD_IN|RB_WAITFD_OUT;
|
|
1323
|
+
int rc = rb_wait_for_single_fd(wait_fd, ev, &tv);
|
|
1324
|
+
curb_debugf("[curb.socket] rb_wait_for_single_fd rc=%d fd=%d ev=%d", rc, wait_fd, ev);
|
|
1325
|
+
if (rc < 0) {
|
|
1326
|
+
if (errno != EINTR) rb_raise(rb_eRuntimeError, "wait_for_single_fd(): %s", strerror(errno));
|
|
1327
|
+
continue;
|
|
1328
|
+
}
|
|
1329
|
+
any_ready = (rc != 0);
|
|
1330
|
+
did_timeout = (rc == 0);
|
|
1331
|
+
handled_wait = 1;
|
|
1332
|
+
}
|
|
867
1333
|
#endif
|
|
868
1334
|
if (!handled_wait) {
|
|
869
1335
|
/* Fallback: single-fd select. */
|
|
@@ -888,7 +1354,11 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
888
1354
|
rb_fd_term(&rfds); rb_fd_term(&wfds); rb_fd_term(&efds);
|
|
889
1355
|
}
|
|
890
1356
|
} else { /* count_tracked == 0 */
|
|
1357
|
+
#ifdef HAVE_RB_THREAD_FD_SELECT
|
|
1358
|
+
rb_thread_fd_select(0, NULL, NULL, NULL, &tv);
|
|
1359
|
+
#else
|
|
891
1360
|
rb_thread_wait_for(tv);
|
|
1361
|
+
#endif
|
|
892
1362
|
did_timeout = 1;
|
|
893
1363
|
}
|
|
894
1364
|
|
|
@@ -902,8 +1372,12 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
902
1372
|
if (wait_what == CURL_POLL_IN || wait_what == CURL_POLL_INOUT) flags |= CURL_CSELECT_IN;
|
|
903
1373
|
if (wait_what == CURL_POLL_OUT || wait_what == CURL_POLL_INOUT) flags |= CURL_CSELECT_OUT;
|
|
904
1374
|
flags |= CURL_CSELECT_ERR;
|
|
905
|
-
|
|
906
|
-
|
|
1375
|
+
#if CURB_SOCKET_DEBUG
|
|
1376
|
+
{
|
|
1377
|
+
char b[32];
|
|
1378
|
+
curb_debugf("[curb.socket] socket_action fd=%d flags=%s", wait_fd, cselect_flags_str(flags, b, sizeof(b)));
|
|
1379
|
+
}
|
|
1380
|
+
#endif
|
|
907
1381
|
mrc = curl_multi_socket_action(rbcm->handle, (curl_socket_t)wait_fd, flags, &rbcm->running);
|
|
908
1382
|
curb_debugf("[curb.socket] socket_action -> mrc=%d running=%d", mrc, rbcm->running);
|
|
909
1383
|
if (mrc != CURLM_OK) raise_curl_multi_error_exception(mrc);
|
|
@@ -912,7 +1386,7 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
912
1386
|
|
|
913
1387
|
rb_curl_multi_read_info(self, rbcm->handle);
|
|
914
1388
|
curb_debugf("[curb.socket] processed completions; running=%d", rbcm->running);
|
|
915
|
-
|
|
1389
|
+
rb_curl_multi_yield_if_given(self, block);
|
|
916
1390
|
}
|
|
917
1391
|
}
|
|
918
1392
|
|
|
@@ -935,6 +1409,9 @@ static VALUE ruby_curl_multi_socket_drive_ensure(VALUE argp) {
|
|
|
935
1409
|
st_free_table(c->ctx->sock_map);
|
|
936
1410
|
c->ctx->sock_map = NULL;
|
|
937
1411
|
}
|
|
1412
|
+
if (c->ctx) {
|
|
1413
|
+
c->ctx->io_cache = Qnil;
|
|
1414
|
+
}
|
|
938
1415
|
return Qnil;
|
|
939
1416
|
}
|
|
940
1417
|
|
|
@@ -943,11 +1420,16 @@ VALUE ruby_curl_multi_socket_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
943
1420
|
VALUE block = Qnil;
|
|
944
1421
|
rb_scan_args(argc, argv, "0&", &block);
|
|
945
1422
|
|
|
946
|
-
|
|
1423
|
+
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
1424
|
+
ruby_curl_multi_ensure_handle(rbcm);
|
|
1425
|
+
if (!rb_ivar_defined(self, id_deferred_exception_ivar)) {
|
|
1426
|
+
clear_multi_deferred_exception_source_id_if_any(self);
|
|
1427
|
+
}
|
|
947
1428
|
|
|
948
1429
|
multi_socket_ctx ctx;
|
|
949
1430
|
ctx.sock_map = st_init_numtable();
|
|
950
1431
|
ctx.timeout_ms = -1;
|
|
1432
|
+
ctx.io_cache = rb_hash_new();
|
|
951
1433
|
|
|
952
1434
|
/* install socket/timer callbacks */
|
|
953
1435
|
curl_multi_setopt(rbcm->handle, CURLMOPT_SOCKETFUNCTION, multi_socket_cb);
|
|
@@ -962,8 +1444,8 @@ VALUE ruby_curl_multi_socket_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
962
1444
|
|
|
963
1445
|
/* finalize */
|
|
964
1446
|
rb_curl_multi_read_info(self, rbcm->handle);
|
|
965
|
-
|
|
966
|
-
if (cCurlMutiAutoClose == 1) rb_funcall(self, rb_intern("
|
|
1447
|
+
rb_curl_multi_yield_if_given(self, block);
|
|
1448
|
+
if (cCurlMutiAutoClose == 1) rb_funcall(self, rb_intern("_autoclose"), 0);
|
|
967
1449
|
|
|
968
1450
|
return Qtrue;
|
|
969
1451
|
}
|
|
@@ -998,7 +1480,8 @@ void cleanup_crt_fd(fd_set *os_set, fd_set *crt_set)
|
|
|
998
1480
|
}
|
|
999
1481
|
#endif
|
|
1000
1482
|
|
|
1001
|
-
|
|
1483
|
+
/* curb_select is only needed when rb_thread_fd_select is NOT available */
|
|
1484
|
+
#if !defined(HAVE_RB_THREAD_FD_SELECT) && (defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
|
|
1002
1485
|
struct _select_set {
|
|
1003
1486
|
int maxfd;
|
|
1004
1487
|
fd_set *fdread, *fdwrite, *fdexcep;
|
|
@@ -1039,13 +1522,17 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1039
1522
|
struct timeval tv = {0, 0};
|
|
1040
1523
|
struct timeval tv_100ms = {0, 100000};
|
|
1041
1524
|
VALUE block = Qnil;
|
|
1042
|
-
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
|
1525
|
+
#if !defined(HAVE_RB_THREAD_FD_SELECT) && (defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
|
|
1043
1526
|
struct _select_set fdset_args;
|
|
1044
1527
|
#endif
|
|
1045
1528
|
|
|
1046
1529
|
rb_scan_args(argc, argv, "0&", &block);
|
|
1047
1530
|
|
|
1048
|
-
|
|
1531
|
+
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
1532
|
+
ruby_curl_multi_ensure_handle(rbcm);
|
|
1533
|
+
if (!rb_ivar_defined(self, id_deferred_exception_ivar)) {
|
|
1534
|
+
clear_multi_deferred_exception_source_id_if_any(self);
|
|
1535
|
+
}
|
|
1049
1536
|
|
|
1050
1537
|
timeout_milliseconds = cCurlMutiDefaulttimeout;
|
|
1051
1538
|
|
|
@@ -1062,9 +1549,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1062
1549
|
// There are no more messages to handle by curl and we can run the ruby block
|
|
1063
1550
|
// passed to perform method.
|
|
1064
1551
|
// When the block completes curl will resume.
|
|
1065
|
-
|
|
1066
|
-
rb_funcall(block, rb_intern("call"), 1, self);
|
|
1067
|
-
}
|
|
1552
|
+
rb_curl_multi_yield_if_given(self, block);
|
|
1068
1553
|
|
|
1069
1554
|
do {
|
|
1070
1555
|
while (rbcm->running) {
|
|
@@ -1082,7 +1567,12 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1082
1567
|
if (timeout_milliseconds == 0) { /* no delay */
|
|
1083
1568
|
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
|
1084
1569
|
rb_curl_multi_read_info( self, rbcm->handle );
|
|
1085
|
-
|
|
1570
|
+
rb_curl_multi_yield_if_given(self, block);
|
|
1571
|
+
#if defined(HAVE_RB_FIBER_SCHEDULER_CURRENT)
|
|
1572
|
+
if (rb_fiber_scheduler_current() != Qnil) {
|
|
1573
|
+
rb_thread_schedule();
|
|
1574
|
+
}
|
|
1575
|
+
#endif
|
|
1086
1576
|
continue;
|
|
1087
1577
|
}
|
|
1088
1578
|
|
|
@@ -1127,7 +1617,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1127
1617
|
/* Process pending transfers after waiting */
|
|
1128
1618
|
rb_curl_multi_run(self, rbcm->handle, &(rbcm->running));
|
|
1129
1619
|
rb_curl_multi_read_info(self, rbcm->handle);
|
|
1130
|
-
|
|
1620
|
+
rb_curl_multi_yield_if_given(self, block);
|
|
1131
1621
|
}
|
|
1132
1622
|
#else
|
|
1133
1623
|
|
|
@@ -1146,7 +1636,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1146
1636
|
|
|
1147
1637
|
if (maxfd == -1) {
|
|
1148
1638
|
/* libcurl recommends sleeping for 100ms */
|
|
1149
|
-
#
|
|
1639
|
+
#ifdef HAVE_RB_THREAD_FD_SELECT
|
|
1150
1640
|
struct timeval tv_sleep = tv_100ms;
|
|
1151
1641
|
rb_thread_fd_select(0, NULL, NULL, NULL, &tv_sleep);
|
|
1152
1642
|
#else
|
|
@@ -1154,7 +1644,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1154
1644
|
#endif
|
|
1155
1645
|
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
|
1156
1646
|
rb_curl_multi_read_info( self, rbcm->handle );
|
|
1157
|
-
|
|
1647
|
+
rb_curl_multi_yield_if_given(self, block);
|
|
1158
1648
|
continue;
|
|
1159
1649
|
}
|
|
1160
1650
|
|
|
@@ -1165,7 +1655,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1165
1655
|
#endif
|
|
1166
1656
|
|
|
1167
1657
|
|
|
1168
|
-
#if (defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
|
|
1658
|
+
#if !defined(HAVE_RB_THREAD_FD_SELECT) && (defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
|
|
1169
1659
|
fdset_args.maxfd = maxfd+1;
|
|
1170
1660
|
fdset_args.fdread = &fdread;
|
|
1171
1661
|
fdset_args.fdwrite = &fdwrite;
|
|
@@ -1173,7 +1663,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1173
1663
|
fdset_args.tv = &tv;
|
|
1174
1664
|
#endif
|
|
1175
1665
|
|
|
1176
|
-
#
|
|
1666
|
+
#ifdef HAVE_RB_THREAD_FD_SELECT
|
|
1177
1667
|
/* Prefer scheduler-aware waiting when available. Build rb_fdset_t sets. */
|
|
1178
1668
|
{
|
|
1179
1669
|
rb_fdset_t rfds, wfds, efds;
|
|
@@ -1224,7 +1714,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1224
1714
|
default: /* action */
|
|
1225
1715
|
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
|
1226
1716
|
rb_curl_multi_read_info( self, rbcm->handle );
|
|
1227
|
-
|
|
1717
|
+
rb_curl_multi_yield_if_given(self, block);
|
|
1228
1718
|
break;
|
|
1229
1719
|
}
|
|
1230
1720
|
#endif /* disabled curl_multi_wait: use fdsets */
|
|
@@ -1233,9 +1723,9 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1233
1723
|
} while( rbcm->running );
|
|
1234
1724
|
|
|
1235
1725
|
rb_curl_multi_read_info( self, rbcm->handle );
|
|
1236
|
-
|
|
1726
|
+
rb_curl_multi_yield_if_given(self, block);
|
|
1237
1727
|
if (cCurlMutiAutoClose == 1) {
|
|
1238
|
-
rb_funcall(self, rb_intern("
|
|
1728
|
+
rb_funcall(self, rb_intern("_autoclose"), 0);
|
|
1239
1729
|
}
|
|
1240
1730
|
return Qtrue;
|
|
1241
1731
|
}
|
|
@@ -1249,7 +1739,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1249
1739
|
*/
|
|
1250
1740
|
VALUE ruby_curl_multi_close(VALUE self) {
|
|
1251
1741
|
ruby_curl_multi *rbcm;
|
|
1252
|
-
|
|
1742
|
+
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
1253
1743
|
rb_curl_multi_detach_all(rbcm);
|
|
1254
1744
|
|
|
1255
1745
|
if (rbcm->handle) {
|
|
@@ -1257,7 +1747,19 @@ VALUE ruby_curl_multi_close(VALUE self) {
|
|
|
1257
1747
|
rbcm->handle = NULL;
|
|
1258
1748
|
}
|
|
1259
1749
|
|
|
1260
|
-
|
|
1750
|
+
rbcm->active = 0;
|
|
1751
|
+
rbcm->running = 0;
|
|
1752
|
+
clear_multi_deferred_exception_if_any(self);
|
|
1753
|
+
clear_multi_deferred_exception_source_id_if_any(self);
|
|
1754
|
+
return self;
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
static VALUE ruby_curl_multi_mark_closed(VALUE self) {
|
|
1758
|
+
ruby_curl_multi *rbcm;
|
|
1759
|
+
|
|
1760
|
+
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
1761
|
+
rbcm->closed = 1;
|
|
1762
|
+
|
|
1261
1763
|
return self;
|
|
1262
1764
|
}
|
|
1263
1765
|
|
|
@@ -1280,28 +1782,33 @@ static void curl_multi_mark(void *ptr) {
|
|
|
1280
1782
|
/* =================== INIT LIB =====================*/
|
|
1281
1783
|
void init_curb_multi() {
|
|
1282
1784
|
idCall = rb_intern("call");
|
|
1785
|
+
id_deferred_exception_ivar = rb_intern("@__curb_deferred_exception");
|
|
1786
|
+
id_deferred_exception_source_id_ivar = rb_intern("@__curb_deferred_exception_source_id");
|
|
1283
1787
|
cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject);
|
|
1284
1788
|
|
|
1285
|
-
|
|
1789
|
+
rb_define_alloc_func(cCurlMulti, ruby_curl_multi_alloc);
|
|
1286
1790
|
|
|
1287
1791
|
/* Class methods */
|
|
1288
|
-
rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0);
|
|
1289
1792
|
rb_define_singleton_method(cCurlMulti, "default_timeout=", ruby_curl_multi_set_default_timeout, 1);
|
|
1290
1793
|
rb_define_singleton_method(cCurlMulti, "default_timeout", ruby_curl_multi_get_default_timeout, 0);
|
|
1291
1794
|
rb_define_singleton_method(cCurlMulti, "autoclose=", ruby_curl_multi_set_autoclose, 1);
|
|
1292
1795
|
rb_define_singleton_method(cCurlMulti, "autoclose", ruby_curl_multi_get_autoclose, 0);
|
|
1293
1796
|
/* Instance methods */
|
|
1797
|
+
rb_define_method(cCurlMulti, "initialize", ruby_curl_multi_initialize, 0);
|
|
1294
1798
|
rb_define_method(cCurlMulti, "max_connects=", ruby_curl_multi_max_connects, 1);
|
|
1295
1799
|
rb_define_method(cCurlMulti, "max_host_connections=", ruby_curl_multi_max_host_connections, 1);
|
|
1296
1800
|
rb_define_method(cCurlMulti, "pipeline=", ruby_curl_multi_pipeline, 1);
|
|
1297
1801
|
rb_define_method(cCurlMulti, "_add", ruby_curl_multi_add, 1);
|
|
1298
1802
|
rb_define_method(cCurlMulti, "_remove", ruby_curl_multi_remove, 1);
|
|
1299
|
-
/*
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1803
|
+
/*
|
|
1804
|
+
* The legacy fdset loop is the stable default. The newer socket-action path
|
|
1805
|
+
* is kept in-tree, but it has shown scheduler regressions for one-handle
|
|
1806
|
+
* multi usage (for example Curl::Easy#perform under Async).
|
|
1807
|
+
*/
|
|
1304
1808
|
rb_define_method(cCurlMulti, "perform", ruby_curl_multi_perform, -1);
|
|
1809
|
+
#if defined(HAVE_CURL_MULTI_SOCKET_ACTION) && defined(HAVE_CURLMOPT_SOCKETFUNCTION) && defined(HAVE_CURLMOPT_TIMERFUNCTION) && defined(HAVE_RB_THREAD_FD_SELECT) && !defined(_WIN32)
|
|
1810
|
+
rb_define_private_method(cCurlMulti, "_socket_perform", ruby_curl_multi_socket_perform, -1);
|
|
1305
1811
|
#endif
|
|
1306
1812
|
rb_define_method(cCurlMulti, "_close", ruby_curl_multi_close, 0);
|
|
1813
|
+
rb_define_private_method(cCurlMulti, "_mark_closed", ruby_curl_multi_mark_closed, 0);
|
|
1307
1814
|
}
|