curb 1.3.3 → 1.3.5
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 +18 -1
- data/ext/curb.h +3 -3
- data/ext/curb_multi.c +310 -68
- data/ext/extconf.rb +1 -0
- data/tests/tc_curl_multi.rb +35 -0
- data/tests/tc_fiber_scheduler.rb +24 -6
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a502fb4afad21a24a20302b7522b616b08afbbe3d4b5b2a3eeee46d83b53c50d
|
|
4
|
+
data.tar.gz: 5e68fd3f3380f2045dfc6d4e8e157ebbb9fc56e4b76601574c92a755665eeef2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 940f343ad1d926ddcf27b994bca191e131176e766dce3d7f211f9ed5b28917ffb9ebfa5b8f71247ac8ca8a0b916e6565b60fb2ae57a964770b3273d1bd7d5d69
|
|
7
|
+
data.tar.gz: 77a44dd831eea50e786d4b4871c2f1702e3bee2c74d0484f7b0812a91d19d4bcdd79c12e20dce7cd8ffcbaf0e06ae77cc6cc7734b7c71fa2feabc636af82b0d8
|
data/Rakefile
CHANGED
|
@@ -106,7 +106,24 @@ else
|
|
|
106
106
|
task :alltests => [:unittests, :bugtests]
|
|
107
107
|
end
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
ruby_memcheck_config = { binary_name: 'curb_core' }
|
|
110
|
+
|
|
111
|
+
if RUBY_ENGINE == 'ruby' && RUBY_VERSION == '4.0.4'
|
|
112
|
+
# Ruby 4.0.4 reports fiber/block-handler VM stack accesses under Valgrind.
|
|
113
|
+
# Keep reporting errors that originate in curb_core, but filter Ruby-side noise.
|
|
114
|
+
ruby_memcheck_config[:filter_all_errors] = true
|
|
115
|
+
if RubyMemcheck::Configuration.instance_method(:initialize).parameters.any? { |type, name|
|
|
116
|
+
type == :key && name == :use_only_ruby_free_at_exit
|
|
117
|
+
}
|
|
118
|
+
ruby_memcheck_config[:use_only_ruby_free_at_exit] = false
|
|
119
|
+
end
|
|
120
|
+
ruby_memcheck_config[:skipped_ruby_functions] =
|
|
121
|
+
RubyMemcheck::Configuration::DEFAULT_SKIPPED_RUBY_FUNCTIONS + [
|
|
122
|
+
/\Arb_vm_frame_block_handler\z/
|
|
123
|
+
]
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
RubyMemcheck.config(**ruby_memcheck_config)
|
|
110
127
|
namespace :test do
|
|
111
128
|
RubyMemcheck::TestTask.new(valgrind: :compile) do|t|
|
|
112
129
|
t.test_files = FileList['tests/tc_*.rb']
|
data/ext/curb.h
CHANGED
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
#include "curb_macros.h"
|
|
29
29
|
|
|
30
30
|
// These should be managed from the Rake 'release' task.
|
|
31
|
-
#define CURB_VERSION "1.3.
|
|
32
|
-
#define CURB_VER_NUM
|
|
31
|
+
#define CURB_VERSION "1.3.5"
|
|
32
|
+
#define CURB_VER_NUM 1035
|
|
33
33
|
#define CURB_VER_MAJ 1
|
|
34
34
|
#define CURB_VER_MIN 3
|
|
35
|
-
#define CURB_VER_MIC
|
|
35
|
+
#define CURB_VER_MIC 5
|
|
36
36
|
#define CURB_VER_PATCH 0
|
|
37
37
|
|
|
38
38
|
|
data/ext/curb_multi.c
CHANGED
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
|
|
29
29
|
#include <errno.h>
|
|
30
30
|
#include <fcntl.h>
|
|
31
|
+
#ifndef _WIN32
|
|
32
|
+
#include <sys/time.h>
|
|
33
|
+
#include <time.h>
|
|
34
|
+
#endif
|
|
31
35
|
#include <stdint.h>
|
|
32
36
|
#include <stdarg.h>
|
|
33
37
|
|
|
@@ -70,6 +74,7 @@ extern VALUE mCurl;
|
|
|
70
74
|
static VALUE idCall;
|
|
71
75
|
static ID id_deferred_exception_ivar;
|
|
72
76
|
static ID id_deferred_exception_source_id_ivar;
|
|
77
|
+
static ID id_socket_io_cache_ivar;
|
|
73
78
|
|
|
74
79
|
#ifdef RDOC_NEVER_DEFINED
|
|
75
80
|
mCurl = rb_define_module("Curl");
|
|
@@ -1070,10 +1075,28 @@ static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_runnin
|
|
|
1070
1075
|
/* ---- socket-action implementation (scheduler-friendly) ---- */
|
|
1071
1076
|
typedef struct {
|
|
1072
1077
|
st_table *sock_map; /* key: int fd, value: int 'what' (CURL_POLL_*) */
|
|
1073
|
-
long
|
|
1078
|
+
long long timeout_deadline_ms; /* absolute deadline for CURL_SOCKET_TIMEOUT */
|
|
1074
1079
|
VALUE io_cache; /* fd -> IO wrapper for fiber-scheduler waits */
|
|
1075
1080
|
} multi_socket_ctx;
|
|
1076
1081
|
|
|
1082
|
+
static long long multi_socket_current_time_ms(void) {
|
|
1083
|
+
#if defined(CLOCK_MONOTONIC)
|
|
1084
|
+
struct timespec ts;
|
|
1085
|
+
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
|
|
1086
|
+
return ((long long)ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
|
|
1087
|
+
}
|
|
1088
|
+
#endif
|
|
1089
|
+
|
|
1090
|
+
struct timeval tv;
|
|
1091
|
+
gettimeofday(&tv, NULL);
|
|
1092
|
+
return ((long long)tv.tv_sec * 1000) + (tv.tv_usec / 1000);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
static int multi_socket_timer_due(multi_socket_ctx *ctx) {
|
|
1096
|
+
return ctx && ctx->timeout_deadline_ms >= 0 &&
|
|
1097
|
+
multi_socket_current_time_ms() >= ctx->timeout_deadline_ms;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1077
1100
|
#if CURB_SOCKET_DEBUG
|
|
1078
1101
|
static void curb_debugf(const char *fmt, ...) {
|
|
1079
1102
|
va_list ap;
|
|
@@ -1118,6 +1141,21 @@ static VALUE fiber_io_wait_protected(VALUE argp) {
|
|
|
1118
1141
|
}
|
|
1119
1142
|
#endif
|
|
1120
1143
|
|
|
1144
|
+
#if defined(HAVE_RB_FIBER_SCHEDULER_IO_SELECT) && defined(HAVE_RB_FIBER_SCHEDULER_CURRENT)
|
|
1145
|
+
struct fiber_io_select_args {
|
|
1146
|
+
VALUE scheduler;
|
|
1147
|
+
VALUE readables;
|
|
1148
|
+
VALUE writables;
|
|
1149
|
+
VALUE exceptables;
|
|
1150
|
+
VALUE timeout;
|
|
1151
|
+
};
|
|
1152
|
+
|
|
1153
|
+
static VALUE fiber_io_select_protected(VALUE argp) {
|
|
1154
|
+
struct fiber_io_select_args *a = (struct fiber_io_select_args *)argp;
|
|
1155
|
+
return rb_fiber_scheduler_io_select(a->scheduler, a->readables, a->writables, a->exceptables, a->timeout);
|
|
1156
|
+
}
|
|
1157
|
+
#endif
|
|
1158
|
+
|
|
1121
1159
|
#if defined(RB_INTEGER_TYPE_P)
|
|
1122
1160
|
#define CURB_INTEGER_P(value) RB_INTEGER_TYPE_P(value)
|
|
1123
1161
|
#else
|
|
@@ -1187,6 +1225,11 @@ static int multi_socket_cb(CURL *easy, curl_socket_t s, int what, void *userp, v
|
|
|
1187
1225
|
#endif
|
|
1188
1226
|
} else {
|
|
1189
1227
|
/* store current interest mask for this fd */
|
|
1228
|
+
st_data_t key = (st_data_t)fd;
|
|
1229
|
+
st_data_t old_what;
|
|
1230
|
+
if (st_lookup(ctx->sock_map, key, &old_what) && (int)old_what != what && !NIL_P(ctx->io_cache)) {
|
|
1231
|
+
rb_hash_delete(ctx->io_cache, INT2NUM(fd));
|
|
1232
|
+
}
|
|
1190
1233
|
st_insert(ctx->sock_map, (st_data_t)fd, (st_data_t)what);
|
|
1191
1234
|
#if CURB_SOCKET_DEBUG
|
|
1192
1235
|
{
|
|
@@ -1201,7 +1244,9 @@ static int multi_socket_cb(CURL *easy, curl_socket_t s, int what, void *userp, v
|
|
|
1201
1244
|
static int multi_timer_cb(CURLM *multi, long timeout_ms, void *userp) {
|
|
1202
1245
|
(void)multi;
|
|
1203
1246
|
multi_socket_ctx *ctx = (multi_socket_ctx *)userp;
|
|
1204
|
-
if (ctx)
|
|
1247
|
+
if (ctx) {
|
|
1248
|
+
ctx->timeout_deadline_ms = timeout_ms < 0 ? -1 : multi_socket_current_time_ms() + timeout_ms;
|
|
1249
|
+
}
|
|
1205
1250
|
curb_debugf("[curb.socket] timer_cb timeout_ms=%ld", timeout_ms);
|
|
1206
1251
|
return 0;
|
|
1207
1252
|
}
|
|
@@ -1225,18 +1270,32 @@ static void rb_fdset_from_sockmap(st_table *map, rb_fdset_t *rfds, rb_fdset_t *w
|
|
|
1225
1270
|
*maxfd_out = a.maxfd;
|
|
1226
1271
|
}
|
|
1227
1272
|
|
|
1228
|
-
struct
|
|
1229
|
-
|
|
1273
|
+
struct ready_fd {
|
|
1274
|
+
int fd;
|
|
1275
|
+
int flags;
|
|
1276
|
+
};
|
|
1277
|
+
|
|
1278
|
+
struct collect_ready_fd_args {
|
|
1279
|
+
rb_fdset_t *r;
|
|
1280
|
+
rb_fdset_t *w;
|
|
1281
|
+
rb_fdset_t *e;
|
|
1282
|
+
struct ready_fd *fds;
|
|
1283
|
+
int capacity;
|
|
1284
|
+
int count;
|
|
1285
|
+
};
|
|
1286
|
+
|
|
1287
|
+
static int collect_ready_fd_i(st_data_t key, st_data_t val, st_data_t argp) {
|
|
1230
1288
|
(void)val;
|
|
1231
|
-
struct
|
|
1289
|
+
struct collect_ready_fd_args *a = (struct collect_ready_fd_args *)argp;
|
|
1232
1290
|
int fd = (int)key;
|
|
1233
1291
|
int flags = 0;
|
|
1234
|
-
if (rb_fd_isset(fd,
|
|
1235
|
-
if (rb_fd_isset(fd,
|
|
1236
|
-
if (rb_fd_isset(fd,
|
|
1237
|
-
if (flags) {
|
|
1238
|
-
|
|
1239
|
-
|
|
1292
|
+
if (rb_fd_isset(fd, a->r)) flags |= CURL_CSELECT_IN;
|
|
1293
|
+
if (rb_fd_isset(fd, a->w)) flags |= CURL_CSELECT_OUT;
|
|
1294
|
+
if (rb_fd_isset(fd, a->e)) flags |= CURL_CSELECT_ERR;
|
|
1295
|
+
if (flags && a->count < a->capacity) {
|
|
1296
|
+
a->fds[a->count].fd = fd;
|
|
1297
|
+
a->fds[a->count].flags = flags;
|
|
1298
|
+
a->count++;
|
|
1240
1299
|
}
|
|
1241
1300
|
return ST_CONTINUE;
|
|
1242
1301
|
}
|
|
@@ -1258,23 +1317,103 @@ static int st_count_i(st_data_t k, st_data_t v, st_data_t argp) {
|
|
|
1258
1317
|
return ST_CONTINUE;
|
|
1259
1318
|
}
|
|
1260
1319
|
|
|
1261
|
-
static
|
|
1320
|
+
static const char *multi_socket_io_mode_for_curl_poll(int what) {
|
|
1321
|
+
if (what == CURL_POLL_IN) return "r";
|
|
1322
|
+
if (what == CURL_POLL_OUT) return "w";
|
|
1323
|
+
return "r+";
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
static VALUE multi_socket_io_for_fd(multi_socket_ctx *ctx, int fd, int what) {
|
|
1262
1327
|
VALUE key = INT2NUM(fd);
|
|
1263
1328
|
VALUE io = rb_hash_aref(ctx->io_cache, key);
|
|
1264
1329
|
if (NIL_P(io)) {
|
|
1265
|
-
io = rb_funcall(rb_cIO, rb_intern("for_fd"), 2, key, rb_str_new_cstr(
|
|
1330
|
+
io = rb_funcall(rb_cIO, rb_intern("for_fd"), 2, key, rb_str_new_cstr(multi_socket_io_mode_for_curl_poll(what)));
|
|
1266
1331
|
rb_funcall(io, rb_intern("autoclose="), 1, Qfalse);
|
|
1267
1332
|
rb_hash_aset(ctx->io_cache, key, io);
|
|
1268
1333
|
}
|
|
1269
1334
|
return io;
|
|
1270
1335
|
}
|
|
1271
1336
|
|
|
1272
|
-
struct io_for_fd_args { multi_socket_ctx *ctx; int fd; };
|
|
1337
|
+
struct io_for_fd_args { multi_socket_ctx *ctx; int fd; int what; };
|
|
1273
1338
|
static VALUE multi_socket_io_for_fd_protected(VALUE argp) {
|
|
1274
1339
|
struct io_for_fd_args *a = (struct io_for_fd_args *)argp;
|
|
1275
|
-
return multi_socket_io_for_fd(a->ctx, a->fd);
|
|
1340
|
+
return multi_socket_io_for_fd(a->ctx, a->fd, a->what);
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
#if defined(HAVE_RB_FIBER_SCHEDULER_IO_SELECT) && defined(HAVE_RB_FIBER_SCHEDULER_CURRENT)
|
|
1344
|
+
struct build_io_select_arrays_args {
|
|
1345
|
+
multi_socket_ctx *ctx;
|
|
1346
|
+
VALUE readables;
|
|
1347
|
+
VALUE writables;
|
|
1348
|
+
VALUE exceptables;
|
|
1349
|
+
int failed;
|
|
1350
|
+
};
|
|
1351
|
+
|
|
1352
|
+
static int build_io_select_arrays_i(st_data_t key, st_data_t val, st_data_t argp) {
|
|
1353
|
+
struct build_io_select_arrays_args *a = (struct build_io_select_arrays_args *)argp;
|
|
1354
|
+
int fd = (int)key;
|
|
1355
|
+
int what = (int)val;
|
|
1356
|
+
struct io_for_fd_args io_args = { a->ctx, fd, what };
|
|
1357
|
+
int io_state = 0;
|
|
1358
|
+
VALUE io;
|
|
1359
|
+
|
|
1360
|
+
if (!multi_socket_fd_valid_p(fd)) {
|
|
1361
|
+
a->failed = 1;
|
|
1362
|
+
return ST_STOP;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
io = rb_protect(multi_socket_io_for_fd_protected, (VALUE)&io_args, &io_state);
|
|
1366
|
+
if (io_state || NIL_P(io)) {
|
|
1367
|
+
if (io_state) {
|
|
1368
|
+
#if CURB_SOCKET_DEBUG
|
|
1369
|
+
VALUE err = rb_errinfo();
|
|
1370
|
+
VALUE msg = rb_obj_as_string(err);
|
|
1371
|
+
curb_debugf("[curb.socket] IO.for_fd failed: %s: %s", rb_obj_classname(err), StringValueCStr(msg));
|
|
1372
|
+
#endif
|
|
1373
|
+
rb_set_errinfo(Qnil);
|
|
1374
|
+
}
|
|
1375
|
+
a->failed = 1;
|
|
1376
|
+
return ST_STOP;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
if (what == CURL_POLL_IN || what == CURL_POLL_INOUT) rb_ary_push(a->readables, io);
|
|
1380
|
+
if (what == CURL_POLL_OUT || what == CURL_POLL_INOUT) rb_ary_push(a->writables, io);
|
|
1381
|
+
rb_ary_push(a->exceptables, io);
|
|
1382
|
+
|
|
1383
|
+
return ST_CONTINUE;
|
|
1276
1384
|
}
|
|
1277
1385
|
|
|
1386
|
+
struct collect_io_select_ready_args {
|
|
1387
|
+
multi_socket_ctx *ctx;
|
|
1388
|
+
VALUE readables;
|
|
1389
|
+
VALUE writables;
|
|
1390
|
+
VALUE exceptables;
|
|
1391
|
+
struct ready_fd *fds;
|
|
1392
|
+
int capacity;
|
|
1393
|
+
int count;
|
|
1394
|
+
};
|
|
1395
|
+
|
|
1396
|
+
static int collect_io_select_ready_i(st_data_t key, st_data_t val, st_data_t argp) {
|
|
1397
|
+
(void)val;
|
|
1398
|
+
struct collect_io_select_ready_args *a = (struct collect_io_select_ready_args *)argp;
|
|
1399
|
+
VALUE io = rb_hash_aref(a->ctx->io_cache, INT2NUM((int)key));
|
|
1400
|
+
int flags = 0;
|
|
1401
|
+
|
|
1402
|
+
if (NIL_P(io)) return ST_CONTINUE;
|
|
1403
|
+
if (RTEST(rb_ary_includes(a->readables, io))) flags |= CURL_CSELECT_IN;
|
|
1404
|
+
if (RTEST(rb_ary_includes(a->writables, io))) flags |= CURL_CSELECT_OUT;
|
|
1405
|
+
if (RTEST(rb_ary_includes(a->exceptables, io))) flags |= CURL_CSELECT_ERR;
|
|
1406
|
+
|
|
1407
|
+
if (flags && a->count < a->capacity) {
|
|
1408
|
+
a->fds[a->count].fd = (int)key;
|
|
1409
|
+
a->fds[a->count].flags = flags;
|
|
1410
|
+
a->count++;
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
return ST_CONTINUE;
|
|
1414
|
+
}
|
|
1415
|
+
#endif
|
|
1416
|
+
|
|
1278
1417
|
static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_socket_ctx *ctx, VALUE block) {
|
|
1279
1418
|
/* prime the state: let libcurl act on timeouts to setup sockets */
|
|
1280
1419
|
CURLMcode mrc = curl_multi_socket_action(rbcm->handle, CURL_SOCKET_TIMEOUT, 0, &rbcm->running);
|
|
@@ -1285,17 +1424,24 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
1285
1424
|
|
|
1286
1425
|
while (rbcm->running) {
|
|
1287
1426
|
struct timeval tv = {0, 0};
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
if (
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1427
|
+
long wait_ms = cCurlMutiDefaulttimeout;
|
|
1428
|
+
|
|
1429
|
+
if (multi_socket_timer_due(ctx)) {
|
|
1430
|
+
mrc = curl_multi_socket_action(rbcm->handle, CURL_SOCKET_TIMEOUT, 0, &rbcm->running);
|
|
1431
|
+
curb_debugf("[curb.socket] socket_action timeout(due) -> mrc=%d running=%d", mrc, rbcm->running);
|
|
1432
|
+
if (mrc != CURLM_OK) raise_curl_multi_error_exception(mrc);
|
|
1433
|
+
rb_curl_multi_read_info(self, rbcm->handle);
|
|
1434
|
+
rb_curl_multi_yield_if_given(self, block);
|
|
1435
|
+
continue;
|
|
1297
1436
|
}
|
|
1298
1437
|
|
|
1438
|
+
if (ctx->timeout_deadline_ms >= 0) {
|
|
1439
|
+
long long remaining_ms = ctx->timeout_deadline_ms - multi_socket_current_time_ms();
|
|
1440
|
+
if (remaining_ms < wait_ms) wait_ms = remaining_ms < 0 ? 0 : (long)remaining_ms;
|
|
1441
|
+
}
|
|
1442
|
+
tv.tv_sec = wait_ms / 1000;
|
|
1443
|
+
tv.tv_usec = (wait_ms % 1000) * 1000;
|
|
1444
|
+
|
|
1299
1445
|
/* Find a representative fd to wait on (if any). */
|
|
1300
1446
|
int wait_fd = -1;
|
|
1301
1447
|
int wait_what = 0;
|
|
@@ -1319,37 +1465,113 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
1319
1465
|
int any_ready = 0;
|
|
1320
1466
|
int ready_flags = 0;
|
|
1321
1467
|
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1468
|
+
int handled_wait = 0;
|
|
1469
|
+
if (count_tracked > 1) {
|
|
1470
|
+
#if defined(HAVE_RB_FIBER_SCHEDULER_IO_SELECT) && defined(HAVE_RB_FIBER_SCHEDULER_CURRENT)
|
|
1471
|
+
{
|
|
1472
|
+
VALUE scheduler = rb_fiber_scheduler_current();
|
|
1473
|
+
if (scheduler != Qnil) {
|
|
1474
|
+
VALUE readables = rb_ary_new();
|
|
1475
|
+
VALUE writables = rb_ary_new();
|
|
1476
|
+
VALUE exceptables = rb_ary_new();
|
|
1477
|
+
struct build_io_select_arrays_args build_args = { ctx, readables, writables, exceptables, 0 };
|
|
1478
|
+
st_foreach(ctx->sock_map, build_io_select_arrays_i, (st_data_t)&build_args);
|
|
1479
|
+
if (!build_args.failed) {
|
|
1480
|
+
double timeout_s = (double)tv.tv_sec + ((double)tv.tv_usec / 1e6);
|
|
1481
|
+
VALUE timeout = rb_float_new(timeout_s);
|
|
1482
|
+
struct fiber_io_select_args select_args = { scheduler, readables, writables, exceptables, timeout };
|
|
1483
|
+
int state = 0;
|
|
1484
|
+
VALUE ready = rb_protect(fiber_io_select_protected, (VALUE)&select_args, &state);
|
|
1485
|
+
if (state) {
|
|
1486
|
+
#if CURB_SOCKET_DEBUG
|
|
1487
|
+
VALUE err = rb_errinfo();
|
|
1488
|
+
VALUE msg = rb_obj_as_string(err);
|
|
1489
|
+
curb_debugf("[curb.socket] scheduler io_select failed: %s: %s", rb_obj_classname(err), StringValueCStr(msg));
|
|
1490
|
+
#endif
|
|
1491
|
+
rb_set_errinfo(Qnil);
|
|
1492
|
+
} else {
|
|
1493
|
+
handled_wait = 1;
|
|
1494
|
+
any_ready = RB_TYPE_P(ready, T_ARRAY);
|
|
1495
|
+
did_timeout = !any_ready && multi_socket_timer_due(ctx);
|
|
1496
|
+
if (any_ready) {
|
|
1497
|
+
VALUE ready_readables = rb_ary_entry(ready, 0);
|
|
1498
|
+
VALUE ready_writables = rb_ary_entry(ready, 1);
|
|
1499
|
+
VALUE ready_exceptables = rb_ary_entry(ready, 2);
|
|
1500
|
+
struct ready_fd *ready_fds = ALLOC_N(struct ready_fd, count_tracked);
|
|
1501
|
+
struct collect_io_select_ready_args d;
|
|
1502
|
+
int i;
|
|
1503
|
+
if (!RB_TYPE_P(ready_readables, T_ARRAY)) ready_readables = rb_ary_new();
|
|
1504
|
+
if (!RB_TYPE_P(ready_writables, T_ARRAY)) ready_writables = rb_ary_new();
|
|
1505
|
+
if (!RB_TYPE_P(ready_exceptables, T_ARRAY)) ready_exceptables = rb_ary_new();
|
|
1506
|
+
d.ctx = ctx;
|
|
1507
|
+
d.readables = ready_readables;
|
|
1508
|
+
d.writables = ready_writables;
|
|
1509
|
+
d.exceptables = ready_exceptables;
|
|
1510
|
+
d.fds = ready_fds;
|
|
1511
|
+
d.capacity = count_tracked;
|
|
1512
|
+
d.count = 0;
|
|
1513
|
+
st_foreach(ctx->sock_map, collect_io_select_ready_i, (st_data_t)&d);
|
|
1514
|
+
any_ready = (d.count > 0);
|
|
1515
|
+
did_timeout = !any_ready && multi_socket_timer_due(ctx);
|
|
1516
|
+
for (i = 0; i < d.count; i++) {
|
|
1517
|
+
mrc = curl_multi_socket_action(rbcm->handle, (curl_socket_t)d.fds[i].fd, d.fds[i].flags, &rbcm->running);
|
|
1518
|
+
if (mrc != CURLM_OK) {
|
|
1519
|
+
xfree(ready_fds);
|
|
1520
|
+
raise_curl_multi_error_exception(mrc);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
xfree(ready_fds);
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
#endif
|
|
1530
|
+
if (!handled_wait) {
|
|
1531
|
+
/* Multi-fd wait using scheduler-aware rb_thread_fd_select. */
|
|
1532
|
+
rb_fdset_t rfds, wfds, efds;
|
|
1533
|
+
rb_fd_init(&rfds); rb_fd_init(&wfds); rb_fd_init(&efds);
|
|
1534
|
+
int maxfd = -1;
|
|
1535
|
+
rb_fdset_from_sockmap(ctx->sock_map, &rfds, &wfds, &efds, &maxfd);
|
|
1536
|
+
int rc = rb_thread_fd_select(maxfd + 1, &rfds, &wfds, &efds, &tv);
|
|
1537
|
+
curb_debugf("[curb.socket] rb_thread_fd_select(multi) rc=%d maxfd=%d", rc, maxfd);
|
|
1538
|
+
if (rc < 0) {
|
|
1539
|
+
rb_fd_term(&rfds); rb_fd_term(&wfds); rb_fd_term(&efds);
|
|
1540
|
+
if (errno != EINTR) rb_raise(rb_eRuntimeError, "select(): %s", strerror(errno));
|
|
1541
|
+
continue;
|
|
1542
|
+
}
|
|
1543
|
+
any_ready = (rc > 0);
|
|
1544
|
+
did_timeout = (rc == 0 && multi_socket_timer_due(ctx));
|
|
1545
|
+
if (any_ready) {
|
|
1546
|
+
struct ready_fd *ready_fds = ALLOC_N(struct ready_fd, count_tracked);
|
|
1547
|
+
struct collect_ready_fd_args d;
|
|
1548
|
+
int i;
|
|
1549
|
+
d.r = &rfds;
|
|
1550
|
+
d.w = &wfds;
|
|
1551
|
+
d.e = &efds;
|
|
1552
|
+
d.fds = ready_fds;
|
|
1553
|
+
d.capacity = count_tracked;
|
|
1554
|
+
d.count = 0;
|
|
1555
|
+
st_foreach(ctx->sock_map, collect_ready_fd_i, (st_data_t)&d);
|
|
1556
|
+
for (i = 0; i < d.count; i++) {
|
|
1557
|
+
mrc = curl_multi_socket_action(rbcm->handle, (curl_socket_t)d.fds[i].fd, d.fds[i].flags, &rbcm->running);
|
|
1558
|
+
if (mrc != CURLM_OK) {
|
|
1559
|
+
xfree(ready_fds);
|
|
1560
|
+
rb_fd_term(&rfds); rb_fd_term(&wfds); rb_fd_term(&efds);
|
|
1561
|
+
raise_curl_multi_error_exception(mrc);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
xfree(ready_fds);
|
|
1565
|
+
}
|
|
1566
|
+
rb_fd_term(&rfds); rb_fd_term(&wfds); rb_fd_term(&efds);
|
|
1567
|
+
handled_wait = 1;
|
|
1568
|
+
}
|
|
1569
|
+
} else if (count_tracked == 1) {
|
|
1349
1570
|
#if defined(HAVE_RB_FIBER_SCHEDULER_IO_WAIT) && defined(HAVE_RB_FIBER_SCHEDULER_CURRENT)
|
|
1350
1571
|
{
|
|
1351
1572
|
VALUE scheduler = rb_fiber_scheduler_current();
|
|
1352
1573
|
if (scheduler != Qnil) {
|
|
1574
|
+
int scheduler_wait_handled = 0;
|
|
1353
1575
|
int events = 0;
|
|
1354
1576
|
if (wait_fd >= 0) {
|
|
1355
1577
|
events = multi_socket_wait_events_for_curl_poll(wait_what);
|
|
@@ -1362,30 +1584,42 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
1362
1584
|
#else
|
|
1363
1585
|
rb_thread_wait_for(tv);
|
|
1364
1586
|
#endif
|
|
1365
|
-
did_timeout =
|
|
1587
|
+
did_timeout = multi_socket_timer_due(ctx);
|
|
1588
|
+
scheduler_wait_handled = 1;
|
|
1366
1589
|
} else if (!multi_socket_fd_valid_p(wait_fd)) {
|
|
1367
1590
|
multi_socket_forget_fd(ctx, wait_fd);
|
|
1368
1591
|
did_timeout = 1;
|
|
1592
|
+
scheduler_wait_handled = 1;
|
|
1369
1593
|
} else {
|
|
1370
|
-
struct io_for_fd_args io_args = { ctx, wait_fd };
|
|
1594
|
+
struct io_for_fd_args io_args = { ctx, wait_fd, wait_what };
|
|
1371
1595
|
int io_state = 0;
|
|
1372
1596
|
VALUE io = rb_protect(multi_socket_io_for_fd_protected, (VALUE)&io_args, &io_state);
|
|
1373
1597
|
if (io_state || NIL_P(io)) {
|
|
1374
|
-
if (io_state)
|
|
1375
|
-
|
|
1376
|
-
|
|
1598
|
+
if (io_state) {
|
|
1599
|
+
#if CURB_SOCKET_DEBUG
|
|
1600
|
+
VALUE err = rb_errinfo();
|
|
1601
|
+
VALUE msg = rb_obj_as_string(err);
|
|
1602
|
+
curb_debugf("[curb.socket] IO.for_fd failed: %s: %s", rb_obj_classname(err), StringValueCStr(msg));
|
|
1603
|
+
#endif
|
|
1604
|
+
rb_set_errinfo(Qnil);
|
|
1605
|
+
}
|
|
1377
1606
|
any_ready = 0;
|
|
1378
1607
|
} else {
|
|
1379
1608
|
struct fiber_io_wait_args args = { scheduler, io, INT2NUM(events), timeout };
|
|
1380
1609
|
int state = 0;
|
|
1381
1610
|
VALUE ready = rb_protect(fiber_io_wait_protected, (VALUE)&args, &state);
|
|
1382
1611
|
if (state) {
|
|
1612
|
+
#if CURB_SOCKET_DEBUG
|
|
1613
|
+
VALUE err = rb_errinfo();
|
|
1614
|
+
VALUE msg = rb_obj_as_string(err);
|
|
1615
|
+
curb_debugf("[curb.socket] scheduler io_wait failed: %s: %s", rb_obj_classname(err), StringValueCStr(msg));
|
|
1616
|
+
#endif
|
|
1383
1617
|
rb_set_errinfo(Qnil);
|
|
1384
|
-
did_timeout = 1;
|
|
1385
1618
|
any_ready = 0;
|
|
1386
1619
|
} else {
|
|
1620
|
+
scheduler_wait_handled = 1;
|
|
1387
1621
|
any_ready = (ready != Qfalse && !NIL_P(ready));
|
|
1388
|
-
did_timeout = !any_ready;
|
|
1622
|
+
did_timeout = !any_ready && multi_socket_timer_due(ctx);
|
|
1389
1623
|
if (any_ready) {
|
|
1390
1624
|
if (ready == Qtrue) {
|
|
1391
1625
|
ready_flags = multi_socket_cselect_flags_for_curl_poll(wait_what);
|
|
@@ -1393,7 +1627,7 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
1393
1627
|
ready_flags = multi_socket_cselect_flags_for_wait_events(NUM2INT(ready));
|
|
1394
1628
|
if (ready_flags == 0) {
|
|
1395
1629
|
any_ready = 0;
|
|
1396
|
-
did_timeout =
|
|
1630
|
+
did_timeout = multi_socket_timer_due(ctx);
|
|
1397
1631
|
}
|
|
1398
1632
|
} else {
|
|
1399
1633
|
ready_flags = multi_socket_cselect_flags_for_curl_poll(wait_what);
|
|
@@ -1402,7 +1636,7 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
1402
1636
|
}
|
|
1403
1637
|
}
|
|
1404
1638
|
}
|
|
1405
|
-
handled_wait = 1;
|
|
1639
|
+
if (scheduler_wait_handled) handled_wait = 1;
|
|
1406
1640
|
}
|
|
1407
1641
|
}
|
|
1408
1642
|
#endif
|
|
@@ -1416,7 +1650,7 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
1416
1650
|
continue;
|
|
1417
1651
|
}
|
|
1418
1652
|
any_ready = (rc != 0);
|
|
1419
|
-
did_timeout = (rc == 0);
|
|
1653
|
+
did_timeout = (rc == 0 && multi_socket_timer_due(ctx));
|
|
1420
1654
|
if (any_ready) ready_flags = multi_socket_cselect_flags_for_wait_events(rc);
|
|
1421
1655
|
handled_wait = 1;
|
|
1422
1656
|
}
|
|
@@ -1440,7 +1674,7 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
1440
1674
|
continue;
|
|
1441
1675
|
}
|
|
1442
1676
|
any_ready = (rc > 0);
|
|
1443
|
-
did_timeout = (rc == 0);
|
|
1677
|
+
did_timeout = (rc == 0 && multi_socket_timer_due(ctx));
|
|
1444
1678
|
if (any_ready && wait_fd >= 0) {
|
|
1445
1679
|
if (rb_fd_isset(wait_fd, &rfds)) ready_flags |= CURL_CSELECT_IN;
|
|
1446
1680
|
if (rb_fd_isset(wait_fd, &wfds)) ready_flags |= CURL_CSELECT_OUT;
|
|
@@ -1454,7 +1688,7 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
1454
1688
|
#else
|
|
1455
1689
|
rb_thread_wait_for(tv);
|
|
1456
1690
|
#endif
|
|
1457
|
-
did_timeout =
|
|
1691
|
+
did_timeout = multi_socket_timer_due(ctx);
|
|
1458
1692
|
}
|
|
1459
1693
|
|
|
1460
1694
|
if (did_timeout) {
|
|
@@ -1489,7 +1723,7 @@ static VALUE ruby_curl_multi_socket_drive_body(VALUE argp) {
|
|
|
1489
1723
|
rb_curl_multi_socket_drive(a->self, a->rbcm, a->ctx, a->block);
|
|
1490
1724
|
return Qtrue;
|
|
1491
1725
|
}
|
|
1492
|
-
struct socket_cleanup_args { ruby_curl_multi *rbcm; multi_socket_ctx *ctx; };
|
|
1726
|
+
struct socket_cleanup_args { VALUE self; ruby_curl_multi *rbcm; multi_socket_ctx *ctx; };
|
|
1493
1727
|
static VALUE ruby_curl_multi_socket_drive_ensure(VALUE argp) {
|
|
1494
1728
|
struct socket_cleanup_args *c = (struct socket_cleanup_args *)argp;
|
|
1495
1729
|
if (c->rbcm && c->rbcm->handle) {
|
|
@@ -1503,8 +1737,14 @@ static VALUE ruby_curl_multi_socket_drive_ensure(VALUE argp) {
|
|
|
1503
1737
|
c->ctx->sock_map = NULL;
|
|
1504
1738
|
}
|
|
1505
1739
|
if (c->ctx) {
|
|
1740
|
+
if (!NIL_P(c->ctx->io_cache)) {
|
|
1741
|
+
rb_hash_clear(c->ctx->io_cache);
|
|
1742
|
+
}
|
|
1506
1743
|
c->ctx->io_cache = Qnil;
|
|
1507
1744
|
}
|
|
1745
|
+
if (!NIL_P(c->self) && rb_ivar_defined(c->self, id_socket_io_cache_ivar)) {
|
|
1746
|
+
rb_funcall(c->self, rb_intern("remove_instance_variable"), 1, ID2SYM(id_socket_io_cache_ivar));
|
|
1747
|
+
}
|
|
1508
1748
|
return Qnil;
|
|
1509
1749
|
}
|
|
1510
1750
|
|
|
@@ -1521,8 +1761,9 @@ static VALUE ruby_curl_multi_socket_perform_impl(int argc, VALUE *argv, VALUE se
|
|
|
1521
1761
|
|
|
1522
1762
|
multi_socket_ctx ctx;
|
|
1523
1763
|
ctx.sock_map = st_init_numtable();
|
|
1524
|
-
ctx.
|
|
1764
|
+
ctx.timeout_deadline_ms = -1;
|
|
1525
1765
|
ctx.io_cache = rb_hash_new();
|
|
1766
|
+
rb_ivar_set(self, id_socket_io_cache_ivar, ctx.io_cache);
|
|
1526
1767
|
|
|
1527
1768
|
/* install socket/timer callbacks */
|
|
1528
1769
|
curl_multi_setopt(rbcm->handle, CURLMOPT_SOCKETFUNCTION, multi_socket_cb);
|
|
@@ -1532,7 +1773,7 @@ static VALUE ruby_curl_multi_socket_perform_impl(int argc, VALUE *argv, VALUE se
|
|
|
1532
1773
|
|
|
1533
1774
|
/* run using socket action loop with ensure-cleanup */
|
|
1534
1775
|
struct socket_drive_args body_args = { self, rbcm, &ctx, block };
|
|
1535
|
-
struct socket_cleanup_args ensure_args = { rbcm, &ctx };
|
|
1776
|
+
struct socket_cleanup_args ensure_args = { self, rbcm, &ctx };
|
|
1536
1777
|
rb_ensure(ruby_curl_multi_socket_drive_body, (VALUE)&body_args, ruby_curl_multi_socket_drive_ensure, (VALUE)&ensure_args);
|
|
1537
1778
|
|
|
1538
1779
|
/* finalize */
|
|
@@ -1825,10 +2066,10 @@ static VALUE ruby_curl_multi_perform_impl(int argc, VALUE *argv, VALUE self) {
|
|
|
1825
2066
|
#endif /* disabled curl_multi_wait: use fdsets */
|
|
1826
2067
|
}
|
|
1827
2068
|
|
|
2069
|
+
rb_curl_multi_read_info( self, rbcm->handle );
|
|
2070
|
+
rb_curl_multi_yield_if_given(self, block);
|
|
1828
2071
|
} while( rbcm->running );
|
|
1829
2072
|
|
|
1830
|
-
rb_curl_multi_read_info( self, rbcm->handle );
|
|
1831
|
-
rb_curl_multi_yield_if_given(self, block);
|
|
1832
2073
|
if (cCurlMutiAutoClose == 1) {
|
|
1833
2074
|
rbcm->allow_close_during_perform = 1;
|
|
1834
2075
|
rb_funcall(self, rb_intern("_autoclose"), 0);
|
|
@@ -1949,6 +2190,7 @@ void init_curb_multi() {
|
|
|
1949
2190
|
idCall = rb_intern("call");
|
|
1950
2191
|
id_deferred_exception_ivar = rb_intern("@__curb_deferred_exception");
|
|
1951
2192
|
id_deferred_exception_source_id_ivar = rb_intern("@__curb_deferred_exception_source_id");
|
|
2193
|
+
id_socket_io_cache_ivar = rb_intern("@__curb_socket_io_cache");
|
|
1952
2194
|
cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject);
|
|
1953
2195
|
|
|
1954
2196
|
rb_define_alloc_func(cCurlMulti, ruby_curl_multi_alloc);
|
data/ext/extconf.rb
CHANGED
|
@@ -691,6 +691,7 @@ have_func('rb_wait_for_single_fd', 'ruby/io.h')
|
|
|
691
691
|
have_header('ruby/fiber/scheduler.h')
|
|
692
692
|
have_func('rb_fiber_scheduler_current', 'ruby/fiber/scheduler.h')
|
|
693
693
|
have_func('rb_fiber_scheduler_io_wait', 'ruby/fiber/scheduler.h')
|
|
694
|
+
have_func('rb_fiber_scheduler_io_select', 'ruby/fiber/scheduler.h')
|
|
694
695
|
have_func('rb_io_stdio_file')
|
|
695
696
|
have_func('curl_multi_wait')
|
|
696
697
|
have_func('curl_multi_socket_action')
|
data/tests/tc_curl_multi.rb
CHANGED
|
@@ -249,6 +249,41 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
249
249
|
m.close if m
|
|
250
250
|
end
|
|
251
251
|
|
|
252
|
+
def test_multi_perform_runs_work_added_from_final_idle_yield
|
|
253
|
+
with_queue_refill_test_server do |port, hits|
|
|
254
|
+
previous_autoclose = Curl::Multi.autoclose
|
|
255
|
+
Curl::Multi.autoclose = true
|
|
256
|
+
|
|
257
|
+
multi = Curl::Multi.new
|
|
258
|
+
slow = Curl::Easy.new("http://127.0.0.1:#{port}/slow")
|
|
259
|
+
queued = Curl::Easy.new("http://127.0.0.1:#{port}/queued")
|
|
260
|
+
completions = []
|
|
261
|
+
empty_yields = 0
|
|
262
|
+
queued_added = false
|
|
263
|
+
|
|
264
|
+
slow.on_complete { completions << :slow }
|
|
265
|
+
queued.on_complete { completions << :queued }
|
|
266
|
+
|
|
267
|
+
multi.add(slow)
|
|
268
|
+
multi.perform do |performing_multi|
|
|
269
|
+
empty_yields += 1 if performing_multi.requests.empty?
|
|
270
|
+
if !queued_added && empty_yields >= 2
|
|
271
|
+
queued_added = true
|
|
272
|
+
performing_multi.add(queued)
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
assert queued_added, "test should add queued work from the final idle yield"
|
|
277
|
+
assert_equal [:slow, :queued], completions
|
|
278
|
+
assert_equal 1, hits[:slow]
|
|
279
|
+
assert_equal 1, hits[:queued]
|
|
280
|
+
assert_equal 0, multi.requests.length
|
|
281
|
+
ensure
|
|
282
|
+
Curl::Multi.autoclose = previous_autoclose if defined?(previous_autoclose)
|
|
283
|
+
multi.close if defined?(multi) && multi
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
252
287
|
def test_multi_easy_get
|
|
253
288
|
n = 1
|
|
254
289
|
urls = []
|
data/tests/tc_fiber_scheduler.rb
CHANGED
|
@@ -15,10 +15,11 @@ class TestCurbFiberScheduler < Test::Unit::TestCase
|
|
|
15
15
|
include TestServerMethods
|
|
16
16
|
|
|
17
17
|
class RecordingScheduler
|
|
18
|
-
attr_reader :io_wait_events
|
|
18
|
+
attr_reader :io_wait_events, :io_select_calls
|
|
19
19
|
|
|
20
20
|
def initialize
|
|
21
21
|
@io_wait_events = []
|
|
22
|
+
@io_select_calls = 0
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
def fiber(&block)
|
|
@@ -31,7 +32,7 @@ class TestCurbFiberScheduler < Test::Unit::TestCase
|
|
|
31
32
|
|
|
32
33
|
readers = (events & IO::READABLE) != 0 ? [io] : nil
|
|
33
34
|
writers = (events & IO::WRITABLE) != 0 ? [io] : nil
|
|
34
|
-
readable, writable = IO.select(readers, writers, nil, timeout)
|
|
35
|
+
readable, writable = blocking_io { IO.select(readers, writers, nil, timeout) }
|
|
35
36
|
|
|
36
37
|
ready = 0
|
|
37
38
|
ready |= IO::READABLE if readable && !readable.empty?
|
|
@@ -39,6 +40,11 @@ class TestCurbFiberScheduler < Test::Unit::TestCase
|
|
|
39
40
|
ready.zero? ? false : ready
|
|
40
41
|
end
|
|
41
42
|
|
|
43
|
+
def io_select(readers, writers, excepts, timeout = nil)
|
|
44
|
+
@io_select_calls += 1
|
|
45
|
+
blocking_io { IO.select(readers, writers, excepts, timeout) }
|
|
46
|
+
end
|
|
47
|
+
|
|
42
48
|
def kernel_sleep(duration = nil)
|
|
43
49
|
sleep(duration || 0)
|
|
44
50
|
end
|
|
@@ -56,6 +62,16 @@ class TestCurbFiberScheduler < Test::Unit::TestCase
|
|
|
56
62
|
|
|
57
63
|
def fiber_interrupt(*)
|
|
58
64
|
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def blocking_io(&block)
|
|
69
|
+
if Fiber.respond_to?(:blocking)
|
|
70
|
+
Fiber.blocking(&block)
|
|
71
|
+
else
|
|
72
|
+
block.call
|
|
73
|
+
end
|
|
74
|
+
end
|
|
59
75
|
end
|
|
60
76
|
|
|
61
77
|
ITERS = 4
|
|
@@ -66,7 +82,7 @@ class TestCurbFiberScheduler < Test::Unit::TestCase
|
|
|
66
82
|
SERIAL_TIME_WOULD_BE_ABOUT = MIN_S * ITERS
|
|
67
83
|
|
|
68
84
|
def setup
|
|
69
|
-
@port =
|
|
85
|
+
@port = unused_local_port
|
|
70
86
|
|
|
71
87
|
@response_proc = lambda do |res|
|
|
72
88
|
res['Content-Type'] = 'text/plain'
|
|
@@ -208,9 +224,11 @@ class TestCurbFiberScheduler < Test::Unit::TestCase
|
|
|
208
224
|
end
|
|
209
225
|
|
|
210
226
|
assert_equal 200, result
|
|
211
|
-
assert_operator scheduler.io_wait_events.length, :>=, 1
|
|
212
|
-
|
|
213
|
-
|
|
227
|
+
assert_operator scheduler.io_wait_events.length + scheduler.io_select_calls, :>=, 1
|
|
228
|
+
unless scheduler.io_wait_events.empty?
|
|
229
|
+
assert scheduler.io_wait_events.all? { |events| events.is_a?(Integer) }
|
|
230
|
+
assert scheduler.io_wait_events.any? { |events| (events & (IO::READABLE | IO::WRITABLE)) != 0 }
|
|
231
|
+
end
|
|
214
232
|
end
|
|
215
233
|
|
|
216
234
|
def test_multi_reuse_after_scheduler_perform
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: curb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.3.
|
|
4
|
+
version: 1.3.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ross Bamford
|
|
8
8
|
- Todd A. Fisher
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-05-
|
|
11
|
+
date: 2026-05-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Curb (probably CUrl-RuBy or something) provides Ruby-language bindings
|
|
14
14
|
for the libcurl(3), a fully-featured client-side URL transfer library. cURL and
|
|
@@ -107,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
107
107
|
- !ruby/object:Gem::Version
|
|
108
108
|
version: '0'
|
|
109
109
|
requirements: []
|
|
110
|
-
rubygems_version: 4.0.
|
|
110
|
+
rubygems_version: 4.0.10
|
|
111
111
|
specification_version: 4
|
|
112
112
|
summary: Ruby libcurl bindings
|
|
113
113
|
test_files:
|