curb 1.2.2 → 1.3.1
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/Rakefile +22 -0
- data/ext/curb.c +282 -231
- data/ext/curb.h +4 -4
- data/ext/curb_easy.c +608 -215
- data/ext/curb_easy.h +5 -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 +612 -142
- 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 +42 -1
- 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_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_gc_compact.rb +178 -16
- data/tests/tc_test_server_methods.rb +110 -0
- metadata +10 -14
- data/tests/test_basic.rb +0 -29
- data/tests/test_fiber_debug.rb +0 -69
- data/tests/test_fiber_simple.rb +0 -65
- data/tests/test_real_url.rb +0 -65
- data/tests/test_simple_fiber.rb +0 -34
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);
|
|
@@ -474,118 +665,291 @@ static VALUE find_easy_by_handle(ruby_curl_multi *rbcm, CURL *easy_handle) {
|
|
|
474
665
|
return ctx.easy;
|
|
475
666
|
}
|
|
476
667
|
|
|
477
|
-
static
|
|
478
|
-
long response_code = -1;
|
|
668
|
+
static VALUE find_easy_value_for_handle(ruby_curl_multi *rbcm, CURL *easy_handle) {
|
|
479
669
|
VALUE easy = Qnil;
|
|
480
670
|
ruby_curl_easy *rbce = NULL;
|
|
481
|
-
VALUE callargs;
|
|
482
|
-
ruby_curl_multi *rbcm = NULL;
|
|
483
|
-
|
|
484
|
-
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
|
485
671
|
|
|
486
|
-
|
|
487
|
-
CURLcode private_rc = curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, (char**)&rbce);
|
|
672
|
+
CURLcode private_rc = curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, (char **)&rbce);
|
|
488
673
|
if (private_rc == CURLE_OK && rbce) {
|
|
489
674
|
easy = rbce->self;
|
|
490
675
|
}
|
|
491
676
|
|
|
492
|
-
/* If PRIVATE is unavailable or invalid, fall back to scanning attachments. */
|
|
493
677
|
if (NIL_P(easy) || !RB_TYPE_P(easy, T_DATA)) {
|
|
494
678
|
easy = find_easy_by_handle(rbcm, easy_handle);
|
|
495
|
-
if (!NIL_P(easy) && RB_TYPE_P(easy, T_DATA)) {
|
|
496
|
-
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
|
497
|
-
}
|
|
498
679
|
}
|
|
499
680
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
681
|
+
return easy;
|
|
682
|
+
}
|
|
683
|
+
|
|
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
|
+
};
|
|
692
|
+
|
|
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;
|
|
696
|
+
|
|
697
|
+
if (!rbce) {
|
|
698
|
+
return ST_CONTINUE;
|
|
699
|
+
}
|
|
700
|
+
|
|
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) {
|
|
505
747
|
return;
|
|
506
748
|
}
|
|
507
749
|
|
|
508
|
-
|
|
750
|
+
ctx.snapshot = snapshot;
|
|
751
|
+
ctx.easies = easies;
|
|
752
|
+
st_foreach(rbcm->attached, collect_new_attached_easies_i, (st_data_t)&ctx);
|
|
509
753
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
754
|
+
for (index = 0; index < RARRAY_LEN(easies); index++) {
|
|
755
|
+
VALUE easy = rb_ary_entry(easies, index);
|
|
756
|
+
ruby_curl_easy *rbce = NULL;
|
|
513
757
|
|
|
514
|
-
|
|
515
|
-
|
|
758
|
+
if (NIL_P(easy) || !RB_TYPE_P(easy, T_DATA)) {
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
516
761
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
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
|
+
}
|
|
766
|
+
|
|
767
|
+
rb_curl_multi_remove_request_reference(self, easy);
|
|
768
|
+
rb_curl_multi_remove(rbcm, easy);
|
|
521
769
|
}
|
|
522
770
|
|
|
523
|
-
|
|
524
|
-
|
|
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;
|
|
525
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;
|
|
526
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
|
+
}
|
|
527
804
|
|
|
528
805
|
if (!rb_easy_nil("complete_proc")) {
|
|
529
|
-
callargs = rb_ary_new3(2, rb_easy_get("complete_proc"), easy);
|
|
530
|
-
rbce->callback_active = 1;
|
|
806
|
+
callargs = rb_ary_new3(2, rb_easy_get("complete_proc"), args->easy);
|
|
807
|
+
args->rbce->callback_active = 1;
|
|
531
808
|
rb_rescue(call_status_handler1, callargs, callback_exception, did_raise);
|
|
532
|
-
rbce->callback_active = 0;
|
|
533
|
-
|
|
809
|
+
args->rbce->callback_active = 0;
|
|
810
|
+
stash_and_raise_status_callback_error_if_unmasked(args, did_raise, easy_callback_error);
|
|
534
811
|
}
|
|
535
812
|
|
|
536
813
|
#ifdef HAVE_CURLINFO_RESPONSE_CODE
|
|
537
|
-
curl_easy_getinfo(rbce->curl, CURLINFO_RESPONSE_CODE, &response_code);
|
|
814
|
+
curl_easy_getinfo(args->rbce->curl, CURLINFO_RESPONSE_CODE, &response_code);
|
|
538
815
|
#else /* use fdsets path for waiting */
|
|
539
816
|
// old libcurl
|
|
540
|
-
curl_easy_getinfo(rbce->curl, CURLINFO_HTTP_CODE, &response_code);
|
|
817
|
+
curl_easy_getinfo(args->rbce->curl, CURLINFO_HTTP_CODE, &response_code);
|
|
541
818
|
#endif
|
|
542
|
-
|
|
543
|
-
curl_easy_getinfo(rbce->curl, CURLINFO_REDIRECT_COUNT, &redirect_count);
|
|
819
|
+
curl_easy_getinfo(args->rbce->curl, CURLINFO_REDIRECT_COUNT, &redirect_count);
|
|
544
820
|
|
|
545
|
-
if (result != 0) {
|
|
821
|
+
if (args->result != 0) {
|
|
546
822
|
if (!rb_easy_nil("failure_proc")) {
|
|
547
|
-
callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
|
|
548
|
-
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;
|
|
549
825
|
rb_rescue(call_status_handler2, callargs, callback_exception, did_raise);
|
|
550
|
-
rbce->callback_active = 0;
|
|
551
|
-
|
|
826
|
+
args->rbce->callback_active = 0;
|
|
827
|
+
stash_and_raise_status_callback_error_if_unmasked(args, did_raise, easy_callback_error);
|
|
552
828
|
}
|
|
553
829
|
} else if (!rb_easy_nil("success_proc") &&
|
|
554
830
|
((response_code >= 200 && response_code < 300) || response_code == 0)) {
|
|
555
831
|
/* NOTE: we allow response_code == 0, in the case of non http requests e.g. reading from disk */
|
|
556
|
-
callargs = rb_ary_new3(2, rb_easy_get("success_proc"), easy);
|
|
557
|
-
rbce->callback_active = 1;
|
|
832
|
+
callargs = rb_ary_new3(2, rb_easy_get("success_proc"), args->easy);
|
|
833
|
+
args->rbce->callback_active = 1;
|
|
558
834
|
rb_rescue(call_status_handler1, callargs, callback_exception, did_raise);
|
|
559
|
-
rbce->callback_active = 0;
|
|
560
|
-
|
|
835
|
+
args->rbce->callback_active = 0;
|
|
836
|
+
stash_and_raise_status_callback_error_if_unmasked(args, did_raise, easy_callback_error);
|
|
561
837
|
|
|
562
838
|
} else if (!rb_easy_nil("redirect_proc") && ((response_code >= 300 && response_code < 400) || redirect_count > 0) ) {
|
|
563
839
|
/* Skip on_redirect callback if follow_location is false AND max_redirects is 0 */
|
|
564
|
-
if (!rbce->follow_location && rbce->max_redirs == 0) {
|
|
840
|
+
if (!args->rbce->follow_location && args->rbce->max_redirs == 0) {
|
|
565
841
|
// Do nothing - skip the callback
|
|
566
842
|
} else {
|
|
567
|
-
rbce->callback_active = 1;
|
|
568
|
-
callargs = rb_ary_new3(3, rb_easy_get("redirect_proc"), easy, rb_curl_easy_error(result));
|
|
569
|
-
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));
|
|
570
845
|
rb_rescue(call_status_handler2, callargs, callback_exception, did_raise);
|
|
571
|
-
|
|
846
|
+
args->rbce->callback_active = 0;
|
|
847
|
+
stash_and_raise_status_callback_error_if_unmasked(args, did_raise, easy_callback_error);
|
|
572
848
|
}
|
|
573
849
|
} else if (!rb_easy_nil("missing_proc") &&
|
|
574
850
|
(response_code >= 400 && response_code < 500)) {
|
|
575
|
-
rbce->callback_active = 1;
|
|
576
|
-
callargs = rb_ary_new3(3, rb_easy_get("missing_proc"), easy, rb_curl_easy_error(result));
|
|
577
|
-
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));
|
|
578
853
|
rb_rescue(call_status_handler2, callargs, callback_exception, did_raise);
|
|
579
|
-
|
|
854
|
+
args->rbce->callback_active = 0;
|
|
855
|
+
stash_and_raise_status_callback_error_if_unmasked(args, did_raise, easy_callback_error);
|
|
580
856
|
} else if (!rb_easy_nil("failure_proc") &&
|
|
581
857
|
(response_code >= 500 && response_code <= 999)) {
|
|
582
|
-
callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
|
|
583
|
-
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;
|
|
584
860
|
rb_rescue(call_status_handler2, callargs, callback_exception, did_raise);
|
|
585
|
-
rbce->callback_active = 0;
|
|
586
|
-
|
|
861
|
+
args->rbce->callback_active = 0;
|
|
862
|
+
stash_and_raise_status_callback_error_if_unmasked(args, did_raise, easy_callback_error);
|
|
863
|
+
}
|
|
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;
|
|
587
882
|
}
|
|
588
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);
|
|
589
953
|
}
|
|
590
954
|
|
|
591
955
|
static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
|
@@ -594,6 +958,9 @@ static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
|
|
594
958
|
CURLcode c_easy_result;
|
|
595
959
|
CURLMsg *c_multi_result; // for picking up messages with the transfer status
|
|
596
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);
|
|
597
964
|
|
|
598
965
|
/* Check for finished easy handles and remove from the multi handle.
|
|
599
966
|
* curl_multi_info_read will query for messages from individual handles.
|
|
@@ -609,8 +976,16 @@ static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
|
|
609
976
|
c_easy_handle = c_multi_result->easy_handle;
|
|
610
977
|
c_easy_result = c_multi_result->data.result; /* return code for transfer */
|
|
611
978
|
|
|
612
|
-
|
|
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
|
+
}
|
|
613
986
|
}
|
|
987
|
+
|
|
988
|
+
raise_multi_deferred_exception_if_idle(self);
|
|
614
989
|
}
|
|
615
990
|
|
|
616
991
|
/* called within ruby_curl_multi_perform */
|
|
@@ -650,6 +1025,7 @@ static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_runnin
|
|
|
650
1025
|
typedef struct {
|
|
651
1026
|
st_table *sock_map; /* key: int fd, value: int 'what' (CURL_POLL_*) */
|
|
652
1027
|
long timeout_ms; /* last timeout set by libcurl timer callback */
|
|
1028
|
+
VALUE io_cache; /* fd -> IO wrapper for fiber-scheduler waits */
|
|
653
1029
|
} multi_socket_ctx;
|
|
654
1030
|
|
|
655
1031
|
#if CURB_SOCKET_DEBUG
|
|
@@ -687,12 +1063,30 @@ static const char *cselect_flags_str(int flags, char *buf, size_t n) {
|
|
|
687
1063
|
#define cselect_flags_str(...) ""
|
|
688
1064
|
#endif
|
|
689
1065
|
|
|
1066
|
+
#if defined(HAVE_RB_FIBER_SCHEDULER_IO_WAIT) && defined(HAVE_RB_FIBER_SCHEDULER_CURRENT)
|
|
690
1067
|
/* Protected call to rb_fiber_scheduler_io_wait to avoid unwinding into C on TypeError. */
|
|
691
|
-
struct fiber_io_wait_args { VALUE scheduler; VALUE io;
|
|
1068
|
+
struct fiber_io_wait_args { VALUE scheduler; VALUE io; VALUE events; VALUE timeout; };
|
|
692
1069
|
static VALUE fiber_io_wait_protected(VALUE argp) {
|
|
693
1070
|
struct fiber_io_wait_args *a = (struct fiber_io_wait_args *)argp;
|
|
694
1071
|
return rb_fiber_scheduler_io_wait(a->scheduler, a->io, a->events, a->timeout);
|
|
695
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
|
+
}
|
|
696
1090
|
|
|
697
1091
|
static int multi_socket_cb(CURL *easy, curl_socket_t s, int what, void *userp, void *socketp) {
|
|
698
1092
|
multi_socket_ctx *ctx = (multi_socket_ctx *)userp;
|
|
@@ -700,22 +1094,25 @@ static int multi_socket_cb(CURL *easy, curl_socket_t s, int what, void *userp, v
|
|
|
700
1094
|
int fd = (int)s;
|
|
701
1095
|
|
|
702
1096
|
if (!ctx || !ctx->sock_map) return 0;
|
|
1097
|
+
if (fd < 0) return 0;
|
|
703
1098
|
|
|
704
1099
|
if (what == CURL_POLL_REMOVE) {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
st_delete(ctx->sock_map, &k, &rec);
|
|
1100
|
+
multi_socket_forget_fd(ctx, fd);
|
|
1101
|
+
#if CURB_SOCKET_DEBUG
|
|
708
1102
|
{
|
|
709
1103
|
char b[16];
|
|
710
1104
|
curb_debugf("[curb.socket] sock_cb fd=%d what=%s (removed)", fd, poll_what_str(what, b, sizeof(b)));
|
|
711
1105
|
}
|
|
1106
|
+
#endif
|
|
712
1107
|
} else {
|
|
713
1108
|
/* store current interest mask for this fd */
|
|
714
1109
|
st_insert(ctx->sock_map, (st_data_t)fd, (st_data_t)what);
|
|
1110
|
+
#if CURB_SOCKET_DEBUG
|
|
715
1111
|
{
|
|
716
1112
|
char b[16];
|
|
717
1113
|
curb_debugf("[curb.socket] sock_cb fd=%d what=%s (tracked)", fd, poll_what_str(what, b, sizeof(b)));
|
|
718
1114
|
}
|
|
1115
|
+
#endif
|
|
719
1116
|
}
|
|
720
1117
|
return 0;
|
|
721
1118
|
}
|
|
@@ -739,6 +1136,7 @@ static int rb_fdset_from_sockmap_i(st_data_t key, st_data_t val, st_data_t argp)
|
|
|
739
1136
|
if (fd > a->maxfd) a->maxfd = fd;
|
|
740
1137
|
return ST_CONTINUE;
|
|
741
1138
|
}
|
|
1139
|
+
CURB_MAYBE_UNUSED_DECL
|
|
742
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) {
|
|
743
1141
|
if (!map) { *maxfd_out = -1; return; }
|
|
744
1142
|
struct build_fdset_args a; a.r = rfds; a.w = wfds; a.e = efds; a.maxfd = -1;
|
|
@@ -779,13 +1177,30 @@ static int st_count_i(st_data_t k, st_data_t v, st_data_t argp) {
|
|
|
779
1177
|
return ST_CONTINUE;
|
|
780
1178
|
}
|
|
781
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
|
+
|
|
782
1197
|
static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_socket_ctx *ctx, VALUE block) {
|
|
783
1198
|
/* prime the state: let libcurl act on timeouts to setup sockets */
|
|
784
1199
|
CURLMcode mrc = curl_multi_socket_action(rbcm->handle, CURL_SOCKET_TIMEOUT, 0, &rbcm->running);
|
|
785
1200
|
if (mrc != CURLM_OK) raise_curl_multi_error_exception(mrc);
|
|
786
1201
|
curb_debugf("[curb.socket] drive: initial socket_action timeout -> mrc=%d running=%d", mrc, rbcm->running);
|
|
787
1202
|
rb_curl_multi_read_info(self, rbcm->handle);
|
|
788
|
-
|
|
1203
|
+
rb_curl_multi_yield_if_given(self, block);
|
|
789
1204
|
|
|
790
1205
|
while (rbcm->running) {
|
|
791
1206
|
struct timeval tv = {0, 0};
|
|
@@ -828,9 +1243,7 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
828
1243
|
rb_fdset_t rfds, wfds, efds;
|
|
829
1244
|
rb_fd_init(&rfds); rb_fd_init(&wfds); rb_fd_init(&efds);
|
|
830
1245
|
int maxfd = -1;
|
|
831
|
-
|
|
832
|
-
st_foreach(ctx->sock_map, rb_fdset_from_sockmap_i, (st_data_t)&a2);
|
|
833
|
-
maxfd = a2.maxfd;
|
|
1246
|
+
rb_fdset_from_sockmap(ctx->sock_map, &rfds, &wfds, &efds, &maxfd);
|
|
834
1247
|
int rc = rb_thread_fd_select(maxfd + 1, &rfds, &wfds, &efds, &tv);
|
|
835
1248
|
curb_debugf("[curb.socket] rb_thread_fd_select(multi) rc=%d maxfd=%d", rc, maxfd);
|
|
836
1249
|
if (rc < 0) {
|
|
@@ -851,25 +1264,8 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
851
1264
|
rb_fd_term(&rfds); rb_fd_term(&wfds); rb_fd_term(&efds);
|
|
852
1265
|
handled_wait = 1;
|
|
853
1266
|
} else if (count_tracked == 1) {
|
|
854
|
-
#if defined(HAVE_RB_WAIT_FOR_SINGLE_FD)
|
|
855
|
-
if (wait_fd >= 0) {
|
|
856
|
-
int ev = 0;
|
|
857
|
-
if (wait_what == CURL_POLL_IN) ev = RB_WAITFD_IN;
|
|
858
|
-
else if (wait_what == CURL_POLL_OUT) ev = RB_WAITFD_OUT;
|
|
859
|
-
else if (wait_what == CURL_POLL_INOUT) ev = RB_WAITFD_IN|RB_WAITFD_OUT;
|
|
860
|
-
int rc = rb_wait_for_single_fd(wait_fd, ev, &tv);
|
|
861
|
-
curb_debugf("[curb.socket] rb_wait_for_single_fd rc=%d fd=%d ev=%d", rc, wait_fd, ev);
|
|
862
|
-
if (rc < 0) {
|
|
863
|
-
if (errno != EINTR) rb_raise(rb_eRuntimeError, "wait_for_single_fd(): %s", strerror(errno));
|
|
864
|
-
continue;
|
|
865
|
-
}
|
|
866
|
-
any_ready = (rc != 0);
|
|
867
|
-
did_timeout = (rc == 0);
|
|
868
|
-
handled_wait = 1;
|
|
869
|
-
}
|
|
870
|
-
#endif
|
|
871
1267
|
#if defined(HAVE_RB_FIBER_SCHEDULER_IO_WAIT) && defined(HAVE_RB_FIBER_SCHEDULER_CURRENT)
|
|
872
|
-
|
|
1268
|
+
{
|
|
873
1269
|
VALUE scheduler = rb_fiber_scheduler_current();
|
|
874
1270
|
if (scheduler != Qnil) {
|
|
875
1271
|
int events = 0;
|
|
@@ -882,25 +1278,58 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
882
1278
|
double timeout_s = (double)tv.tv_sec + ((double)tv.tv_usec / 1e6);
|
|
883
1279
|
VALUE timeout = rb_float_new(timeout_s);
|
|
884
1280
|
if (wait_fd < 0) {
|
|
1281
|
+
#ifdef HAVE_RB_THREAD_FD_SELECT
|
|
1282
|
+
rb_thread_fd_select(0, NULL, NULL, NULL, &tv);
|
|
1283
|
+
#else
|
|
885
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);
|
|
886
1289
|
did_timeout = 1;
|
|
887
1290
|
} else {
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
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;
|
|
896
1299
|
} else {
|
|
897
|
-
|
|
898
|
-
|
|
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
|
+
}
|
|
899
1311
|
}
|
|
900
1312
|
}
|
|
901
1313
|
handled_wait = 1;
|
|
902
1314
|
}
|
|
903
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
|
+
}
|
|
904
1333
|
#endif
|
|
905
1334
|
if (!handled_wait) {
|
|
906
1335
|
/* Fallback: single-fd select. */
|
|
@@ -925,7 +1354,11 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
925
1354
|
rb_fd_term(&rfds); rb_fd_term(&wfds); rb_fd_term(&efds);
|
|
926
1355
|
}
|
|
927
1356
|
} else { /* count_tracked == 0 */
|
|
1357
|
+
#ifdef HAVE_RB_THREAD_FD_SELECT
|
|
1358
|
+
rb_thread_fd_select(0, NULL, NULL, NULL, &tv);
|
|
1359
|
+
#else
|
|
928
1360
|
rb_thread_wait_for(tv);
|
|
1361
|
+
#endif
|
|
929
1362
|
did_timeout = 1;
|
|
930
1363
|
}
|
|
931
1364
|
|
|
@@ -939,8 +1372,12 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
939
1372
|
if (wait_what == CURL_POLL_IN || wait_what == CURL_POLL_INOUT) flags |= CURL_CSELECT_IN;
|
|
940
1373
|
if (wait_what == CURL_POLL_OUT || wait_what == CURL_POLL_INOUT) flags |= CURL_CSELECT_OUT;
|
|
941
1374
|
flags |= CURL_CSELECT_ERR;
|
|
942
|
-
|
|
943
|
-
|
|
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
|
|
944
1381
|
mrc = curl_multi_socket_action(rbcm->handle, (curl_socket_t)wait_fd, flags, &rbcm->running);
|
|
945
1382
|
curb_debugf("[curb.socket] socket_action -> mrc=%d running=%d", mrc, rbcm->running);
|
|
946
1383
|
if (mrc != CURLM_OK) raise_curl_multi_error_exception(mrc);
|
|
@@ -949,7 +1386,7 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
949
1386
|
|
|
950
1387
|
rb_curl_multi_read_info(self, rbcm->handle);
|
|
951
1388
|
curb_debugf("[curb.socket] processed completions; running=%d", rbcm->running);
|
|
952
|
-
|
|
1389
|
+
rb_curl_multi_yield_if_given(self, block);
|
|
953
1390
|
}
|
|
954
1391
|
}
|
|
955
1392
|
|
|
@@ -972,6 +1409,9 @@ static VALUE ruby_curl_multi_socket_drive_ensure(VALUE argp) {
|
|
|
972
1409
|
st_free_table(c->ctx->sock_map);
|
|
973
1410
|
c->ctx->sock_map = NULL;
|
|
974
1411
|
}
|
|
1412
|
+
if (c->ctx) {
|
|
1413
|
+
c->ctx->io_cache = Qnil;
|
|
1414
|
+
}
|
|
975
1415
|
return Qnil;
|
|
976
1416
|
}
|
|
977
1417
|
|
|
@@ -980,11 +1420,16 @@ VALUE ruby_curl_multi_socket_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
980
1420
|
VALUE block = Qnil;
|
|
981
1421
|
rb_scan_args(argc, argv, "0&", &block);
|
|
982
1422
|
|
|
983
|
-
|
|
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
|
+
}
|
|
984
1428
|
|
|
985
1429
|
multi_socket_ctx ctx;
|
|
986
1430
|
ctx.sock_map = st_init_numtable();
|
|
987
1431
|
ctx.timeout_ms = -1;
|
|
1432
|
+
ctx.io_cache = rb_hash_new();
|
|
988
1433
|
|
|
989
1434
|
/* install socket/timer callbacks */
|
|
990
1435
|
curl_multi_setopt(rbcm->handle, CURLMOPT_SOCKETFUNCTION, multi_socket_cb);
|
|
@@ -999,8 +1444,8 @@ VALUE ruby_curl_multi_socket_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
999
1444
|
|
|
1000
1445
|
/* finalize */
|
|
1001
1446
|
rb_curl_multi_read_info(self, rbcm->handle);
|
|
1002
|
-
|
|
1003
|
-
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);
|
|
1004
1449
|
|
|
1005
1450
|
return Qtrue;
|
|
1006
1451
|
}
|
|
@@ -1035,7 +1480,8 @@ void cleanup_crt_fd(fd_set *os_set, fd_set *crt_set)
|
|
|
1035
1480
|
}
|
|
1036
1481
|
#endif
|
|
1037
1482
|
|
|
1038
|
-
|
|
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))
|
|
1039
1485
|
struct _select_set {
|
|
1040
1486
|
int maxfd;
|
|
1041
1487
|
fd_set *fdread, *fdwrite, *fdexcep;
|
|
@@ -1076,13 +1522,17 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1076
1522
|
struct timeval tv = {0, 0};
|
|
1077
1523
|
struct timeval tv_100ms = {0, 100000};
|
|
1078
1524
|
VALUE block = Qnil;
|
|
1079
|
-
#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))
|
|
1080
1526
|
struct _select_set fdset_args;
|
|
1081
1527
|
#endif
|
|
1082
1528
|
|
|
1083
1529
|
rb_scan_args(argc, argv, "0&", &block);
|
|
1084
1530
|
|
|
1085
|
-
|
|
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
|
+
}
|
|
1086
1536
|
|
|
1087
1537
|
timeout_milliseconds = cCurlMutiDefaulttimeout;
|
|
1088
1538
|
|
|
@@ -1099,9 +1549,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1099
1549
|
// There are no more messages to handle by curl and we can run the ruby block
|
|
1100
1550
|
// passed to perform method.
|
|
1101
1551
|
// When the block completes curl will resume.
|
|
1102
|
-
|
|
1103
|
-
rb_funcall(block, rb_intern("call"), 1, self);
|
|
1104
|
-
}
|
|
1552
|
+
rb_curl_multi_yield_if_given(self, block);
|
|
1105
1553
|
|
|
1106
1554
|
do {
|
|
1107
1555
|
while (rbcm->running) {
|
|
@@ -1119,7 +1567,12 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1119
1567
|
if (timeout_milliseconds == 0) { /* no delay */
|
|
1120
1568
|
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
|
1121
1569
|
rb_curl_multi_read_info( self, rbcm->handle );
|
|
1122
|
-
|
|
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
|
|
1123
1576
|
continue;
|
|
1124
1577
|
}
|
|
1125
1578
|
|
|
@@ -1164,7 +1617,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1164
1617
|
/* Process pending transfers after waiting */
|
|
1165
1618
|
rb_curl_multi_run(self, rbcm->handle, &(rbcm->running));
|
|
1166
1619
|
rb_curl_multi_read_info(self, rbcm->handle);
|
|
1167
|
-
|
|
1620
|
+
rb_curl_multi_yield_if_given(self, block);
|
|
1168
1621
|
}
|
|
1169
1622
|
#else
|
|
1170
1623
|
|
|
@@ -1183,7 +1636,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1183
1636
|
|
|
1184
1637
|
if (maxfd == -1) {
|
|
1185
1638
|
/* libcurl recommends sleeping for 100ms */
|
|
1186
|
-
#
|
|
1639
|
+
#ifdef HAVE_RB_THREAD_FD_SELECT
|
|
1187
1640
|
struct timeval tv_sleep = tv_100ms;
|
|
1188
1641
|
rb_thread_fd_select(0, NULL, NULL, NULL, &tv_sleep);
|
|
1189
1642
|
#else
|
|
@@ -1191,7 +1644,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1191
1644
|
#endif
|
|
1192
1645
|
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
|
1193
1646
|
rb_curl_multi_read_info( self, rbcm->handle );
|
|
1194
|
-
|
|
1647
|
+
rb_curl_multi_yield_if_given(self, block);
|
|
1195
1648
|
continue;
|
|
1196
1649
|
}
|
|
1197
1650
|
|
|
@@ -1202,7 +1655,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1202
1655
|
#endif
|
|
1203
1656
|
|
|
1204
1657
|
|
|
1205
|
-
#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))
|
|
1206
1659
|
fdset_args.maxfd = maxfd+1;
|
|
1207
1660
|
fdset_args.fdread = &fdread;
|
|
1208
1661
|
fdset_args.fdwrite = &fdwrite;
|
|
@@ -1210,7 +1663,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1210
1663
|
fdset_args.tv = &tv;
|
|
1211
1664
|
#endif
|
|
1212
1665
|
|
|
1213
|
-
#
|
|
1666
|
+
#ifdef HAVE_RB_THREAD_FD_SELECT
|
|
1214
1667
|
/* Prefer scheduler-aware waiting when available. Build rb_fdset_t sets. */
|
|
1215
1668
|
{
|
|
1216
1669
|
rb_fdset_t rfds, wfds, efds;
|
|
@@ -1242,7 +1695,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1242
1695
|
#elif HAVE_RB_THREAD_BLOCKING_REGION
|
|
1243
1696
|
rc = rb_thread_blocking_region(curb_select, &fdset_args, RUBY_UBF_IO, 0);
|
|
1244
1697
|
#else
|
|
1245
|
-
rc =
|
|
1698
|
+
rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
|
|
1246
1699
|
#endif
|
|
1247
1700
|
|
|
1248
1701
|
#ifdef _WIN32
|
|
@@ -1261,7 +1714,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1261
1714
|
default: /* action */
|
|
1262
1715
|
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
|
1263
1716
|
rb_curl_multi_read_info( self, rbcm->handle );
|
|
1264
|
-
|
|
1717
|
+
rb_curl_multi_yield_if_given(self, block);
|
|
1265
1718
|
break;
|
|
1266
1719
|
}
|
|
1267
1720
|
#endif /* disabled curl_multi_wait: use fdsets */
|
|
@@ -1270,9 +1723,9 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1270
1723
|
} while( rbcm->running );
|
|
1271
1724
|
|
|
1272
1725
|
rb_curl_multi_read_info( self, rbcm->handle );
|
|
1273
|
-
|
|
1726
|
+
rb_curl_multi_yield_if_given(self, block);
|
|
1274
1727
|
if (cCurlMutiAutoClose == 1) {
|
|
1275
|
-
rb_funcall(self, rb_intern("
|
|
1728
|
+
rb_funcall(self, rb_intern("_autoclose"), 0);
|
|
1276
1729
|
}
|
|
1277
1730
|
return Qtrue;
|
|
1278
1731
|
}
|
|
@@ -1286,7 +1739,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1286
1739
|
*/
|
|
1287
1740
|
VALUE ruby_curl_multi_close(VALUE self) {
|
|
1288
1741
|
ruby_curl_multi *rbcm;
|
|
1289
|
-
|
|
1742
|
+
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
1290
1743
|
rb_curl_multi_detach_all(rbcm);
|
|
1291
1744
|
|
|
1292
1745
|
if (rbcm->handle) {
|
|
@@ -1294,7 +1747,19 @@ VALUE ruby_curl_multi_close(VALUE self) {
|
|
|
1294
1747
|
rbcm->handle = NULL;
|
|
1295
1748
|
}
|
|
1296
1749
|
|
|
1297
|
-
|
|
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
|
+
|
|
1298
1763
|
return self;
|
|
1299
1764
|
}
|
|
1300
1765
|
|
|
@@ -1317,28 +1782,33 @@ static void curl_multi_mark(void *ptr) {
|
|
|
1317
1782
|
/* =================== INIT LIB =====================*/
|
|
1318
1783
|
void init_curb_multi() {
|
|
1319
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");
|
|
1320
1787
|
cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject);
|
|
1321
1788
|
|
|
1322
|
-
|
|
1789
|
+
rb_define_alloc_func(cCurlMulti, ruby_curl_multi_alloc);
|
|
1323
1790
|
|
|
1324
1791
|
/* Class methods */
|
|
1325
|
-
rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0);
|
|
1326
1792
|
rb_define_singleton_method(cCurlMulti, "default_timeout=", ruby_curl_multi_set_default_timeout, 1);
|
|
1327
1793
|
rb_define_singleton_method(cCurlMulti, "default_timeout", ruby_curl_multi_get_default_timeout, 0);
|
|
1328
1794
|
rb_define_singleton_method(cCurlMulti, "autoclose=", ruby_curl_multi_set_autoclose, 1);
|
|
1329
1795
|
rb_define_singleton_method(cCurlMulti, "autoclose", ruby_curl_multi_get_autoclose, 0);
|
|
1330
1796
|
/* Instance methods */
|
|
1797
|
+
rb_define_method(cCurlMulti, "initialize", ruby_curl_multi_initialize, 0);
|
|
1331
1798
|
rb_define_method(cCurlMulti, "max_connects=", ruby_curl_multi_max_connects, 1);
|
|
1332
1799
|
rb_define_method(cCurlMulti, "max_host_connections=", ruby_curl_multi_max_host_connections, 1);
|
|
1333
1800
|
rb_define_method(cCurlMulti, "pipeline=", ruby_curl_multi_pipeline, 1);
|
|
1334
1801
|
rb_define_method(cCurlMulti, "_add", ruby_curl_multi_add, 1);
|
|
1335
1802
|
rb_define_method(cCurlMulti, "_remove", ruby_curl_multi_remove, 1);
|
|
1336
|
-
/*
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
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
|
+
*/
|
|
1341
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);
|
|
1342
1811
|
#endif
|
|
1343
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);
|
|
1344
1814
|
}
|