cool.io 1.2.0-x86-mingw32 → 1.2.3-x86-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +11 -2
- data/CHANGES.md +18 -0
- data/Gemfile +1 -1
- data/README.md +1 -5
- data/ext/cool.io/cool.io.h +1 -0
- data/ext/cool.io/ev_wrap.h +4 -2
- data/ext/cool.io/extconf.rb +4 -0
- data/ext/cool.io/loop.c +35 -75
- data/ext/libev/ev.c +82 -0
- data/ext/libev/ev_select.c +58 -9
- data/ext/libev/ruby_gil.patch +97 -0
- data/lib/cool.io/listener.rb +9 -3
- data/lib/cool.io/loop.rb +2 -2
- data/lib/cool.io/server.rb +2 -1
- data/lib/cool.io/version.rb +1 -1
- data/spec/dns_spec.rb +1 -1
- data/spec/spec_helper.rb +7 -0
- data/spec/tcp_server_spec.rb +132 -0
- data/spec/unix_server_spec.rb +3 -1
- metadata +6 -4
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -1,9 +1,27 @@
|
|
1
|
+
1.2.3
|
2
|
+
-----
|
3
|
+
|
4
|
+
* Fix CPU consuming issue on Windows.
|
5
|
+
|
6
|
+
1.2.2
|
7
|
+
-----
|
8
|
+
|
9
|
+
* Add timeout option to Loop#run and Loop#run_once. Default by nil
|
10
|
+
* Support Ruby 2.2.0
|
11
|
+
|
12
|
+
1.2.1
|
13
|
+
-----
|
14
|
+
|
15
|
+
* Release the GIL when libev polls (#24)
|
16
|
+
* Add Listener#listen method to change backlog size
|
17
|
+
|
1
18
|
1.2.0
|
2
19
|
-----
|
3
20
|
|
4
21
|
* Support Windows environment via cross compilation
|
5
22
|
* Include iobuffer library
|
6
23
|
* Update to libev 4.15
|
24
|
+
* Remove Ruby 1.8 support
|
7
25
|
|
8
26
|
1.1.0
|
9
27
|
-----
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
Cool.io
|
2
2
|
=======
|
3
3
|
|
4
|
-
###
|
5
|
-
### Please check out [Celluloid::IO](http://github.com/celluloid/celluloid-io) instead!
|
4
|
+
### If you are interested in Celluloid based IO framework, please check out [Celluloid::IO](http://github.com/celluloid/celluloid-io)
|
6
5
|
|
7
6
|
Cool.io is an event library for Ruby, built on the libev event library which
|
8
7
|
provides a cross-platform interface to high performance system calls . This
|
@@ -15,10 +14,8 @@ applications.
|
|
15
14
|
|
16
15
|
You can include Cool.io in your programs with:
|
17
16
|
|
18
|
-
require 'rubygems'
|
19
17
|
require 'cool.io'
|
20
18
|
|
21
|
-
Questions? Sign up for the mailing list by emailing: [cool.io@librelist.com](mailto:cool.io@librelist.com)
|
22
19
|
|
23
20
|
Anatomy
|
24
21
|
-------
|
@@ -97,7 +94,6 @@ Example Program
|
|
97
94
|
|
98
95
|
Cool.io provides a Sinatra-like DSL for authoring event-driven programs:
|
99
96
|
|
100
|
-
require 'rubygems'
|
101
97
|
require 'cool.io'
|
102
98
|
|
103
99
|
ADDR = '127.0.0.1'
|
data/ext/cool.io/cool.io.h
CHANGED
data/ext/cool.io/ev_wrap.h
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
#define EV_STANDALONE /* keeps ev from requiring config.h */
|
2
|
+
|
2
3
|
#ifdef _WIN32
|
3
|
-
#
|
4
|
-
#
|
4
|
+
#define EV_SELECT_IS_WINSOCKET 1 /* configure libev for windows select */
|
5
|
+
#define EV_USE_MONOTONIC 0
|
6
|
+
#define EV_USE_REALTIME 0
|
5
7
|
#endif
|
6
8
|
|
7
9
|
#include "../libev/ev.h"
|
data/ext/cool.io/extconf.rb
CHANGED
@@ -8,6 +8,10 @@ if have_func('rb_thread_blocking_region')
|
|
8
8
|
$defs << '-DHAVE_RB_THREAD_BLOCKING_REGION'
|
9
9
|
end
|
10
10
|
|
11
|
+
if have_func('rb_thread_call_without_gvl')
|
12
|
+
$defs << '-DHAVE_RB_THEREAD_CALL_WITHOUT_GVL'
|
13
|
+
end
|
14
|
+
|
11
15
|
if have_func('rb_thread_alone')
|
12
16
|
$defs << '-DHAVE_RB_THREAD_ALONE'
|
13
17
|
end
|
data/ext/cool.io/loop.c
CHANGED
@@ -11,14 +11,6 @@
|
|
11
11
|
|
12
12
|
#include "cool.io.h"
|
13
13
|
|
14
|
-
#if defined(HAVE_RB_THREAD_BLOCKING_REGION)
|
15
|
-
# define Coolio_Loop_may_block_safely() (1)
|
16
|
-
#elif defined(HAVE_RB_THREAD_ALONE)
|
17
|
-
# define Coolio_Loop_may_block_safely() (rb_thread_alone())
|
18
|
-
#else /* just in case Ruby changes: */
|
19
|
-
# define Coolio_Loop_may_block_safely() (0)
|
20
|
-
#endif
|
21
|
-
|
22
14
|
static VALUE mCoolio = Qnil;
|
23
15
|
static VALUE cCoolio_Loop = Qnil;
|
24
16
|
|
@@ -28,10 +20,10 @@ static void Coolio_Loop_free(struct Coolio_Loop *loop);
|
|
28
20
|
|
29
21
|
static VALUE Coolio_Loop_initialize(VALUE self);
|
30
22
|
static VALUE Coolio_Loop_ev_loop_new(VALUE self, VALUE flags);
|
31
|
-
static VALUE Coolio_Loop_run_once(VALUE self);
|
23
|
+
static VALUE Coolio_Loop_run_once(int argc, VALUE *argv, VALUE self);
|
32
24
|
static VALUE Coolio_Loop_run_nonblock(VALUE self);
|
33
25
|
|
34
|
-
static void
|
26
|
+
static void Coolio_Loop_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents);
|
35
27
|
static void Coolio_Loop_dispatch_events(struct Coolio_Loop *loop_data);
|
36
28
|
|
37
29
|
#define DEFAULT_EVENTBUF_SIZE 32
|
@@ -54,7 +46,7 @@ void Init_coolio_loop()
|
|
54
46
|
|
55
47
|
rb_define_method(cCoolio_Loop, "initialize", Coolio_Loop_initialize, 0);
|
56
48
|
rb_define_private_method(cCoolio_Loop, "ev_loop_new", Coolio_Loop_ev_loop_new, 1);
|
57
|
-
rb_define_method(cCoolio_Loop, "run_once", Coolio_Loop_run_once,
|
49
|
+
rb_define_method(cCoolio_Loop, "run_once", Coolio_Loop_run_once, -1);
|
58
50
|
rb_define_method(cCoolio_Loop, "run_nonblock", Coolio_Loop_run_nonblock, 0);
|
59
51
|
}
|
60
52
|
|
@@ -63,6 +55,7 @@ static VALUE Coolio_Loop_allocate(VALUE klass)
|
|
63
55
|
struct Coolio_Loop *loop = (struct Coolio_Loop *)xmalloc(sizeof(struct Coolio_Loop));
|
64
56
|
|
65
57
|
loop->ev_loop = 0;
|
58
|
+
ev_init(&loop->timer, Coolio_Loop_timeout_callback);
|
66
59
|
loop->running = 0;
|
67
60
|
loop->events_received = 0;
|
68
61
|
loop->eventbuf_size = DEFAULT_EVENTBUF_SIZE;
|
@@ -174,90 +167,57 @@ void Coolio_Loop_process_event(VALUE watcher, int revents)
|
|
174
167
|
loop_data->events_received++;
|
175
168
|
}
|
176
169
|
|
170
|
+
/* Called whenever a timeout fires on the event loop */
|
171
|
+
static void Coolio_Loop_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents)
|
172
|
+
{
|
173
|
+
/* We don't actually need to do anything here, the mere firing of the
|
174
|
+
timer is sufficient to interrupt the selector. However, libev still wants a callback */
|
175
|
+
}
|
176
|
+
|
177
177
|
/**
|
178
178
|
* call-seq:
|
179
179
|
* Coolio::Loop.run_once -> nil
|
180
180
|
*
|
181
181
|
* Run the Coolio::Loop once, blocking until events are received.
|
182
182
|
*/
|
183
|
-
static VALUE Coolio_Loop_run_once(VALUE self)
|
183
|
+
static VALUE Coolio_Loop_run_once(int argc, VALUE *argv, VALUE self)
|
184
184
|
{
|
185
|
+
VALUE timeout;
|
185
186
|
VALUE nevents;
|
187
|
+
struct Coolio_Loop *loop_data;
|
186
188
|
|
187
|
-
|
188
|
-
struct Coolio_Loop *loop_data;
|
189
|
-
|
190
|
-
Data_Get_Struct(self, struct Coolio_Loop, loop_data);
|
189
|
+
rb_scan_args(argc, argv, "01", &timeout);
|
191
190
|
|
192
|
-
|
191
|
+
if (timeout != Qnil && NUM2DBL(timeout) < 0) {
|
192
|
+
rb_raise(rb_eArgError, "time interval must be positive");
|
193
|
+
}
|
193
194
|
|
194
|
-
|
195
|
-
Coolio_Loop_dispatch_events(loop_data);
|
195
|
+
Data_Get_Struct(self, struct Coolio_Loop, loop_data);
|
196
196
|
|
197
|
-
|
198
|
-
loop_data->events_received = 0;
|
197
|
+
assert(loop_data->ev_loop && !loop_data->events_received);
|
199
198
|
|
199
|
+
/* Implement the optional timeout (if any) as a ev_timer */
|
200
|
+
/* Using the technique written at
|
201
|
+
http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#code_ev_timer_code_relative_and_opti,
|
202
|
+
the timer is not stopped/started everytime when timeout is specified, instead,
|
203
|
+
the timer is stopped when timeout is not specified. */
|
204
|
+
if (timeout != Qnil) {
|
205
|
+
/* It seems libev is not a fan of timers being zero, so fudge a little */
|
206
|
+
loop_data->timer.repeat = NUM2DBL(timeout) + 0.0001;
|
207
|
+
ev_timer_again(loop_data->ev_loop, &loop_data->timer);
|
200
208
|
} else {
|
201
|
-
|
202
|
-
rb_thread_schedule();
|
209
|
+
ev_timer_stop(loop_data->ev_loop, &loop_data->timer);
|
203
210
|
}
|
204
211
|
|
205
|
-
|
206
|
-
}
|
207
|
-
|
208
|
-
/* Ruby 1.9 supports blocking system calls through rb_thread_blocking_region() */
|
209
|
-
#ifdef HAVE_RB_THREAD_BLOCKING_REGION
|
210
|
-
#define HAVE_EV_LOOP_ONESHOT
|
211
|
-
static VALUE Coolio_Loop_ev_loop_oneshot_blocking(void *ptr)
|
212
|
-
{
|
213
|
-
/* The libev loop has now escaped through the Global VM Lock unscathed! */
|
214
|
-
struct Coolio_Loop *loop_data = (struct Coolio_Loop *)ptr;
|
215
|
-
|
212
|
+
/* libev is patched to release the GIL when it makes its system call */
|
216
213
|
RUN_LOOP(loop_data, EVLOOP_ONESHOT);
|
217
|
-
|
218
|
-
return Qnil;
|
219
|
-
}
|
220
214
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
rb_thread_blocking_region(Coolio_Loop_ev_loop_oneshot_blocking, loop_data, RUBY_UBF_IO, 0);
|
225
|
-
}
|
226
|
-
#endif
|
227
|
-
|
228
|
-
/* Ruby 1.8 requires us to periodically run the event loop then defer back to
|
229
|
-
* the green threads scheduler */
|
230
|
-
#ifndef HAVE_EV_LOOP_ONESHOT
|
231
|
-
#define BLOCKING_INTERVAL 0.01 /* Block for 10ms at a time */
|
232
|
-
|
233
|
-
/* Stub for scheduler's ev_timer callback */
|
234
|
-
static void timer_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents)
|
235
|
-
{
|
236
|
-
ev_timer_again (ev_loop, timer);
|
237
|
-
}
|
238
|
-
|
239
|
-
/* Run the event loop, calling rb_thread_schedule every 10ms */
|
240
|
-
static void Coolio_Loop_ev_loop_oneshot(struct Coolio_Loop *loop_data)
|
241
|
-
{
|
242
|
-
struct ev_timer timer;
|
243
|
-
struct timeval tv;
|
244
|
-
|
245
|
-
/* Set up an ev_timer to unblock the loop every 10ms */
|
246
|
-
ev_timer_init(&timer, timer_callback, BLOCKING_INTERVAL, BLOCKING_INTERVAL);
|
247
|
-
ev_timer_start(loop_data->ev_loop, &timer);
|
248
|
-
|
249
|
-
/* Loop until we receive events */
|
250
|
-
while(!loop_data->events_received) {
|
251
|
-
TRAP_BEG;
|
252
|
-
RUN_LOOP(loop_data, EVLOOP_ONESHOT);
|
253
|
-
TRAP_END;
|
254
|
-
|
255
|
-
rb_thread_schedule();
|
256
|
-
}
|
215
|
+
Coolio_Loop_dispatch_events(loop_data);
|
216
|
+
nevents = INT2NUM(loop_data->events_received);
|
217
|
+
loop_data->events_received = 0;
|
257
218
|
|
258
|
-
|
219
|
+
return nevents;
|
259
220
|
}
|
260
|
-
#endif
|
261
221
|
|
262
222
|
/**
|
263
223
|
* call-seq:
|
data/ext/libev/ev.c
CHANGED
@@ -37,6 +37,10 @@
|
|
37
37
|
* either the BSD or the GPL.
|
38
38
|
*/
|
39
39
|
|
40
|
+
/* ########## COOLIO PATCHERY HO! ########## */
|
41
|
+
#include "ruby.h"
|
42
|
+
/* ######################################## */
|
43
|
+
|
40
44
|
/* this big block deduces configuration from config.h */
|
41
45
|
#ifndef EV_STANDALONE
|
42
46
|
# ifdef EV_CONFIG_H
|
@@ -3237,9 +3241,27 @@ time_update (EV_P_ ev_tstamp max_block)
|
|
3237
3241
|
}
|
3238
3242
|
}
|
3239
3243
|
|
3244
|
+
/* ########## COOLIO PATCHERY HO! ########## */
|
3245
|
+
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
3246
|
+
static
|
3247
|
+
VALUE ev_backend_poll(void **args)
|
3248
|
+
{
|
3249
|
+
struct ev_loop *loop = (struct ev_loop *)args[0];
|
3250
|
+
ev_tstamp waittime = *(ev_tstamp *)args[1];
|
3251
|
+
backend_poll (EV_A_ waittime);
|
3252
|
+
}
|
3253
|
+
#endif
|
3254
|
+
/* ######################################## */
|
3255
|
+
|
3240
3256
|
int
|
3241
3257
|
ev_run (EV_P_ int flags)
|
3242
3258
|
{
|
3259
|
+
/* ########## COOLIO PATCHERY HO! ########## */
|
3260
|
+
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
3261
|
+
void *poll_args[2];
|
3262
|
+
#endif
|
3263
|
+
/* ######################################## */
|
3264
|
+
|
3243
3265
|
#if EV_FEATURE_API
|
3244
3266
|
++loop_depth;
|
3245
3267
|
#endif
|
@@ -3357,7 +3379,67 @@ ev_run (EV_P_ int flags)
|
|
3357
3379
|
++loop_count;
|
3358
3380
|
#endif
|
3359
3381
|
assert ((loop_done = EVBREAK_RECURSE, 1)); /* assert for side effect */
|
3382
|
+
|
3383
|
+
/*
|
3384
|
+
########################## COOLIO PATCHERY HO! ##########################
|
3385
|
+
|
3386
|
+
The original patch file is made by Tony Arcieri.
|
3387
|
+
https://github.com/celluloid/nio4r/blob/680143345726c5a64bb22376ca8fc3c6857019ae/ext/libev/ruby_gil.patch.
|
3388
|
+
|
3389
|
+
According to the grandwizards of Ruby, locking and unlocking of the global
|
3390
|
+
interpreter lock are apparently too powerful a concept for a mere mortal to
|
3391
|
+
wield (although redefining what + and - do to numbers is totally cool).
|
3392
|
+
And so it came to pass that the only acceptable way to release the global
|
3393
|
+
interpreter lock is through a convoluted callback system that thakes a
|
3394
|
+
function pointer. While the grandwizard of libev foresaw this sort of scenario,
|
3395
|
+
he too attempted to place an API with callbacks on it, one that runs before
|
3396
|
+
the system call, and one that runs immediately after.
|
3397
|
+
|
3398
|
+
And so it came to pass that trying to wrap everything up in callbacks created
|
3399
|
+
two incompatible APIs, Ruby's which releases the global interpreter lock and
|
3400
|
+
reacquires it when the callback returns, and libev's, which wants two
|
3401
|
+
callbacks, one which runs before the polling operation starts, and one which
|
3402
|
+
runs after it finishes.
|
3403
|
+
|
3404
|
+
These two systems are incompatible as they both want to use callbacks to
|
3405
|
+
solve the same problem, however libev wants to use before/after callbacks,
|
3406
|
+
and Ruby wants to use an "around" callback. This presents a significant
|
3407
|
+
problem as these two patterns of callbacks are diametrical opposites of each
|
3408
|
+
other and thus cannot be composed.
|
3409
|
+
|
3410
|
+
And thus we are left with no choice but to patch the internals of libev in
|
3411
|
+
order to release a mutex at just the precise moment.
|
3412
|
+
|
3413
|
+
Let this be a lesson to the all: CALLBACKS FUCKING BLOW
|
3414
|
+
|
3415
|
+
#######################################################################
|
3416
|
+
*/
|
3417
|
+
|
3418
|
+
/*
|
3419
|
+
simulate to rb_thread_call_without_gvl using rb_theread_blocking_region.
|
3420
|
+
https://github.com/brianmario/mysql2/blob/master/ext/mysql2/client.h#L8
|
3421
|
+
*/
|
3422
|
+
|
3423
|
+
#ifndef HAVE_RB_THREAD_CALL_WITHOUT_GVL
|
3424
|
+
#ifdef HAVE_RB_THREAD_BLOCKING_REGION
|
3425
|
+
|
3426
|
+
#define rb_thread_call_without_gvl(func, data1, ubf, data2) \
|
3427
|
+
rb_thread_blocking_region((rb_blocking_function_t *)func, data1, ubf, data2)
|
3428
|
+
|
3429
|
+
#endif
|
3430
|
+
#endif
|
3431
|
+
|
3432
|
+
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
3433
|
+
poll_args[0] = (void *)loop;
|
3434
|
+
poll_args[1] = (void *)&waittime;
|
3435
|
+
rb_thread_call_without_gvl(ev_backend_poll, (void *)&poll_args, RUBY_UBF_IO, 0);
|
3436
|
+
#else
|
3360
3437
|
backend_poll (EV_A_ waittime);
|
3438
|
+
#endif
|
3439
|
+
/*
|
3440
|
+
############################# END PATCHERY ############################
|
3441
|
+
*/
|
3442
|
+
|
3361
3443
|
assert ((loop_done = EVBREAK_CANCEL, 1)); /* assert for side effect */
|
3362
3444
|
|
3363
3445
|
pipe_write_wanted = 0; /* just an optimisation, no fence needed */
|
data/ext/libev/ev_select.c
CHANGED
@@ -67,6 +67,53 @@
|
|
67
67
|
|
68
68
|
#include <string.h>
|
69
69
|
|
70
|
+
#ifdef _WIN32
|
71
|
+
/*
|
72
|
+
########## COOLIO PATCHERY HO! ##########
|
73
|
+
|
74
|
+
Ruby undefs FD_* utilities for own implementation.
|
75
|
+
It converts fd argument into socket handle internally on Windows,
|
76
|
+
so libev should not use Ruby's FD_* utilities.
|
77
|
+
|
78
|
+
Following FD_* utilities come from MinGW.
|
79
|
+
RubyInstaller is built by MinGW so this should work.
|
80
|
+
*/
|
81
|
+
int PASCAL __WSAFDIsSet(SOCKET,fd_set*);
|
82
|
+
#define EV_WIN_FD_CLR(fd,set) do { u_int __i;\
|
83
|
+
for (__i = 0; __i < ((fd_set *)(set))->fd_count ; __i++) {\
|
84
|
+
if (((fd_set *)(set))->fd_array[__i] == (fd)) {\
|
85
|
+
while (__i < ((fd_set *)(set))->fd_count-1) {\
|
86
|
+
((fd_set*)(set))->fd_array[__i] = ((fd_set*)(set))->fd_array[__i+1];\
|
87
|
+
__i++;\
|
88
|
+
}\
|
89
|
+
((fd_set*)(set))->fd_count--;\
|
90
|
+
break;\
|
91
|
+
}\
|
92
|
+
}\
|
93
|
+
} while (0)
|
94
|
+
#define EV_WIN_FD_SET(fd, set) do { u_int __i;\
|
95
|
+
for (__i = 0; __i < ((fd_set *)(set))->fd_count ; __i++) {\
|
96
|
+
if (((fd_set *)(set))->fd_array[__i] == (fd)) {\
|
97
|
+
break;\
|
98
|
+
}\
|
99
|
+
}\
|
100
|
+
if (__i == ((fd_set *)(set))->fd_count) {\
|
101
|
+
if (((fd_set *)(set))->fd_count < FD_SETSIZE) {\
|
102
|
+
((fd_set *)(set))->fd_array[__i] = (fd);\
|
103
|
+
((fd_set *)(set))->fd_count++;\
|
104
|
+
}\
|
105
|
+
}\
|
106
|
+
} while(0)
|
107
|
+
#define EV_WIN_FD_ZERO(set) (((fd_set *)(set))->fd_count=0)
|
108
|
+
#define EV_WIN_FD_ISSET(fd, set) __WSAFDIsSet((SOCKET)(fd), (fd_set *)(set))
|
109
|
+
/* ######################################## */
|
110
|
+
#else
|
111
|
+
#define EV_WIN_FD_CLR FD_CLR
|
112
|
+
#define EV_WIN_FD_SET FD_SET
|
113
|
+
#define EV_WIN_FD_ZERO FD_ZERO
|
114
|
+
#define EV_WIN_FD_ISSET FD_ISSET
|
115
|
+
#endif
|
116
|
+
|
70
117
|
static void
|
71
118
|
select_modify (EV_P_ int fd, int oev, int nev)
|
72
119
|
{
|
@@ -91,17 +138,17 @@ select_modify (EV_P_ int fd, int oev, int nev)
|
|
91
138
|
if ((oev ^ nev) & EV_READ)
|
92
139
|
#endif
|
93
140
|
if (nev & EV_READ)
|
94
|
-
|
141
|
+
EV_WIN_FD_SET (handle, (fd_set *)vec_ri);
|
95
142
|
else
|
96
|
-
|
143
|
+
EV_WIN_FD_CLR (handle, (fd_set *)vec_ri);
|
97
144
|
|
98
145
|
#if EV_SELECT_IS_WINSOCKET
|
99
146
|
if ((oev ^ nev) & EV_WRITE)
|
100
147
|
#endif
|
101
148
|
if (nev & EV_WRITE)
|
102
|
-
|
149
|
+
EV_WIN_FD_SET (handle, (fd_set *)vec_wi);
|
103
150
|
else
|
104
|
-
|
151
|
+
EV_WIN_FD_CLR (handle, (fd_set *)vec_wi);
|
105
152
|
|
106
153
|
#else
|
107
154
|
|
@@ -136,6 +183,8 @@ select_modify (EV_P_ int fd, int oev, int nev)
|
|
136
183
|
}
|
137
184
|
}
|
138
185
|
|
186
|
+
#undef socket
|
187
|
+
|
139
188
|
static void
|
140
189
|
select_poll (EV_P_ ev_tstamp timeout)
|
141
190
|
{
|
@@ -230,10 +279,10 @@ select_poll (EV_P_ ev_tstamp timeout)
|
|
230
279
|
int handle = fd;
|
231
280
|
#endif
|
232
281
|
|
233
|
-
if (
|
234
|
-
if (
|
282
|
+
if (EV_WIN_FD_ISSET (handle, (fd_set *)vec_ro)) events |= EV_READ;
|
283
|
+
if (EV_WIN_FD_ISSET (handle, (fd_set *)vec_wo)) events |= EV_WRITE;
|
235
284
|
#ifdef _WIN32
|
236
|
-
if (
|
285
|
+
if (EV_WIN_FD_ISSET (handle, (fd_set *)vec_eo)) events |= EV_WRITE;
|
237
286
|
#endif
|
238
287
|
|
239
288
|
if (expect_true (events))
|
@@ -279,9 +328,9 @@ select_init (EV_P_ int flags)
|
|
279
328
|
backend_poll = select_poll;
|
280
329
|
|
281
330
|
#if EV_SELECT_USE_FD_SET
|
282
|
-
vec_ri = ev_malloc (sizeof (fd_set));
|
331
|
+
vec_ri = ev_malloc (sizeof (fd_set)); EV_WIN_FD_ZERO ((fd_set *)vec_ri);
|
283
332
|
vec_ro = ev_malloc (sizeof (fd_set));
|
284
|
-
vec_wi = ev_malloc (sizeof (fd_set));
|
333
|
+
vec_wi = ev_malloc (sizeof (fd_set)); EV_WIN_FD_ZERO ((fd_set *)vec_wi);
|
285
334
|
vec_wo = ev_malloc (sizeof (fd_set));
|
286
335
|
#ifdef _WIN32
|
287
336
|
vec_eo = ev_malloc (sizeof (fd_set));
|
@@ -0,0 +1,97 @@
|
|
1
|
+
diff --git a/ext/libev/ev.c b/ext/libev/ev.c
|
2
|
+
index e5bd5ab..10f6ff2 100644
|
3
|
+
--- a/ext/libev/ev.c
|
4
|
+
+++ b/ext/libev/ev.c
|
5
|
+
@@ -37,6 +37,10 @@
|
6
|
+
* either the BSD or the GPL.
|
7
|
+
*/
|
8
|
+
|
9
|
+
+/* ########## COOLIO PATCHERY HO! ########## */
|
10
|
+
+#include "ruby.h"
|
11
|
+
+/* ######################################## */
|
12
|
+
+
|
13
|
+
/* this big block deduces configuration from config.h */
|
14
|
+
#ifndef EV_STANDALONE
|
15
|
+
# ifdef EV_CONFIG_H
|
16
|
+
@@ -3237,9 +3241,27 @@ time_update (EV_P_ ev_tstamp max_block)
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
+/* ########## COOLIO PATCHERY HO! ########## */
|
21
|
+
+#if defined(HAVE_RB_THREAD_BLOCKING_REGION)
|
22
|
+
+static
|
23
|
+
+VALUE ev_backend_poll(void **args)
|
24
|
+
+{
|
25
|
+
+ struct ev_loop *loop = (struct ev_loop *)args[0];
|
26
|
+
+ ev_tstamp waittime = *(ev_tstamp *)args[1];
|
27
|
+
+ backend_poll (EV_A_ waittime);
|
28
|
+
+}
|
29
|
+
+#endif
|
30
|
+
+/* ######################################## */
|
31
|
+
+
|
32
|
+
int
|
33
|
+
ev_run (EV_P_ int flags)
|
34
|
+
{
|
35
|
+
+/* ########## COOLIO PATCHERY HO! ########## */
|
36
|
+
+#if defined(HAVE_RB_THREAD_BLOCKING_REGION)
|
37
|
+
+ void *poll_args[2];
|
38
|
+
+#endif
|
39
|
+
+/* ######################################## */
|
40
|
+
+
|
41
|
+
#if EV_FEATURE_API
|
42
|
+
++loop_depth;
|
43
|
+
#endif
|
44
|
+
@@ -3357,7 +3379,53 @@ ev_run (EV_P_ int flags)
|
45
|
+
++loop_count;
|
46
|
+
#endif
|
47
|
+
assert ((loop_done = EVBREAK_RECURSE, 1)); /* assert for side effect */
|
48
|
+
+
|
49
|
+
+/*
|
50
|
+
+########################## COOLIO PATCHERY HO! ##########################
|
51
|
+
+
|
52
|
+
+The original patch file is made by Tony Arcieri.
|
53
|
+
+https://github.com/celluloid/nio4r/blob/680143345726c5a64bb22376ca8fc3c6857019ae/ext/libev/ruby_gil.patch.
|
54
|
+
+
|
55
|
+
+According to the grandwizards of Ruby, locking and unlocking of the global
|
56
|
+
+interpreter lock are apparently too powerful a concept for a mere mortal to
|
57
|
+
+wield (although redefining what + and - do to numbers is totally cool).
|
58
|
+
+And so it came to pass that the only acceptable way to release the global
|
59
|
+
+interpreter lock is through a convoluted callback system that thakes a
|
60
|
+
+function pointer. While the grandwizard of libev foresaw this sort of scenario,
|
61
|
+
+he too attempted to place an API with callbacks on it, one that runs before
|
62
|
+
+the system call, and one that runs immediately after.
|
63
|
+
+
|
64
|
+
+And so it came to pass that trying to wrap everything up in callbacks created
|
65
|
+
+two incompatible APIs, Ruby's which releases the global interpreter lock and
|
66
|
+
+reacquires it when the callback returns, and libev's, which wants two
|
67
|
+
+callbacks, one which runs before the polling operation starts, and one which
|
68
|
+
+runs after it finishes.
|
69
|
+
+
|
70
|
+
+These two systems are incompatible as they both want to use callbacks to
|
71
|
+
+solve the same problem, however libev wants to use before/after callbacks,
|
72
|
+
+and Ruby wants to use an "around" callback. This presents a significant
|
73
|
+
+problem as these two patterns of callbacks are diametrical opposites of each
|
74
|
+
+other and thus cannot be composed.
|
75
|
+
+
|
76
|
+
+And thus we are left with no choice but to patch the internals of libev in
|
77
|
+
+order to release a mutex at just the precise moment.
|
78
|
+
+
|
79
|
+
+Let this be a lesson to the all: CALLBACKS FUCKING BLOW
|
80
|
+
+
|
81
|
+
+#######################################################################
|
82
|
+
+*/
|
83
|
+
+
|
84
|
+
+#if defined(HAVE_RB_THREAD_BLOCKING_REGION)
|
85
|
+
+ poll_args[0] = (void *)loop;
|
86
|
+
+ poll_args[1] = (void *)&waittime;
|
87
|
+
+ rb_thread_blocking_region(ev_backend_poll, (void *)&poll_args, RUBY_UBF_IO, 0);
|
88
|
+
+#else
|
89
|
+
backend_poll (EV_A_ waittime);
|
90
|
+
+#endif
|
91
|
+
+/*
|
92
|
+
+############################# END PATCHERY ############################
|
93
|
+
+*/
|
94
|
+
+
|
95
|
+
assert ((loop_done = EVBREAK_CANCEL, 1)); /* assert for side effect */
|
96
|
+
|
97
|
+
pipe_write_wanted = 0; /* just an optimisation, no fence needed */
|
data/lib/cool.io/listener.rb
CHANGED
@@ -21,6 +21,10 @@ module Coolio
|
|
21
21
|
@listen_socket.fileno
|
22
22
|
end
|
23
23
|
|
24
|
+
def listen(backlog)
|
25
|
+
@listen_socket.listen(backlog)
|
26
|
+
end
|
27
|
+
|
24
28
|
# Close the listener
|
25
29
|
def close
|
26
30
|
detach if attached?
|
@@ -53,9 +57,9 @@ module Coolio
|
|
53
57
|
end
|
54
58
|
end
|
55
59
|
|
56
|
-
|
57
|
-
DEFAULT_BACKLOG = 1024
|
60
|
+
DEFAULT_BACKLOG = 1024
|
58
61
|
|
62
|
+
class TCPListener < Listener
|
59
63
|
# Create a new Coolio::TCPListener on the specified address and port.
|
60
64
|
# Accepts the following options:
|
61
65
|
#
|
@@ -87,7 +91,9 @@ module Coolio
|
|
87
91
|
# Optionally, it can also take anyn existing UNIXServer object
|
88
92
|
# and create a Coolio::UNIXListener out of it.
|
89
93
|
def initialize(*args)
|
90
|
-
|
94
|
+
s = ::UNIXServer === args.first ? args.first : ::UNIXServer.new(*args)
|
95
|
+
s.instance_eval { listen(DEFAULT_BACKLOG) }
|
96
|
+
super(s)
|
91
97
|
end
|
92
98
|
end
|
93
99
|
end
|
data/lib/cool.io/loop.rb
CHANGED
@@ -88,12 +88,12 @@ module Coolio
|
|
88
88
|
# event callbacks to watchers until all watchers associated with
|
89
89
|
# the loop have been disabled or detached. The loop may be
|
90
90
|
# explicitly stopped by calling the stop method on the loop object.
|
91
|
-
def run
|
91
|
+
def run(timeout = nil)
|
92
92
|
raise RuntimeError, "no watchers for this loop" if @watchers.empty?
|
93
93
|
|
94
94
|
@running = true
|
95
95
|
while @running and not @active_watchers.zero?
|
96
|
-
run_once
|
96
|
+
run_once(timeout)
|
97
97
|
end
|
98
98
|
@running = false
|
99
99
|
end
|
data/lib/cool.io/server.rb
CHANGED
@@ -56,7 +56,7 @@ module Coolio
|
|
56
56
|
raise ArgumentError, "port must be an integer" if nil == port
|
57
57
|
::TCPServer.new(host, port)
|
58
58
|
end
|
59
|
-
listen_socket.instance_eval { listen(
|
59
|
+
listen_socket.instance_eval { listen(DEFAULT_BACKLOG) } # Change listen backlog to 1024
|
60
60
|
super(listen_socket, klass, *args, &block)
|
61
61
|
end
|
62
62
|
end
|
@@ -68,6 +68,7 @@ module Coolio
|
|
68
68
|
class UNIXServer < Server
|
69
69
|
def initialize(path, klass = UNIXSocket, *args, &block)
|
70
70
|
s = ::UNIXServer === path ? path : ::UNIXServer.new(path)
|
71
|
+
s.instance_eval { listen(DEFAULT_BACKLOG) }
|
71
72
|
super(s, klass, *args, &block)
|
72
73
|
end
|
73
74
|
end
|
data/lib/cool.io/version.rb
CHANGED
data/spec/dns_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -4,6 +4,13 @@ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
|
4
4
|
require 'rspec'
|
5
5
|
require 'cool.io'
|
6
6
|
|
7
|
+
def unused_port
|
8
|
+
s = TCPServer.open(0)
|
9
|
+
port = s.addr[1]
|
10
|
+
s.close
|
11
|
+
port
|
12
|
+
end
|
13
|
+
|
7
14
|
RSpec.configure do |c|
|
8
15
|
if RUBY_PLATFORM =~ /mingw|win32/
|
9
16
|
$stderr.puts "Skip some specs on Windows"
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
TIMEOUT = 0.010
|
4
|
+
HOST = '127.0.0.1'
|
5
|
+
PORT = unused_port
|
6
|
+
|
7
|
+
def send_data(data)
|
8
|
+
io = TCPSocket.new('127.0.0.1', PORT)
|
9
|
+
begin
|
10
|
+
io.write data
|
11
|
+
ensure
|
12
|
+
io.close
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class MyConnection < Coolio::Socket
|
17
|
+
attr_accessor :data, :connected, :closed
|
18
|
+
|
19
|
+
def initialize(io, on_message)
|
20
|
+
super(io)
|
21
|
+
@on_message = on_message
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_connect
|
25
|
+
@connected = true
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_close
|
29
|
+
@closed = true
|
30
|
+
end
|
31
|
+
|
32
|
+
def on_read(data)
|
33
|
+
@on_message.call(data)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
@data = ""
|
38
|
+
def on_message(data)
|
39
|
+
@data = data
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_run(data = nil)
|
43
|
+
reactor = Coolio::Loop.new
|
44
|
+
server = Cool.io::TCPServer.new(HOST, PORT, MyConnection, method(:on_message))
|
45
|
+
reactor.attach(server)
|
46
|
+
thread = Thread.new { reactor.run }
|
47
|
+
send_data(data) if data
|
48
|
+
sleep TIMEOUT
|
49
|
+
reactor.stop
|
50
|
+
server.detach
|
51
|
+
send_data('') # to leave from blocking loop
|
52
|
+
thread.join
|
53
|
+
@data
|
54
|
+
ensure
|
55
|
+
server.close
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_run_once(data = nil)
|
59
|
+
reactor = Coolio::Loop.new
|
60
|
+
server = Cool.io::TCPServer.new(HOST, PORT, MyConnection, method(:on_message))
|
61
|
+
reactor.attach(server)
|
62
|
+
thread = Thread.new do
|
63
|
+
reactor.run_once # on_connect
|
64
|
+
reactor.run_once # on_read
|
65
|
+
end
|
66
|
+
send_data(data) if data
|
67
|
+
thread.join
|
68
|
+
server.detach
|
69
|
+
@data
|
70
|
+
ensure
|
71
|
+
server.close
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_run_once_timeout(timeout = TIMEOUT)
|
75
|
+
reactor = Coolio::Loop.new
|
76
|
+
server = Cool.io::TCPServer.new(HOST, PORT, MyConnection, method(:on_message))
|
77
|
+
reactor.attach(server)
|
78
|
+
running = true
|
79
|
+
thread = Thread.new { reactor.run_once(timeout) }
|
80
|
+
sleep timeout
|
81
|
+
server.detach
|
82
|
+
thread.join
|
83
|
+
@data
|
84
|
+
ensure
|
85
|
+
server.close
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_run_timeout(data = nil, timeout = TIMEOUT)
|
89
|
+
reactor = Coolio::Loop.new
|
90
|
+
server = Cool.io::TCPServer.new(HOST, PORT, MyConnection, method(:on_message))
|
91
|
+
reactor.attach(server)
|
92
|
+
running = true
|
93
|
+
thread = Thread.new do
|
94
|
+
while running and reactor.has_active_watchers?
|
95
|
+
reactor.run_once(timeout)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
send_data(data) if data
|
99
|
+
sleep timeout
|
100
|
+
server.detach
|
101
|
+
running = false # another send is not required
|
102
|
+
thread.join
|
103
|
+
@data
|
104
|
+
ensure
|
105
|
+
server.close
|
106
|
+
end
|
107
|
+
|
108
|
+
# This test should work on Windows
|
109
|
+
describe Coolio::TCPServer, :env => :win do
|
110
|
+
|
111
|
+
it '#run' do
|
112
|
+
test_run("hello").should == "hello"
|
113
|
+
end
|
114
|
+
|
115
|
+
it '#run_once' do
|
116
|
+
test_run_once("hello").should == "hello"
|
117
|
+
end
|
118
|
+
|
119
|
+
it '#run_once(timeout)' do
|
120
|
+
test_run_once_timeout # should not block
|
121
|
+
end
|
122
|
+
|
123
|
+
it '#run_once(-timeout)' do
|
124
|
+
expect { test_run_once_timeout(-0.1) }.to raise_error(ArgumentError)
|
125
|
+
end
|
126
|
+
|
127
|
+
it '#run(timeout)' do
|
128
|
+
test_run_timeout("hello").should == "hello"
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
data/spec/unix_server_spec.rb
CHANGED
@@ -11,13 +11,15 @@ describe Cool.io::UNIXServer, :env => :win do
|
|
11
11
|
|
12
12
|
it "creates a new Cool.io::UNIXServer" do
|
13
13
|
listener = Cool.io::UNIXListener.new(@tmp.path)
|
14
|
+
listener.listen(24)
|
14
15
|
File.socket?(@tmp.path).should == true
|
15
16
|
end
|
16
17
|
|
17
18
|
it "builds off an existing ::UNIXServer" do
|
18
19
|
unix_server = ::UNIXServer.new(@tmp.path)
|
19
20
|
File.socket?(@tmp.path).should == true
|
20
|
-
listener = Cool.io::UNIXServer.new(unix_server)
|
21
|
+
listener = Cool.io::UNIXServer.new(unix_server, Coolio::UNIXSocket)
|
22
|
+
listener.listen(24)
|
21
23
|
File.socket?(@tmp.path).should == true
|
22
24
|
listener.fileno.should == unix_server.fileno
|
23
25
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cool.io
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 1.2.
|
9
|
+
- 3
|
10
|
+
version: 1.2.3
|
11
11
|
platform: x86-mingw32
|
12
12
|
authors:
|
13
13
|
- Tony Arcieri
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date:
|
19
|
+
date: 2014-04-26 00:00:00 -07:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -130,6 +130,7 @@ files:
|
|
130
130
|
- ext/libev/ev_vars.h
|
131
131
|
- ext/libev/ev_win32.c
|
132
132
|
- ext/libev/ev_wrap.h
|
133
|
+
- ext/libev/ruby_gil.patch
|
133
134
|
- ext/libev/test_libev_win32.c
|
134
135
|
- lib/.gitignore
|
135
136
|
- lib/cool.io.rb
|
@@ -153,6 +154,7 @@ files:
|
|
153
154
|
- spec/dns_spec.rb
|
154
155
|
- spec/spec_helper.rb
|
155
156
|
- spec/stat_watcher_spec.rb
|
157
|
+
- spec/tcp_server_spec.rb
|
156
158
|
- spec/timer_watcher_spec.rb
|
157
159
|
- spec/unix_listener_spec.rb
|
158
160
|
- spec/unix_server_spec.rb
|