cool.io 1.0.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/workflows/test.yaml +54 -0
- data/.gitignore +30 -0
- data/.rspec +3 -0
- data/{CHANGES → CHANGES.md} +175 -79
- data/{README.markdown → README.md} +43 -49
- data/Rakefile +44 -69
- data/appveyor.yml +18 -0
- data/cool.io.gemspec +18 -131
- data/examples/callbacked_echo_server.rb +24 -0
- data/ext/cool.io/buffer.c +781 -0
- data/ext/cool.io/cool.io.h +13 -0
- data/ext/cool.io/cool.io_ext.c +1 -0
- data/ext/cool.io/ev_wrap.h +4 -2
- data/ext/cool.io/extconf.rb +27 -16
- data/ext/cool.io/iowatcher.c +17 -8
- data/ext/cool.io/loop.c +36 -84
- data/ext/cool.io/stat_watcher.c +103 -25
- data/ext/cool.io/timer_watcher.c +3 -3
- data/ext/cool.io/utils.c +5 -0
- data/ext/cool.io/watcher.h +1 -1
- data/ext/libev/Changes +152 -3
- data/ext/libev/LICENSE +2 -1
- data/ext/libev/README +8 -8
- data/ext/libev/ev.c +1719 -348
- data/ext/libev/ev.h +146 -118
- data/ext/libev/ev_epoll.c +71 -20
- data/ext/libev/ev_kqueue.c +36 -16
- data/ext/libev/ev_poll.c +14 -11
- data/ext/libev/ev_port.c +43 -18
- data/ext/libev/ev_select.c +82 -25
- data/ext/libev/ev_vars.h +28 -21
- data/ext/libev/ev_win32.c +19 -10
- data/ext/libev/ev_wrap.h +166 -152
- data/ext/libev/test_libev_win32.c +1 -1
- data/gems.rb +9 -0
- data/lib/cool.io/async_watcher.rb +1 -1
- data/lib/cool.io/custom_require.rb +9 -0
- data/lib/cool.io/dns_resolver.rb +30 -22
- data/lib/cool.io/dsl.rb +32 -28
- data/lib/cool.io/io.rb +55 -35
- data/lib/cool.io/iowatcher.rb +1 -1
- data/lib/cool.io/listener.rb +35 -17
- data/lib/cool.io/loop.rb +17 -25
- data/lib/cool.io/meta.rb +6 -5
- data/lib/cool.io/server.rb +5 -4
- data/lib/cool.io/socket.rb +41 -30
- data/lib/cool.io/timer_watcher.rb +1 -1
- data/lib/cool.io/version.rb +7 -0
- data/lib/cool.io.rb +11 -10
- data/lib/coolio.rb +1 -1
- data/libev_ruby_gil.diff +175 -0
- data/libev_win_select.diff +130 -0
- data/spec/async_watcher_spec.rb +5 -5
- data/spec/dns_spec.rb +27 -8
- data/spec/iobuffer_spec.rb +147 -0
- data/spec/spec_helper.rb +15 -1
- data/spec/stat_watcher_spec.rb +81 -0
- data/spec/tcp_server_spec.rb +225 -0
- data/spec/tcp_socket_spec.rb +185 -0
- data/spec/timer_watcher_spec.rb +23 -19
- data/spec/udp_socket_spec.rb +58 -0
- data/spec/unix_listener_spec.rb +8 -8
- data/spec/unix_server_spec.rb +10 -8
- metadata +109 -112
- data/VERSION +0 -1
- data/examples/httpclient.rb +0 -38
- data/ext/http11_client/.gitignore +0 -5
- data/ext/http11_client/LICENSE +0 -31
- data/ext/http11_client/ext_help.h +0 -14
- data/ext/http11_client/extconf.rb +0 -6
- data/ext/http11_client/http11_client.c +0 -300
- data/ext/http11_client/http11_parser.c +0 -403
- data/ext/http11_client/http11_parser.h +0 -48
- data/ext/http11_client/http11_parser.rl +0 -173
- data/lib/cool.io/eventmachine.rb +0 -234
- data/lib/cool.io/http_client.rb +0 -419
- data/lib/rev.rb +0 -4
- data/spec/possible_tests/schedules_other_threads.rb +0 -48
- data/spec/possible_tests/test_on_resolve_failed.rb +0 -9
- data/spec/possible_tests/test_resolves.rb +0 -27
- data/spec/possible_tests/test_write_during_resolve.rb +0 -27
- data/spec/possible_tests/works_straight.rb +0 -71
data/lib/coolio.rb
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# For those people who don't like the cool.io styling...
|
|
2
|
-
require 'cool.io'
|
|
2
|
+
require 'cool.io'
|
data/libev_ruby_gil.diff
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
diff --git a/ext/libev/ev.c b/ext/libev/ev.c
|
|
2
|
+
index 39b9faf..dae87f1 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
|
+
@@ -107,7 +111,7 @@
|
|
17
|
+
# undef EV_USE_POLL
|
|
18
|
+
# define EV_USE_POLL 0
|
|
19
|
+
# endif
|
|
20
|
+
-
|
|
21
|
+
+
|
|
22
|
+
# if HAVE_EPOLL_CTL && HAVE_SYS_EPOLL_H
|
|
23
|
+
# ifndef EV_USE_EPOLL
|
|
24
|
+
# define EV_USE_EPOLL EV_FEATURE_BACKENDS
|
|
25
|
+
@@ -116,7 +120,7 @@
|
|
26
|
+
# undef EV_USE_EPOLL
|
|
27
|
+
# define EV_USE_EPOLL 0
|
|
28
|
+
# endif
|
|
29
|
+
-
|
|
30
|
+
+
|
|
31
|
+
# if HAVE_KQUEUE && HAVE_SYS_EVENT_H
|
|
32
|
+
# ifndef EV_USE_KQUEUE
|
|
33
|
+
# define EV_USE_KQUEUE EV_FEATURE_BACKENDS
|
|
34
|
+
@@ -125,7 +129,7 @@
|
|
35
|
+
# undef EV_USE_KQUEUE
|
|
36
|
+
# define EV_USE_KQUEUE 0
|
|
37
|
+
# endif
|
|
38
|
+
-
|
|
39
|
+
+
|
|
40
|
+
# if HAVE_PORT_H && HAVE_PORT_CREATE
|
|
41
|
+
# ifndef EV_USE_PORT
|
|
42
|
+
# define EV_USE_PORT EV_FEATURE_BACKENDS
|
|
43
|
+
@@ -161,7 +165,7 @@
|
|
44
|
+
# undef EV_USE_EVENTFD
|
|
45
|
+
# define EV_USE_EVENTFD 0
|
|
46
|
+
# endif
|
|
47
|
+
-
|
|
48
|
+
+
|
|
49
|
+
#endif
|
|
50
|
+
|
|
51
|
+
#include <stdlib.h>
|
|
52
|
+
@@ -2174,7 +2178,7 @@ downheap (ANHE *heap, int N, int k)
|
|
53
|
+
|
|
54
|
+
heap [k] = heap [c];
|
|
55
|
+
ev_active (ANHE_w (heap [k])) = k;
|
|
56
|
+
-
|
|
57
|
+
+
|
|
58
|
+
k = c;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@@ -2594,7 +2598,7 @@ ev_supported_backends (void) EV_THROW
|
|
62
|
+
if (EV_USE_EPOLL ) flags |= EVBACKEND_EPOLL;
|
|
63
|
+
if (EV_USE_POLL ) flags |= EVBACKEND_POLL;
|
|
64
|
+
if (EV_USE_SELECT) flags |= EVBACKEND_SELECT;
|
|
65
|
+
-
|
|
66
|
+
+
|
|
67
|
+
return flags;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@@ -3398,9 +3402,33 @@ time_update (EV_P_ ev_tstamp max_block)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
+/* ########## COOLIO PATCHERY HO! ########## */
|
|
75
|
+
+#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
|
76
|
+
+struct ev_poll_args {
|
|
77
|
+
+ struct ev_loop *loop;
|
|
78
|
+
+ ev_tstamp waittime;
|
|
79
|
+
+};
|
|
80
|
+
+
|
|
81
|
+
+static
|
|
82
|
+
+VALUE ev_backend_poll(void *ptr)
|
|
83
|
+
+{
|
|
84
|
+
+ struct ev_poll_args *args = (struct ev_poll_args *)ptr;
|
|
85
|
+
+ struct ev_loop *loop = args->loop;
|
|
86
|
+
+ backend_poll (EV_A_ args->waittime);
|
|
87
|
+
+ return Qnil;
|
|
88
|
+
+}
|
|
89
|
+
+#endif
|
|
90
|
+
+/* ######################################## */
|
|
91
|
+
+
|
|
92
|
+
int
|
|
93
|
+
ev_run (EV_P_ int flags)
|
|
94
|
+
{
|
|
95
|
+
+/* ########## COOLIO PATCHERY HO! ########## */
|
|
96
|
+
+#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
|
97
|
+
+ struct ev_poll_args poll_args;
|
|
98
|
+
+#endif
|
|
99
|
+
+/* ######################################## */
|
|
100
|
+
+
|
|
101
|
+
#if EV_FEATURE_API
|
|
102
|
+
++loop_depth;
|
|
103
|
+
#endif
|
|
104
|
+
@@ -3518,7 +3546,70 @@ ev_run (EV_P_ int flags)
|
|
105
|
+
++loop_count;
|
|
106
|
+
#endif
|
|
107
|
+
assert ((loop_done = EVBREAK_RECURSE, 1)); /* assert for side effect */
|
|
108
|
+
- backend_poll (EV_A_ waittime);
|
|
109
|
+
+
|
|
110
|
+
+/*
|
|
111
|
+
+########################## COOLIO PATCHERY HO! ##########################
|
|
112
|
+
+
|
|
113
|
+
+According to the grandwizards of Ruby, locking and unlocking of the global
|
|
114
|
+
+interpreter lock are apparently too powerful a concept for a mere mortal to
|
|
115
|
+
+wield (although redefining what + and - do to numbers is totally cool).
|
|
116
|
+
+And so it came to pass that the only acceptable way to release the global
|
|
117
|
+
+interpreter lock is through a convoluted callback system that thakes a
|
|
118
|
+
+function pointer. While the grandwizard of libev foresaw this sort of scenario,
|
|
119
|
+
+he too attempted to place an API with callbacks on it, one that runs before
|
|
120
|
+
+the system call, and one that runs immediately after.
|
|
121
|
+
+
|
|
122
|
+
+And so it came to pass that trying to wrap everything up in callbacks created
|
|
123
|
+
+two incompatible APIs, Ruby's which releases the global interpreter lock and
|
|
124
|
+
+reacquires it when the callback returns, and libev's, which wants two
|
|
125
|
+
+callbacks, one which runs before the polling operation starts, and one which
|
|
126
|
+
+runs after it finishes.
|
|
127
|
+
+
|
|
128
|
+
+These two systems are incompatible as they both want to use callbacks to
|
|
129
|
+
+solve the same problem, however libev wants to use before/after callbacks,
|
|
130
|
+
+and Ruby wants to use an "around" callback. This presents a significant
|
|
131
|
+
+problem as these two patterns of callbacks are diametrical opposites of each
|
|
132
|
+
+other and thus cannot be composed.
|
|
133
|
+
+
|
|
134
|
+
+And thus we are left with no choice but to patch the internals of libev in
|
|
135
|
+
+order to release a mutex at just the precise moment.
|
|
136
|
+
+
|
|
137
|
+
+This is a great example of a situation where granular locking and unlocking
|
|
138
|
+
+of the GVL is practically required. The goal is to get as close to the
|
|
139
|
+
+system call as possible, and to keep the GVL unlocked for the shortest
|
|
140
|
+
+amount of time possible.
|
|
141
|
+
+
|
|
142
|
+
+Perhaps Ruby could benefit from such an API, e.g:
|
|
143
|
+
+
|
|
144
|
+
+rb_thread_unsafe_dangerous_crazy_blocking_region_begin(...);
|
|
145
|
+
+rb_thread_unsafe_dangerous_crazy_blocking_region_end(...);
|
|
146
|
+
+
|
|
147
|
+
+#######################################################################
|
|
148
|
+
+*/
|
|
149
|
+
+
|
|
150
|
+
+/*
|
|
151
|
+
+ simulate to rb_thread_call_without_gvl using rb_theread_blocking_region.
|
|
152
|
+
+ https://github.com/brianmario/mysql2/blob/master/ext/mysql2/client.h#L8
|
|
153
|
+
+*/
|
|
154
|
+
+
|
|
155
|
+
+#ifndef HAVE_RB_THREAD_CALL_WITHOUT_GVL
|
|
156
|
+
+#ifdef HAVE_RB_THREAD_BLOCKING_REGION
|
|
157
|
+
+#define rb_thread_call_without_gvl(func, data1, ubf, data2) \
|
|
158
|
+
+ rb_thread_blocking_region((rb_blocking_function_t *)func, data1, ubf, data2)
|
|
159
|
+
+#endif
|
|
160
|
+
+#endif
|
|
161
|
+
+
|
|
162
|
+
+#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
|
163
|
+
+ poll_args.loop = loop;
|
|
164
|
+
+ poll_args.waittime = waittime;
|
|
165
|
+
+ rb_thread_call_without_gvl(ev_backend_poll, (void *)&poll_args, RUBY_UBF_IO, 0);
|
|
166
|
+
+#else
|
|
167
|
+
+ backend_poll (EV_A_ waittime);
|
|
168
|
+
+#endif
|
|
169
|
+
+/*
|
|
170
|
+
+############################# END PATCHERY ############################
|
|
171
|
+
+*/
|
|
172
|
+
+
|
|
173
|
+
assert ((loop_done = EVBREAK_CANCEL, 1)); /* assert for side effect */
|
|
174
|
+
|
|
175
|
+
pipe_write_wanted = 0; /* just an optimisation, no fence needed */
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
diff --git a/ext/libev/ev.c b/ext/libev/ev.c
|
|
2
|
+
index dae87f1..d15f6bd 100644
|
|
3
|
+
--- a/ext/libev/ev.c
|
|
4
|
+
+++ b/ext/libev/ev.c
|
|
5
|
+
@@ -207,6 +207,7 @@
|
|
6
|
+
#else
|
|
7
|
+
# include <io.h>
|
|
8
|
+
# define WIN32_LEAN_AND_MEAN
|
|
9
|
+
+# define FD_SETSIZE 1024
|
|
10
|
+
# include <winsock2.h>
|
|
11
|
+
# include <windows.h>
|
|
12
|
+
# ifndef EV_SELECT_IS_WINSOCKET
|
|
13
|
+
diff --git a/ext/libev/ev_select.c b/ext/libev/ev_select.c
|
|
14
|
+
index f38d6ca..7050778 100644
|
|
15
|
+
--- a/ext/libev/ev_select.c
|
|
16
|
+
+++ b/ext/libev/ev_select.c
|
|
17
|
+
@@ -67,6 +67,54 @@
|
|
18
|
+
|
|
19
|
+
#include <string.h>
|
|
20
|
+
|
|
21
|
+
+#ifdef _WIN32
|
|
22
|
+
+/*
|
|
23
|
+
+########## COOLIO PATCHERY HO! ##########
|
|
24
|
+
+
|
|
25
|
+
+Ruby undefs FD_* utilities for own implementation.
|
|
26
|
+
+It converts fd argument into socket handle internally on Windows,
|
|
27
|
+
+so libev should not use Ruby's FD_* utilities.
|
|
28
|
+
+
|
|
29
|
+
+Following FD_* utilities come from MinGW.
|
|
30
|
+
+RubyInstaller is built by MinGW so this should work.
|
|
31
|
+
+*/
|
|
32
|
+
+int PASCAL __WSAFDIsSet(SOCKET,fd_set*);
|
|
33
|
+
+#define EV_WIN_FD_CLR(fd,set) do { u_int __i;\
|
|
34
|
+
+for (__i = 0; __i < ((fd_set *)(set))->fd_count ; __i++) {\
|
|
35
|
+
+ if (((fd_set *)(set))->fd_array[__i] == (fd)) {\
|
|
36
|
+
+ while (__i < ((fd_set *)(set))->fd_count-1) {\
|
|
37
|
+
+ ((fd_set*)(set))->fd_array[__i] = ((fd_set*)(set))->fd_array[__i+1];\
|
|
38
|
+
+ __i++;\
|
|
39
|
+
+ }\
|
|
40
|
+
+ ((fd_set*)(set))->fd_count--;\
|
|
41
|
+
+ break;\
|
|
42
|
+
+ }\
|
|
43
|
+
+}\
|
|
44
|
+
+} while (0)
|
|
45
|
+
+#define EV_WIN_FD_SET(fd, set) do { u_int __i;\
|
|
46
|
+
+for (__i = 0; __i < ((fd_set *)(set))->fd_count ; __i++) {\
|
|
47
|
+
+ if (((fd_set *)(set))->fd_array[__i] == (fd)) {\
|
|
48
|
+
+ break;\
|
|
49
|
+
+ }\
|
|
50
|
+
+}\
|
|
51
|
+
+if (__i == ((fd_set *)(set))->fd_count) {\
|
|
52
|
+
+ if (((fd_set *)(set))->fd_count < FD_SETSIZE) {\
|
|
53
|
+
+ ((fd_set *)(set))->fd_array[__i] = (fd);\
|
|
54
|
+
+ ((fd_set *)(set))->fd_count++;\
|
|
55
|
+
+ }\
|
|
56
|
+
+}\
|
|
57
|
+
+} while(0)
|
|
58
|
+
+#define EV_WIN_FD_ZERO(set) (((fd_set *)(set))->fd_count=0)
|
|
59
|
+
+#define EV_WIN_FD_ISSET(fd, set) __WSAFDIsSet((SOCKET)(fd), (fd_set *)(set))
|
|
60
|
+
+#define EV_WIN_FD_COUNT(set) (((fd_set *)(set))->fd_count)
|
|
61
|
+
+/* ######################################## */
|
|
62
|
+
+#else
|
|
63
|
+
+#define EV_WIN_FD_CLR FD_CLR
|
|
64
|
+
+#define EV_WIN_FD_SET FD_SET
|
|
65
|
+
+#define EV_WIN_FD_ZERO FD_ZERO
|
|
66
|
+
+#define EV_WIN_FD_ISSET FD_ISSET
|
|
67
|
+
+#endif
|
|
68
|
+
+
|
|
69
|
+
static void
|
|
70
|
+
select_modify (EV_P_ int fd, int oev, int nev)
|
|
71
|
+
{
|
|
72
|
+
@@ -91,17 +139,17 @@ select_modify (EV_P_ int fd, int oev, int nev)
|
|
73
|
+
if ((oev ^ nev) & EV_READ)
|
|
74
|
+
#endif
|
|
75
|
+
if (nev & EV_READ)
|
|
76
|
+
- FD_SET (handle, (fd_set *)vec_ri);
|
|
77
|
+
+ EV_WIN_FD_SET (handle, (fd_set *)vec_ri);
|
|
78
|
+
else
|
|
79
|
+
- FD_CLR (handle, (fd_set *)vec_ri);
|
|
80
|
+
+ EV_WIN_FD_CLR (handle, (fd_set *)vec_ri);
|
|
81
|
+
|
|
82
|
+
#if EV_SELECT_IS_WINSOCKET
|
|
83
|
+
if ((oev ^ nev) & EV_WRITE)
|
|
84
|
+
#endif
|
|
85
|
+
if (nev & EV_WRITE)
|
|
86
|
+
- FD_SET (handle, (fd_set *)vec_wi);
|
|
87
|
+
+ EV_WIN_FD_SET (handle, (fd_set *)vec_wi);
|
|
88
|
+
else
|
|
89
|
+
- FD_CLR (handle, (fd_set *)vec_wi);
|
|
90
|
+
+ EV_WIN_FD_CLR (handle, (fd_set *)vec_wi);
|
|
91
|
+
|
|
92
|
+
#else
|
|
93
|
+
|
|
94
|
+
@@ -197,8 +245,8 @@ select_poll (EV_P_ ev_tstamp timeout)
|
|
95
|
+
{
|
|
96
|
+
if (timeout)
|
|
97
|
+
{
|
|
98
|
+
- unsigned long ms = timeout * 1e3;
|
|
99
|
+
- Sleep (ms ? ms : 1);
|
|
100
|
+
+ unsigned long ms = (unsigned long)(timeout * 1e3);
|
|
101
|
+
+ SleepEx (ms ? ms : 1, TRUE);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return;
|
|
105
|
+
@@ -230,10 +278,10 @@ select_poll (EV_P_ ev_tstamp timeout)
|
|
106
|
+
int handle = fd;
|
|
107
|
+
#endif
|
|
108
|
+
|
|
109
|
+
- if (FD_ISSET (handle, (fd_set *)vec_ro)) events |= EV_READ;
|
|
110
|
+
- if (FD_ISSET (handle, (fd_set *)vec_wo)) events |= EV_WRITE;
|
|
111
|
+
+ if (EV_WIN_FD_ISSET (handle, (fd_set *)vec_ro)) events |= EV_READ;
|
|
112
|
+
+ if (EV_WIN_FD_ISSET (handle, (fd_set *)vec_wo)) events |= EV_WRITE;
|
|
113
|
+
#ifdef _WIN32
|
|
114
|
+
- if (FD_ISSET (handle, (fd_set *)vec_eo)) events |= EV_WRITE;
|
|
115
|
+
+ if (EV_WIN_FD_ISSET (handle, (fd_set *)vec_eo)) events |= EV_WRITE;
|
|
116
|
+
#endif
|
|
117
|
+
|
|
118
|
+
if (expect_true (events))
|
|
119
|
+
@@ -279,9 +327,9 @@ select_init (EV_P_ int flags)
|
|
120
|
+
backend_poll = select_poll;
|
|
121
|
+
|
|
122
|
+
#if EV_SELECT_USE_FD_SET
|
|
123
|
+
- vec_ri = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_ri);
|
|
124
|
+
+ vec_ri = ev_malloc (sizeof (fd_set)); EV_WIN_FD_ZERO ((fd_set *)vec_ri);
|
|
125
|
+
vec_ro = ev_malloc (sizeof (fd_set));
|
|
126
|
+
- vec_wi = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_wi);
|
|
127
|
+
+ vec_wi = ev_malloc (sizeof (fd_set)); EV_WIN_FD_ZERO ((fd_set *)vec_wi);
|
|
128
|
+
vec_wo = ev_malloc (sizeof (fd_set));
|
|
129
|
+
#ifdef _WIN32
|
|
130
|
+
vec_eo = ev_malloc (sizeof (fd_set));
|
data/spec/async_watcher_spec.rb
CHANGED
|
@@ -2,7 +2,7 @@ require File.expand_path('../spec_helper', __FILE__)
|
|
|
2
2
|
require 'tempfile'
|
|
3
3
|
require 'fcntl'
|
|
4
4
|
|
|
5
|
-
describe Cool.io::AsyncWatcher do
|
|
5
|
+
describe Cool.io::AsyncWatcher, :env => :exclude_win do
|
|
6
6
|
|
|
7
7
|
it "does not signal on spurious wakeups" do
|
|
8
8
|
aw = Cool.io::AsyncWatcher.new
|
|
@@ -30,7 +30,7 @@ describe Cool.io::AsyncWatcher do
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
# ensure children are ready
|
|
33
|
-
nr_fork.times { rd.sysread(1).
|
|
33
|
+
nr_fork.times { expect(rd.sysread(1)).to eq('.') }
|
|
34
34
|
|
|
35
35
|
# send our signals
|
|
36
36
|
nr_signal.times { aw.signal }
|
|
@@ -41,15 +41,15 @@ describe Cool.io::AsyncWatcher do
|
|
|
41
41
|
children.each do |pid|
|
|
42
42
|
Process.kill(:TERM, pid)
|
|
43
43
|
_, status = Process.waitpid2(pid)
|
|
44
|
-
status.exitstatus.
|
|
44
|
+
expect(status.exitstatus).to eq(0)
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
# we should've written a line for every signal we sent
|
|
48
48
|
lines = tmp.readlines
|
|
49
|
-
lines.size.
|
|
49
|
+
expect(lines.size).to eq(nr_signal)
|
|
50
50
|
|
|
51
51
|
# theoretically a bad kernel scheduler could give us fewer...
|
|
52
|
-
lines.sort.uniq.size.
|
|
52
|
+
expect(lines.sort.uniq.size).to eq(nr_fork)
|
|
53
53
|
|
|
54
54
|
tmp.close!
|
|
55
55
|
end
|
data/spec/dns_spec.rb
CHANGED
|
@@ -19,21 +19,40 @@ end
|
|
|
19
19
|
describe "DNS" do
|
|
20
20
|
before :each do
|
|
21
21
|
@loop = Cool.io::Loop.new
|
|
22
|
+
@preferred_localhost_address = ::Socket.getaddrinfo("localhost", nil).first[3]
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
it "connects to valid domains" do
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
begin
|
|
27
|
+
c = ConnectorThingy.connect(VALID_DOMAIN, 80).attach(@loop)
|
|
28
|
+
|
|
29
|
+
expect do
|
|
30
|
+
@loop.run
|
|
31
|
+
end.to raise_error(ItWorked)
|
|
32
|
+
ensure
|
|
33
|
+
c.close
|
|
34
|
+
end
|
|
30
35
|
end
|
|
31
36
|
|
|
32
37
|
it "fires on_resolve_failed for invalid domains" do
|
|
33
38
|
ConnectorThingy.connect(INVALID_DOMAIN, 80).attach(@loop)
|
|
34
39
|
|
|
35
|
-
|
|
40
|
+
expect do
|
|
36
41
|
@loop.run
|
|
37
|
-
end.
|
|
42
|
+
end.to raise_error(WontResolve)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "resolve localhost even though hosts is empty" do
|
|
46
|
+
Tempfile.open("empty") do |file|
|
|
47
|
+
expect( Coolio::DNSResolver.hosts("localhost", file.path)).to eq @preferred_localhost_address
|
|
48
|
+
end
|
|
38
49
|
end
|
|
39
|
-
|
|
50
|
+
|
|
51
|
+
it "resolve missing localhost even though hosts entries exist" do
|
|
52
|
+
Tempfile.open("empty") do |file|
|
|
53
|
+
file.puts("127.0.0.1 example.internal")
|
|
54
|
+
file.flush
|
|
55
|
+
expect( Coolio::DNSResolver.hosts("localhost", file.path)).to eq @preferred_localhost_address
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
describe Cool.io::Buffer do
|
|
5
|
+
|
|
6
|
+
let :buffer do
|
|
7
|
+
Cool.io::Buffer.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "provides a subset of the methods available in Strings" do
|
|
11
|
+
expect(buffer << "foo").to eq "foo"
|
|
12
|
+
expect(buffer << "bar").to eq "bar"
|
|
13
|
+
expect(buffer.to_str).to eq "foobar"
|
|
14
|
+
expect(buffer.to_str).to eq "foobar"
|
|
15
|
+
expect(buffer.size).to eq 6
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "provides append and prepend" do
|
|
19
|
+
expect(buffer.append "bar").to eq "bar"
|
|
20
|
+
expect(buffer.prepend "foo").to eq "foo"
|
|
21
|
+
expect(buffer.append "baz").to eq "baz"
|
|
22
|
+
expect(buffer.to_str).to eq "foobarbaz"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context "#read" do
|
|
26
|
+
it "can be used to retrieve the contents of a buffer" do
|
|
27
|
+
expect(buffer << "foo").to eq "foo"
|
|
28
|
+
expect(buffer.read 2).to eq "fo"
|
|
29
|
+
expect(buffer << "bar").to eq "bar"
|
|
30
|
+
expect(buffer.read 2).to eq "ob"
|
|
31
|
+
expect(buffer << "baz").to eq "baz"
|
|
32
|
+
expect(buffer.read 3).to eq "arb"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe "provides methods for performing non-blocking I/O" do
|
|
37
|
+
require "tempfile"
|
|
38
|
+
|
|
39
|
+
context "#read_from" do
|
|
40
|
+
context "using local file", :env => :exclude_win do
|
|
41
|
+
let :tmp do
|
|
42
|
+
t = Tempfile.open "read_from"
|
|
43
|
+
t << "foobar"
|
|
44
|
+
t.rewind
|
|
45
|
+
t
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "will read as much data as possible" do
|
|
49
|
+
expect(buffer.read_from tmp).to eq 6
|
|
50
|
+
expect(buffer.to_str).to eq "foobar"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
context "using udp socket" do
|
|
55
|
+
before :each do
|
|
56
|
+
@receiver = UDPSocket.open
|
|
57
|
+
@receiver.bind nil, 0
|
|
58
|
+
|
|
59
|
+
@sender = UDPSocket.open
|
|
60
|
+
@sender.connect "localhost", @receiver.addr[1]
|
|
61
|
+
end
|
|
62
|
+
after :each do
|
|
63
|
+
@receiver.close
|
|
64
|
+
@sender.close
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "will read as much data as possible" do
|
|
68
|
+
select [], [@sender]
|
|
69
|
+
@sender.send "foo", 0
|
|
70
|
+
select [@receiver]
|
|
71
|
+
expect(buffer.read_from @receiver).to eq 3
|
|
72
|
+
expect(buffer.to_str).to eq "foo"
|
|
73
|
+
|
|
74
|
+
select [], [@sender]
|
|
75
|
+
@sender.send "barbaz", 0
|
|
76
|
+
select [@receiver]
|
|
77
|
+
expect(buffer.read_from @receiver).to eq 6
|
|
78
|
+
expect(buffer.to_str).to eq "foobarbaz"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
context "#write_to" do
|
|
84
|
+
context "using local file", :env => :exclude_win do
|
|
85
|
+
let :tmp do
|
|
86
|
+
Tempfile.open "write_to"
|
|
87
|
+
end
|
|
88
|
+
it "writes the contents of the buffer" do
|
|
89
|
+
buffer << "foo"
|
|
90
|
+
expect(buffer.write_to tmp).to eq 3
|
|
91
|
+
tmp.rewind
|
|
92
|
+
expect(tmp.read 3).to eq "foo"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
context "using udp socket" do
|
|
97
|
+
before :each do
|
|
98
|
+
@receiver = UDPSocket.open
|
|
99
|
+
@receiver.bind nil, 0
|
|
100
|
+
|
|
101
|
+
@sender = UDPSocket.open
|
|
102
|
+
@sender.connect "localhost", @receiver.addr[1]
|
|
103
|
+
end
|
|
104
|
+
after :each do
|
|
105
|
+
@receiver.close
|
|
106
|
+
@sender.close
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "will read as much data as possible" do
|
|
110
|
+
buffer << "foo"
|
|
111
|
+
select [], [@sender]
|
|
112
|
+
expect(buffer.write_to @sender).to eq 3
|
|
113
|
+
select [@receiver]
|
|
114
|
+
expect(@receiver.recvfrom_nonblock(3)[0]).to eq "foo"
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
context "#clear" do
|
|
121
|
+
it "clear all data" do
|
|
122
|
+
buffer << "foo"
|
|
123
|
+
expect(buffer.size).to eq 3
|
|
124
|
+
expect(buffer.empty?).to eq false
|
|
125
|
+
buffer.clear
|
|
126
|
+
expect(buffer.size).to eq 0
|
|
127
|
+
expect(buffer.empty?).to eq true
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
context "#read_frame" do
|
|
132
|
+
it "Read up to and including the given frame marker" do
|
|
133
|
+
buffer << "foo\nbarbaz"
|
|
134
|
+
data = ""
|
|
135
|
+
expect(buffer.read_frame data, "\n".ord).to eq true
|
|
136
|
+
expect(buffer.empty?).to eq false
|
|
137
|
+
expect(data).to eq "foo\n"
|
|
138
|
+
expect(buffer.to_str).to eq "barbaz"
|
|
139
|
+
|
|
140
|
+
expect(buffer.read_frame data, "\n".ord).to eq false
|
|
141
|
+
expect(buffer.empty?).to eq true
|
|
142
|
+
expect(data).to eq "foo\nbarbaz"
|
|
143
|
+
expect(buffer.to_str).to eq ""
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -2,4 +2,18 @@ $LOAD_PATH.unshift File.dirname(__FILE__)
|
|
|
2
2
|
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
|
3
3
|
|
|
4
4
|
require 'rspec'
|
|
5
|
-
require 'cool.io'
|
|
5
|
+
require 'cool.io'
|
|
6
|
+
|
|
7
|
+
def unused_port
|
|
8
|
+
s = TCPServer.open(0)
|
|
9
|
+
port = s.addr[1]
|
|
10
|
+
s.close
|
|
11
|
+
port
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
RSpec.configure do |c|
|
|
15
|
+
if RUBY_PLATFORM =~ /mingw|mswin/
|
|
16
|
+
$stderr.puts "Skip some specs on Windows"
|
|
17
|
+
c.filter_run_excluding :env => :exclude_win
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
|
2
|
+
|
|
3
|
+
TEMP_FILE_PATH = "./test.txt"
|
|
4
|
+
|
|
5
|
+
INTERVAL = 0.010
|
|
6
|
+
|
|
7
|
+
class MyStatWatcher < Cool.io::StatWatcher
|
|
8
|
+
attr_accessor :accessed, :previous, :current
|
|
9
|
+
|
|
10
|
+
def initialize(path)
|
|
11
|
+
super path, INTERVAL
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def on_change(previous, current)
|
|
15
|
+
self.accessed = true
|
|
16
|
+
self.previous = previous
|
|
17
|
+
self.current = current
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def run_with_file_change(path)
|
|
22
|
+
reactor = Cool.io::Loop.new
|
|
23
|
+
|
|
24
|
+
sw = MyStatWatcher.new(path)
|
|
25
|
+
sw.attach(reactor)
|
|
26
|
+
|
|
27
|
+
tw = Cool.io::TimerWatcher.new(INTERVAL, true)
|
|
28
|
+
tw.on_timer do
|
|
29
|
+
reactor.stop if sw.accessed
|
|
30
|
+
write_file(path)
|
|
31
|
+
end
|
|
32
|
+
tw.attach(reactor)
|
|
33
|
+
|
|
34
|
+
reactor.run
|
|
35
|
+
|
|
36
|
+
tw.detach
|
|
37
|
+
sw.detach
|
|
38
|
+
|
|
39
|
+
sw
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def write_file(path)
|
|
43
|
+
File.open(path, "w+") { |f| f.write(rand.to_s) }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def delete_file(path)
|
|
47
|
+
File.delete(TEMP_FILE_PATH)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe Cool.io::StatWatcher do
|
|
51
|
+
|
|
52
|
+
let :watcher do
|
|
53
|
+
run_with_file_change(TEMP_FILE_PATH)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
before :each do
|
|
57
|
+
write_file(TEMP_FILE_PATH)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
after :each do
|
|
61
|
+
delete_file(TEMP_FILE_PATH)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "fire on_change when the file it is watching is modified" do
|
|
65
|
+
expect(watcher.accessed).to eq(true)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "should pass previous and current file stat info given a stat watcher" do
|
|
69
|
+
expect(watcher.previous.ino).to eq(watcher.current.ino)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "should raise when the handler does not take 2 parameters" do
|
|
73
|
+
class MyStatWatcher < Cool.io::StatWatcher
|
|
74
|
+
remove_method :on_change
|
|
75
|
+
def on_change
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
expect { watcher.accessed }.to raise_error(ArgumentError)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end
|