polyphony 0.13 → 0.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.gitbook.yaml +5 -0
  3. data/.gitignore +55 -0
  4. data/.rubocop.yml +49 -0
  5. data/CHANGELOG.md +13 -2
  6. data/Gemfile +3 -0
  7. data/Gemfile.lock +31 -0
  8. data/LICENSE +21 -0
  9. data/README.md +35 -18
  10. data/Rakefile +20 -0
  11. data/TODO.md +49 -0
  12. data/docs/getting-started/getting-started.md +10 -0
  13. data/docs/getting-started/tutorial.md +2 -0
  14. data/docs/summary.md +9 -0
  15. data/examples/core/cancel.rb +10 -0
  16. data/examples/core/channel_echo.rb +43 -0
  17. data/examples/core/enumerator.rb +14 -0
  18. data/examples/core/fork.rb +22 -0
  19. data/examples/core/genserver.rb +74 -0
  20. data/examples/core/lock.rb +20 -0
  21. data/examples/core/move_on.rb +11 -0
  22. data/examples/core/move_on_twice.rb +17 -0
  23. data/examples/core/move_on_with_ensure.rb +17 -0
  24. data/examples/core/multiple_async.rb +17 -0
  25. data/examples/core/nested_async.rb +18 -0
  26. data/examples/core/nested_cancel.rb +41 -0
  27. data/examples/core/nested_multiple_async.rb +19 -0
  28. data/examples/core/next_tick.rb +13 -0
  29. data/examples/core/pulse.rb +13 -0
  30. data/examples/core/resource.rb +29 -0
  31. data/examples/core/resource_cancel.rb +34 -0
  32. data/examples/core/resource_delegate.rb +32 -0
  33. data/examples/core/sleep.rb +9 -0
  34. data/examples/core/sleep2.rb +13 -0
  35. data/examples/core/spawn.rb +15 -0
  36. data/examples/core/spawn_cancel.rb +19 -0
  37. data/examples/core/spawn_error.rb +28 -0
  38. data/examples/core/supervisor.rb +22 -0
  39. data/examples/core/supervisor_with_cancel_scope.rb +24 -0
  40. data/examples/core/supervisor_with_error.rb +23 -0
  41. data/examples/core/supervisor_with_manual_move_on.rb +25 -0
  42. data/examples/core/thread.rb +30 -0
  43. data/examples/core/thread_cancel.rb +30 -0
  44. data/examples/core/thread_pool.rb +60 -0
  45. data/examples/core/throttle.rb +17 -0
  46. data/examples/fs/read.rb +37 -0
  47. data/examples/interfaces/pg_client.rb +38 -0
  48. data/examples/interfaces/pg_pool.rb +37 -0
  49. data/examples/interfaces/pg_query.rb +32 -0
  50. data/examples/interfaces/redis_channels.rb +119 -0
  51. data/examples/interfaces/redis_client.rb +21 -0
  52. data/examples/interfaces/redis_pubsub.rb +26 -0
  53. data/examples/interfaces/redis_pubsub_perf.rb +65 -0
  54. data/examples/io/config.ru +3 -0
  55. data/examples/io/echo_client.rb +22 -0
  56. data/examples/io/echo_server.rb +14 -0
  57. data/examples/io/echo_server_with_timeout.rb +33 -0
  58. data/examples/io/echo_stdin.rb +15 -0
  59. data/examples/io/happy_eyeballs.rb +32 -0
  60. data/examples/io/http_client.rb +19 -0
  61. data/examples/io/http_server.js +24 -0
  62. data/examples/io/http_server.rb +16 -0
  63. data/examples/io/http_server_forked.rb +27 -0
  64. data/examples/io/http_server_throttled.rb +16 -0
  65. data/examples/io/http_ws_server.rb +42 -0
  66. data/examples/io/https_client.rb +17 -0
  67. data/examples/io/https_server.rb +23 -0
  68. data/examples/io/https_wss_server.rb +46 -0
  69. data/examples/io/rack_server.rb +19 -0
  70. data/examples/io/rack_server_https.rb +24 -0
  71. data/examples/io/rack_server_https_forked.rb +32 -0
  72. data/examples/io/websocket_server.rb +33 -0
  73. data/examples/io/ws_page.html +34 -0
  74. data/examples/io/wss_page.html +34 -0
  75. data/examples/performance/perf_multi_snooze.rb +21 -0
  76. data/examples/performance/perf_snooze.rb +30 -0
  77. data/examples/performance/thread-vs-fiber/polyphony_server.rb +63 -0
  78. data/examples/performance/thread-vs-fiber/threaded_server.rb +27 -0
  79. data/examples/streams/lines.rb +27 -0
  80. data/examples/streams/stdio.rb +18 -0
  81. data/ext/ev/async.c +168 -0
  82. data/ext/ev/child.c +169 -0
  83. data/ext/ev/ev.h +32 -0
  84. data/ext/ev/ev_ext.c +20 -0
  85. data/ext/ev/ev_module.c +222 -0
  86. data/ext/ev/io.c +405 -0
  87. data/ext/ev/libev.h +9 -0
  88. data/ext/ev/signal.c +119 -0
  89. data/ext/ev/timer.c +197 -0
  90. data/ext/libev/Changes +513 -0
  91. data/ext/libev/LICENSE +37 -0
  92. data/ext/libev/README +58 -0
  93. data/ext/libev/README.embed +3 -0
  94. data/ext/libev/ev.c +5214 -0
  95. data/ext/libev/ev.h +849 -0
  96. data/ext/libev/ev_epoll.c +285 -0
  97. data/ext/libev/ev_kqueue.c +218 -0
  98. data/ext/libev/ev_poll.c +151 -0
  99. data/ext/libev/ev_port.c +189 -0
  100. data/ext/libev/ev_select.c +316 -0
  101. data/ext/libev/ev_vars.h +204 -0
  102. data/ext/libev/ev_win32.c +162 -0
  103. data/ext/libev/ev_wrap.h +200 -0
  104. data/ext/libev/test_libev_win32.c +123 -0
  105. data/lib/polyphony.rb +7 -2
  106. data/lib/polyphony/core.rb +1 -1
  107. data/lib/polyphony/core/{coroutine.rb → coprocess.rb} +10 -10
  108. data/lib/polyphony/core/exceptions.rb +5 -5
  109. data/lib/polyphony/core/supervisor.rb +16 -16
  110. data/lib/polyphony/core/thread.rb +1 -1
  111. data/lib/polyphony/extensions/io.rb +43 -42
  112. data/lib/polyphony/extensions/kernel.rb +10 -34
  113. data/lib/polyphony/extensions/postgres.rb +3 -2
  114. data/lib/polyphony/extensions/redis.rb +1 -1
  115. data/lib/polyphony/extensions/socket.rb +8 -4
  116. data/lib/polyphony/extensions/ssl.rb +0 -54
  117. data/lib/polyphony/http/agent.rb +4 -10
  118. data/lib/polyphony/http/http1.rb +25 -25
  119. data/lib/polyphony/http/http1_request.rb +38 -26
  120. data/lib/polyphony/http/http2.rb +4 -5
  121. data/lib/polyphony/http/http2_request.rb +12 -18
  122. data/lib/polyphony/http/rack.rb +1 -3
  123. data/lib/polyphony/http/server.rb +9 -9
  124. data/lib/polyphony/net.rb +2 -2
  125. data/lib/polyphony/resource_pool.rb +5 -1
  126. data/lib/polyphony/version.rb +1 -1
  127. data/lib/polyphony/websocket.rb +52 -0
  128. data/polyphony.gemspec +31 -0
  129. data/test/test_coprocess.rb +131 -0
  130. data/test/test_core.rb +274 -0
  131. data/test/test_ev.rb +117 -0
  132. data/test/test_io.rb +38 -0
  133. metadata +113 -7
  134. data/lib/polyphony/core/async.rb +0 -36
  135. data/lib/polyphony/net_old.rb +0 -299
@@ -0,0 +1,169 @@
1
+ #include "ev.h"
2
+
3
+ struct EV_Child {
4
+ struct ev_child ev_child;
5
+ int active;
6
+ int pid;
7
+ VALUE self;
8
+ VALUE callback;
9
+ VALUE fiber;
10
+ };
11
+
12
+ static VALUE mEV = Qnil;
13
+ static VALUE cEV_Child = Qnil;
14
+
15
+ /* Allocator/deallocator */
16
+ static VALUE EV_Child_allocate(VALUE klass);
17
+ static void EV_Child_mark(void *ptr);
18
+ static void EV_Child_free(void *ptr);
19
+ static size_t EV_Child_size(const void *ptr);
20
+
21
+ /* Methods */
22
+ static VALUE EV_Child_initialize(VALUE self, VALUE pid);
23
+
24
+ static VALUE EV_Child_start(VALUE self);
25
+ static VALUE EV_Child_stop(VALUE self);
26
+ static VALUE EV_Child_await(VALUE self);
27
+
28
+ void EV_Child_callback(ev_loop *ev_loop, struct ev_child *child, int revents);
29
+
30
+ /* Child encapsulates an child watcher */
31
+ void Init_EV_Child() {
32
+ mEV = rb_define_module("EV");
33
+ cEV_Child = rb_define_class_under(mEV, "Child", rb_cData);
34
+ rb_define_alloc_func(cEV_Child, EV_Child_allocate);
35
+
36
+ rb_define_method(cEV_Child, "initialize", EV_Child_initialize, 1);
37
+ rb_define_method(cEV_Child, "start", EV_Child_start, 0);
38
+ rb_define_method(cEV_Child, "stop", EV_Child_stop, 0);
39
+ rb_define_method(cEV_Child, "await", EV_Child_await, 0);
40
+ }
41
+
42
+ static const rb_data_type_t EV_Child_type = {
43
+ "EV_Child",
44
+ {EV_Child_mark, EV_Child_free, EV_Child_size,},
45
+ 0, 0,
46
+ RUBY_TYPED_FREE_IMMEDIATELY,
47
+ };
48
+
49
+ static VALUE EV_Child_allocate(VALUE klass) {
50
+ struct EV_Child *child = (struct EV_Child *)xmalloc(sizeof(struct EV_Child));
51
+ return TypedData_Wrap_Struct(klass, &EV_Child_type, child);
52
+ }
53
+
54
+ static void EV_Child_mark(void *ptr) {
55
+ struct EV_Child *child = ptr;
56
+ if (child->callback != Qnil) {
57
+ rb_gc_mark(child->callback);
58
+ }
59
+ if (child->fiber != Qnil) {
60
+ rb_gc_mark(child->fiber);
61
+ }
62
+ }
63
+
64
+ static void EV_Child_free(void *ptr) {
65
+ struct EV_Child *child = ptr;
66
+ if (child->active) {
67
+ ev_child_stop(EV_DEFAULT, &child->ev_child);
68
+ }
69
+ xfree(child);
70
+ }
71
+
72
+ static size_t EV_Child_size(const void *ptr) {
73
+ return sizeof(struct EV_Child);
74
+ }
75
+
76
+ #define GetEV_Child(obj, child) \
77
+ TypedData_Get_Struct((obj), struct EV_Child, &EV_Child_type, (child))
78
+
79
+ static VALUE EV_Child_initialize(VALUE self, VALUE pid) {
80
+ struct EV_Child *child;
81
+
82
+ GetEV_Child(self, child);
83
+
84
+ child->self = self;
85
+ child->callback = Qnil;
86
+ child->fiber = Qnil;
87
+ child->pid = NUM2INT(pid);
88
+ child->active = 0;
89
+
90
+ ev_child_init(&child->ev_child, EV_Child_callback, child->pid, 0);
91
+
92
+ return Qnil;
93
+ }
94
+
95
+ void EV_Child_callback(ev_loop *ev_loop, struct ev_child *ev_child, int revents) {
96
+ VALUE fiber;
97
+ VALUE resume_value;
98
+ struct EV_Child *child = (struct EV_Child*)ev_child;
99
+ resume_value = INT2NUM(child->pid);
100
+
101
+ child->active = 0;
102
+ ev_child_stop(EV_DEFAULT, ev_child);
103
+ EV_del_watcher_ref(child->self);
104
+
105
+ if (child->fiber != Qnil) {
106
+ fiber = child->fiber;
107
+ child->fiber = Qnil;
108
+ SCHEDULE_FIBER(fiber, 1, resume_value);
109
+ }
110
+ else if (child->callback != Qnil) {
111
+ rb_funcall(child->callback, ID_call, 1, resume_value);
112
+ }
113
+ }
114
+
115
+ static VALUE EV_Child_start(VALUE self) {
116
+ struct EV_Child *child;
117
+ GetEV_Child(self, child);
118
+
119
+ if (rb_block_given_p()) {
120
+ child->callback = rb_block_proc();
121
+ }
122
+
123
+ if (!child->active) {
124
+ ev_child_start(EV_DEFAULT, &child->ev_child);
125
+ child->active = 1;
126
+ EV_add_watcher_ref(self);
127
+ }
128
+
129
+ return self;
130
+ }
131
+
132
+ static VALUE EV_Child_stop(VALUE self) {
133
+ struct EV_Child *child;
134
+ GetEV_Child(self, child);
135
+
136
+ if (child->active) {
137
+ ev_child_stop(EV_DEFAULT, &child->ev_child);
138
+ child->active = 0;
139
+ EV_del_watcher_ref(self);
140
+ }
141
+
142
+ return self;
143
+ }
144
+
145
+ static VALUE EV_Child_await(VALUE self) {
146
+ struct EV_Child *child;
147
+ VALUE ret;
148
+
149
+ GetEV_Child(self, child);
150
+
151
+ child->fiber = rb_fiber_current();
152
+ child->active = 1;
153
+ ev_child_start(EV_DEFAULT, &child->ev_child);
154
+ EV_add_watcher_ref(self);
155
+
156
+ ret = YIELD_TO_REACTOR();
157
+
158
+ // fiber is resumed, check if resumed value is an exception
159
+ if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
160
+ if (child->active) {
161
+ child->active = 0;
162
+ ev_child_stop(EV_DEFAULT, &child->ev_child);
163
+ }
164
+ return rb_funcall(ret, ID_raise, 1, ret);
165
+ }
166
+ else {
167
+ return ret;
168
+ }
169
+ }
@@ -0,0 +1,32 @@
1
+ #ifndef RUBY_EV_H
2
+ #define RUBY_EV_H
3
+
4
+ #include "ruby.h"
5
+ #include "ruby/io.h"
6
+ #include "libev.h"
7
+
8
+ void EV_add_watcher_ref(VALUE obj);
9
+ void EV_del_watcher_ref(VALUE obj);
10
+ void EV_async_free(void *p);
11
+
12
+ #define SCHEDULE_FIBER(obj, args...) rb_funcall(obj, ID_transfer, args)
13
+ #define YIELD_TO_REACTOR() rb_funcall(EV_reactor_fiber, ID_transfer, 0)
14
+
15
+ extern VALUE EV_reactor_fiber;
16
+ extern VALUE EV_root_fiber;
17
+
18
+ extern ID ID_call;
19
+ extern ID ID_caller;
20
+ extern ID ID_clear;
21
+ extern ID ID_each;
22
+ extern ID ID_inspect;
23
+ extern ID ID_raise;
24
+ extern ID ID_read_watcher;
25
+ extern ID ID_scheduled_value;
26
+ extern ID ID_transfer;
27
+ extern ID ID_write_watcher;
28
+ extern ID ID_R;
29
+ extern ID ID_W;
30
+ extern ID ID_RW;
31
+
32
+ #endif /* RUBY_EV_H */
@@ -0,0 +1,20 @@
1
+ #include "ev.h"
2
+ #include "../libev/ev.c"
3
+
4
+ void Init_EV();
5
+ void Init_EV_Async();
6
+ void Init_EV_Child();
7
+ void Init_EV_IO();
8
+ void Init_EV_Signal();
9
+ void Init_EV_Timer();
10
+
11
+ void Init_ev_ext() {
12
+ ev_set_allocator(xrealloc);
13
+
14
+ Init_EV();
15
+ Init_EV_Async();
16
+ Init_EV_Child();
17
+ Init_EV_IO();
18
+ Init_EV_Signal();
19
+ Init_EV_Timer();
20
+ }
@@ -0,0 +1,222 @@
1
+ #include "ev.h"
2
+
3
+ static VALUE mEV = Qnil;
4
+
5
+ static VALUE EV_run(VALUE self);
6
+ static VALUE EV_break(VALUE self);
7
+ static VALUE EV_restart(VALUE self);
8
+ static VALUE EV_rerun(VALUE self);
9
+
10
+ static VALUE EV_ref(VALUE self);
11
+ static VALUE EV_unref(VALUE self);
12
+
13
+ static VALUE EV_next_tick(VALUE self);
14
+ static VALUE EV_snooze(VALUE self);
15
+ static VALUE EV_post_fork(VALUE self);
16
+
17
+ static VALUE EV_suspend(VALUE self);
18
+ static VALUE EV_schedule_fiber(VALUE self, VALUE fiber, VALUE value);
19
+
20
+ static VALUE watcher_refs;
21
+ static VALUE next_tick_procs;
22
+
23
+ static struct ev_timer next_tick_timer;
24
+ static int next_tick_active;
25
+ void EV_next_tick_callback(ev_loop *ev_loop, struct ev_timer *timer, int revents);
26
+
27
+ VALUE EV_reactor_fiber;
28
+ VALUE EV_root_fiber;
29
+
30
+ ID ID_call;
31
+ ID ID_caller;
32
+ ID ID_clear;
33
+ ID ID_each;
34
+ ID ID_inspect;
35
+ ID ID_raise;
36
+ ID ID_read_watcher;
37
+ ID ID_scheduled_value;
38
+ ID ID_transfer;
39
+ ID ID_write_watcher;
40
+ ID ID_R;
41
+ ID ID_W;
42
+ ID ID_RW;
43
+
44
+ void Init_EV() {
45
+ mEV = rb_define_module("EV");
46
+
47
+ rb_define_singleton_method(mEV, "break", EV_break, 0);
48
+ rb_define_singleton_method(mEV, "restart", EV_restart, 0);
49
+ rb_define_singleton_method(mEV, "rerun", EV_rerun, 0);
50
+ rb_define_singleton_method(mEV, "ref", EV_ref, 0);
51
+ rb_define_singleton_method(mEV, "unref", EV_unref, 0);
52
+ rb_define_singleton_method(mEV, "next_tick", EV_next_tick, 0);
53
+ rb_define_singleton_method(mEV, "snooze", EV_snooze, 0);
54
+ rb_define_singleton_method(mEV, "post_fork", EV_post_fork, 0);
55
+ rb_define_singleton_method(mEV, "schedule_fiber", EV_schedule_fiber, 2);
56
+
57
+ rb_define_method(rb_mKernel, "suspend", EV_suspend, 0);
58
+
59
+ ID_call = rb_intern("call");
60
+ ID_caller = rb_intern("caller");
61
+ ID_clear = rb_intern("clear");
62
+ ID_each = rb_intern("each");
63
+ ID_inspect = rb_intern("inspect");
64
+ ID_raise = rb_intern("raise");
65
+ ID_read_watcher = rb_intern("read_watcher");
66
+ ID_scheduled_value = rb_intern("@scheduled_value");
67
+ ID_transfer = rb_intern("transfer");
68
+ ID_write_watcher = rb_intern("write_watcher");
69
+ ID_R = rb_intern("r");
70
+ ID_W = rb_intern("w");
71
+ ID_RW = rb_intern("rw");
72
+
73
+ EV_root_fiber = rb_fiber_current();
74
+ EV_reactor_fiber = rb_fiber_new(EV_run, Qnil);
75
+ rb_gv_set("__reactor_fiber__", EV_reactor_fiber);
76
+
77
+
78
+ watcher_refs = rb_hash_new();
79
+ rb_global_variable(&watcher_refs);
80
+
81
+ next_tick_procs = rb_ary_new();
82
+ rb_global_variable(&next_tick_procs);
83
+
84
+ ev_timer_init(&next_tick_timer, EV_next_tick_callback, 0., 0.);
85
+ next_tick_active = 0;
86
+ }
87
+
88
+ static VALUE EV_run(VALUE self) {
89
+ ev_run(EV_DEFAULT, 0);
90
+ rb_gv_set("__reactor_fiber__", Qnil);
91
+ return Qnil;
92
+ }
93
+
94
+ static VALUE EV_break(VALUE self) {
95
+ // make sure reactor fiber is alive
96
+ if (!RTEST(rb_fiber_alive_p(EV_reactor_fiber))) {
97
+ return Qnil;
98
+ }
99
+
100
+ ev_break(EV_DEFAULT, EVBREAK_ALL);
101
+ return YIELD_TO_REACTOR();
102
+ }
103
+
104
+ static VALUE EV_restart(VALUE self) {
105
+ EV_reactor_fiber = rb_fiber_new(EV_run, Qnil);
106
+ rb_gv_set("__reactor_fiber__", EV_reactor_fiber);
107
+ return Qnil;
108
+ }
109
+
110
+ static VALUE EV_rerun(VALUE self) {
111
+ EV_break(self);
112
+ EV_restart(self);
113
+ return Qnil;
114
+ }
115
+
116
+ static VALUE EV_ref(VALUE self) {
117
+ ev_ref(EV_DEFAULT);
118
+ return Qnil;
119
+ }
120
+
121
+ static VALUE EV_unref(VALUE self) {
122
+ ev_unref(EV_DEFAULT);
123
+ return Qnil;
124
+ }
125
+
126
+ void EV_add_watcher_ref(VALUE obj) {
127
+ rb_hash_aset(watcher_refs, rb_obj_id(obj), obj);
128
+ }
129
+
130
+ void EV_del_watcher_ref(VALUE obj) {
131
+ rb_hash_delete(watcher_refs, rb_obj_id(obj));
132
+ }
133
+
134
+ static VALUE EV_next_tick(VALUE self) {
135
+ VALUE proc = rb_block_proc();
136
+ if (RTEST(proc)) {
137
+ rb_ary_push(next_tick_procs, proc);
138
+ if (!next_tick_active) {
139
+ next_tick_active = 1;
140
+ ev_timer_start(EV_DEFAULT, &next_tick_timer);
141
+ }
142
+ }
143
+ return Qnil;
144
+ }
145
+
146
+ static VALUE EV_snooze(VALUE self) {
147
+ VALUE ret;
148
+ VALUE fiber = rb_fiber_current();
149
+
150
+ rb_ary_push(next_tick_procs, fiber);
151
+ if (!next_tick_active) {
152
+ next_tick_active = 1;
153
+ ev_timer_start(EV_DEFAULT, &next_tick_timer);
154
+ }
155
+
156
+ ret = YIELD_TO_REACTOR();
157
+
158
+ // fiber is resumed, check if resumed value is an exception
159
+ if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
160
+ rb_ary_delete(next_tick_procs, fiber);
161
+ return rb_funcall(ret, ID_raise, 1, ret);
162
+ }
163
+ else {
164
+ return ret;
165
+ }
166
+ }
167
+
168
+ static VALUE EV_post_fork(VALUE self) {
169
+ ev_loop_fork(EV_DEFAULT);
170
+
171
+ EV_reactor_fiber = rb_fiber_new(EV_run, Qnil);
172
+ rb_gv_set("__reactor_fiber__", EV_reactor_fiber);
173
+ EV_root_fiber = rb_fiber_current();
174
+
175
+ return Qnil;
176
+ }
177
+
178
+ VALUE EV_next_tick_caller(VALUE proc, VALUE data, int argc, VALUE* argv) {
179
+ if (rb_obj_is_proc(proc)) {
180
+ rb_funcall(proc, ID_call, 1, Qtrue);
181
+ }
182
+ else {
183
+ SCHEDULE_FIBER(proc, 1, rb_ivar_get(proc, ID_scheduled_value));
184
+ }
185
+ return Qnil;
186
+ }
187
+
188
+ void EV_next_tick_callback(ev_loop *ev_loop, struct ev_timer *timer, int revents) {
189
+ VALUE scheduled_procs = rb_ary_dup(next_tick_procs);
190
+ rb_funcall(next_tick_procs, ID_clear, 0);
191
+ rb_block_call(scheduled_procs, ID_each, 0, NULL, EV_next_tick_caller, Qnil);
192
+ if (rb_array_len(next_tick_procs) > 0) {
193
+ ev_timer_start(EV_DEFAULT, &next_tick_timer);
194
+ } else {
195
+ next_tick_active = 0;
196
+ }
197
+ }
198
+
199
+ static VALUE EV_suspend(VALUE self) {
200
+ // make sure reactor fiber is alive
201
+ if (!RTEST(rb_fiber_alive_p(EV_reactor_fiber))) {
202
+ return Qnil;
203
+ }
204
+
205
+ VALUE ret = YIELD_TO_REACTOR();
206
+
207
+ // fiber is resumed, check if resumed value is an exception
208
+ return RTEST(rb_obj_is_kind_of(ret, rb_eException)) ?
209
+ rb_funcall(ret, ID_raise, 1, ret) : ret;
210
+ }
211
+
212
+ static VALUE EV_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
213
+ rb_ivar_set(fiber, ID_scheduled_value, value);
214
+
215
+ rb_ary_push(next_tick_procs, fiber);
216
+ if (!next_tick_active) {
217
+ next_tick_active = 1;
218
+ ev_timer_start(EV_DEFAULT, &next_tick_timer);
219
+ }
220
+
221
+ return Qnil;
222
+ }
@@ -0,0 +1,405 @@
1
+ #include "ev.h"
2
+
3
+ #ifdef GetReadFile
4
+ # define FPTR_TO_FD(fptr) (fileno(GetReadFile(fptr)))
5
+ #else
6
+ # define FPTR_TO_FD(fptr) fptr->fd
7
+ #endif /* GetReadFile */
8
+
9
+ struct EV_IO {
10
+ struct ev_io ev_io;
11
+ int active;
12
+ int event_mask;
13
+ VALUE callback;
14
+ VALUE fiber;
15
+ };
16
+
17
+ static VALUE mEV = Qnil;
18
+ static VALUE cEV_IO = Qnil;
19
+
20
+ static VALUE EV_IO_allocate(VALUE klass);
21
+ static void EV_IO_mark(void *ptr);
22
+ static void EV_IO_free(void *ptr);
23
+ static size_t EV_IO_size(const void *ptr);
24
+
25
+ static VALUE EV_IO_initialize(VALUE self, VALUE io, VALUE event_mask);
26
+
27
+ static VALUE EV_IO_start(VALUE self);
28
+ static VALUE EV_IO_stop(VALUE self);
29
+ static VALUE EV_IO_await(VALUE self);
30
+
31
+ void EV_IO_callback(ev_loop *ev_loop, struct ev_io *io, int revents);
32
+
33
+ static int EV_IO_symbol2event_mask(VALUE sym);
34
+
35
+ static VALUE IO_read(int argc, VALUE *argv, VALUE io);
36
+ static VALUE IO_readpartial(int argc, VALUE *argv, VALUE io);
37
+ static VALUE IO_write(int argc, VALUE *argv, VALUE io);
38
+
39
+ void Init_EV_IO() {
40
+ mEV = rb_define_module("EV");
41
+ cEV_IO = rb_define_class_under(mEV, "IO", rb_cData);
42
+ rb_define_alloc_func(cEV_IO, EV_IO_allocate);
43
+
44
+ rb_define_method(cEV_IO, "initialize", EV_IO_initialize, 2);
45
+ rb_define_method(cEV_IO, "start", EV_IO_start, 0);
46
+ rb_define_method(cEV_IO, "stop", EV_IO_stop, 0);
47
+ rb_define_method(cEV_IO, "await", EV_IO_await, 0);
48
+
49
+ VALUE cIO = rb_const_get(rb_cObject, rb_intern("IO"));
50
+ rb_define_method(cIO, "read", IO_read, -1);
51
+ rb_define_method(cIO, "readpartial", IO_readpartial, -1);
52
+ rb_define_method(cIO, "write", IO_write, -1);
53
+ rb_define_method(cIO, "write_nonblock", IO_write, -1);
54
+ rb_define_method(cIO, "<<", IO_write, -1);
55
+ }
56
+
57
+ static const rb_data_type_t EV_IO_type = {
58
+ "EV_IO",
59
+ {EV_IO_mark, EV_IO_free, EV_IO_size,},
60
+ 0, 0,
61
+ RUBY_TYPED_FREE_IMMEDIATELY,
62
+ };
63
+
64
+ static VALUE EV_IO_allocate(VALUE klass) {
65
+ struct EV_IO *io = (struct EV_IO *)xmalloc(sizeof(struct EV_IO));
66
+
67
+ return TypedData_Wrap_Struct(klass, &EV_IO_type, io);
68
+ }
69
+
70
+ static void EV_IO_mark(void *ptr) {
71
+ struct EV_IO *io = ptr;
72
+ if (io->callback != Qnil) {
73
+ rb_gc_mark(io->callback);
74
+ }
75
+ if (io->fiber != Qnil) {
76
+ rb_gc_mark(io->fiber);
77
+ }
78
+ }
79
+
80
+ static void EV_IO_free(void *ptr) {
81
+ struct EV_IO *io = ptr;
82
+ ev_io_stop(EV_DEFAULT, &io->ev_io);
83
+ xfree(io);
84
+ }
85
+
86
+ static size_t EV_IO_size(const void *ptr) {
87
+ return sizeof(struct EV_IO);
88
+ }
89
+
90
+ static const char * S_IO = "IO";
91
+ static const char * S_to_io = "to_io";
92
+
93
+ #define GetEV_IO(obj, io) TypedData_Get_Struct((obj), struct EV_IO, &EV_IO_type, (io))
94
+
95
+ static VALUE EV_IO_initialize(VALUE self, VALUE io_obj, VALUE event_mask) {
96
+ struct EV_IO *io;
97
+ rb_io_t *fptr;
98
+
99
+ GetEV_IO(self, io);
100
+
101
+ io->event_mask = EV_IO_symbol2event_mask(event_mask);
102
+ io->callback = Qnil;
103
+ io->fiber = Qnil;
104
+ io->active = 0;
105
+
106
+ GetOpenFile(rb_convert_type(io_obj, T_FILE, S_IO, S_to_io), fptr);
107
+ ev_io_init(&io->ev_io, EV_IO_callback, FPTR_TO_FD(fptr), io->event_mask);
108
+
109
+ return Qnil;
110
+ }
111
+
112
+ void EV_IO_callback(ev_loop *ev_loop, struct ev_io *ev_io, int revents) {
113
+ VALUE fiber;
114
+ struct EV_IO *io = (struct EV_IO*)ev_io;
115
+
116
+ if (io->fiber != Qnil) {
117
+ ev_io_stop(EV_DEFAULT, ev_io);
118
+ io->active = 0;
119
+ fiber = io->fiber;
120
+ io->fiber = Qnil;
121
+ SCHEDULE_FIBER(fiber, 0);
122
+ }
123
+ else if (io->callback != Qnil) {
124
+ rb_funcall(io->callback, ID_call, 1, INT2NUM(revents));
125
+ }
126
+ else {
127
+ ev_io_stop(EV_DEFAULT, ev_io);
128
+ }
129
+ }
130
+
131
+ static VALUE EV_IO_start(VALUE self) {
132
+ struct EV_IO *io;
133
+ GetEV_IO(self, io);
134
+
135
+ if (rb_block_given_p()) {
136
+ io->callback = rb_block_proc();
137
+ }
138
+
139
+ if (!io->active) {
140
+ ev_io_start(EV_DEFAULT, &io->ev_io);
141
+ io->active = 1;
142
+ }
143
+
144
+ return self;
145
+ }
146
+
147
+ static VALUE EV_IO_stop(VALUE self) {
148
+ struct EV_IO *io;
149
+ GetEV_IO(self, io);
150
+
151
+ if (io->active) {
152
+ ev_io_stop(EV_DEFAULT, &io->ev_io);
153
+ io->active = 0;
154
+ }
155
+
156
+ return self;
157
+ }
158
+
159
+ static VALUE EV_IO_await(VALUE self) {
160
+ struct EV_IO *io;
161
+ VALUE ret;
162
+
163
+ GetEV_IO(self, io);
164
+
165
+ io->fiber = rb_fiber_current();
166
+ io->active = 1;
167
+ ev_io_start(EV_DEFAULT, &io->ev_io);
168
+ ret = YIELD_TO_REACTOR();
169
+
170
+ // make sure io watcher is stopped
171
+ if (io->active) {
172
+ io->active = 0;
173
+ ev_io_stop(EV_DEFAULT, &io->ev_io);
174
+ }
175
+
176
+ // fiber is resumed, check if resumed value is an exception
177
+ if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
178
+ return rb_funcall(ret, ID_raise, 1, ret);
179
+ }
180
+ else {
181
+ return Qnil;
182
+ }
183
+ }
184
+
185
+ static int EV_IO_symbol2event_mask(VALUE sym) {
186
+ ID sym_id;
187
+
188
+ if (NIL_P(sym)) {
189
+ return 0;
190
+ }
191
+
192
+ sym_id = SYM2ID(sym);
193
+
194
+ if(sym_id == ID_R) {
195
+ return EV_READ;
196
+ } else if(sym_id == ID_W) {
197
+ return EV_WRITE;
198
+ } else if(sym_id == ID_RW) {
199
+ return EV_READ | EV_WRITE;
200
+ } else {
201
+ rb_raise(rb_eArgError, "invalid interest type %s (must be :r, :w, or :rw)",
202
+ RSTRING_PTR(rb_funcall(sym, ID_inspect, 0)));
203
+ }
204
+ }
205
+
206
+ //////////////////////////////////////////////////////////////////////
207
+ //////////////////////////////////////////////////////////////////////
208
+ // the following is copied verbatim from the Ruby source code (io.c)
209
+ struct io_internal_read_struct {
210
+ int fd;
211
+ int nonblock;
212
+ void *buf;
213
+ size_t capa;
214
+ };
215
+
216
+ static int io_setstrbuf(VALUE *str, long len) {
217
+ #ifdef _WIN32
218
+ len = (len + 1) & ~1L; /* round up for wide char */
219
+ #endif
220
+ if (NIL_P(*str)) {
221
+ *str = rb_str_new(0, len);
222
+ return 1;
223
+ }
224
+ else {
225
+ VALUE s = StringValue(*str);
226
+ long clen = RSTRING_LEN(s);
227
+ if (clen >= len) {
228
+ rb_str_modify(s);
229
+ return 0;
230
+ }
231
+ len -= clen;
232
+ }
233
+ rb_str_modify_expand(*str, len);
234
+ return 0;
235
+ }
236
+
237
+ #define MAX_REALLOC_GAP 4096
238
+ static void io_shrink_read_string(VALUE str, long n) {
239
+ if (rb_str_capacity(str) - n > MAX_REALLOC_GAP) {
240
+ rb_str_resize(str, n);
241
+ }
242
+ }
243
+
244
+ static void io_set_read_length(VALUE str, long n, int shrinkable) {
245
+ if (RSTRING_LEN(str) != n) {
246
+ rb_str_modify(str);
247
+ rb_str_set_len(str, n);
248
+ if (shrinkable) io_shrink_read_string(str, n);
249
+ }
250
+ }
251
+ //////////////////////////////////////////////////////////////////////
252
+ //////////////////////////////////////////////////////////////////////
253
+
254
+ static VALUE IO_read(int argc, VALUE *argv, VALUE io) {
255
+ long len = argc == 1 ? NUM2LONG(argv[0]) : 8192;
256
+
257
+ rb_io_t *fptr;
258
+ long n;
259
+ int shrinkable;
260
+ VALUE read_watcher = Qnil;
261
+
262
+ if (len < 0) {
263
+ rb_raise(rb_eArgError, "negative length %ld given", len);
264
+ }
265
+
266
+ VALUE str = argc >= 2 ? argv[1] : Qnil;
267
+
268
+ shrinkable = io_setstrbuf(&str, len);
269
+ OBJ_TAINT(str);
270
+ GetOpenFile(io, fptr);
271
+ rb_io_check_byte_readable(fptr);
272
+
273
+ if (len == 0)
274
+ return str;
275
+
276
+ char *buf = RSTRING_PTR(str);
277
+ long total = 0;
278
+
279
+ while (1) {
280
+ n = read(fptr->fd, buf, len);
281
+ if (n < 0) {
282
+ int e = errno;
283
+ if ((e == EWOULDBLOCK || e == EAGAIN)) {
284
+ if (read_watcher == Qnil)
285
+ read_watcher = rb_funcall(io, ID_read_watcher, 0);
286
+ EV_IO_await(read_watcher);
287
+ }
288
+ else
289
+ rb_syserr_fail(e, strerror(e));
290
+ // rb_syserr_fail_path(e, fptr->pathv);
291
+ }
292
+ else if (n == 0)
293
+ break;
294
+ else {
295
+ total = total + n;
296
+ buf += n;
297
+ len -= n;
298
+ if (len == 0)
299
+ break;
300
+ }
301
+ }
302
+
303
+ io_set_read_length(str, total, shrinkable);
304
+
305
+ if (total == 0)
306
+ return Qnil;
307
+
308
+ return str;
309
+ }
310
+
311
+ static VALUE IO_readpartial(int argc, VALUE *argv, VALUE io) {
312
+ long len = argc == 1 ? NUM2LONG(argv[0]) : 8192;
313
+
314
+ rb_io_t *fptr;
315
+ long n;
316
+ int shrinkable;
317
+ VALUE read_watcher = Qnil;
318
+
319
+ if (len < 0) {
320
+ rb_raise(rb_eArgError, "negative length %ld given", len);
321
+ }
322
+
323
+ VALUE str = argc >= 2 ? argv[1] : Qnil;
324
+
325
+ shrinkable = io_setstrbuf(&str, len);
326
+ OBJ_TAINT(str);
327
+ GetOpenFile(io, fptr);
328
+ rb_io_check_byte_readable(fptr);
329
+
330
+ if (len == 0)
331
+ return str;
332
+
333
+ while (1) {
334
+ n = read(fptr->fd, RSTRING_PTR(str), len);
335
+ if (n < 0) {
336
+ int e = errno;
337
+ if ((e == EWOULDBLOCK || e == EAGAIN)) {
338
+ if (read_watcher == Qnil)
339
+ read_watcher = rb_funcall(io, ID_read_watcher, 0);
340
+ EV_IO_await(read_watcher);
341
+ }
342
+ else
343
+ rb_syserr_fail(e, strerror(e));
344
+ // rb_syserr_fail_path(e, fptr->pathv);
345
+ }
346
+ else
347
+ break;
348
+ }
349
+
350
+ io_set_read_length(str, n, shrinkable);
351
+
352
+ if (n == 0)
353
+ return Qnil;
354
+
355
+ return str;
356
+ }
357
+
358
+ static VALUE IO_write(int argc, VALUE *argv, VALUE io) {
359
+ long i;
360
+ long n;
361
+ long total = 0;
362
+ rb_io_t *fptr;
363
+
364
+ io = rb_io_get_write_io(io);
365
+ VALUE write_watcher = Qnil;
366
+
367
+ GetOpenFile(io, fptr);
368
+ rb_io_check_writable(fptr);
369
+ rb_io_set_nonblock(fptr);
370
+
371
+ for (i = 0; i < argc; i++) {
372
+ VALUE str = argv[i];
373
+ if (!RB_TYPE_P(str, T_STRING))
374
+ str = rb_obj_as_string(str);
375
+ char *buf = RSTRING_PTR(str);
376
+ long len = RSTRING_LEN(str);
377
+ RB_GC_GUARD(str);
378
+ while (1) {
379
+ n = write(fptr->fd, buf, len);
380
+
381
+ if (n < 0) {
382
+ int e = errno;
383
+ if (e == EWOULDBLOCK || e == EAGAIN) {
384
+ if (write_watcher == Qnil)
385
+ write_watcher = rb_funcall(io, ID_write_watcher, 0);
386
+ EV_IO_await(write_watcher);
387
+ }
388
+ else {
389
+ rb_syserr_fail(e, strerror(e));
390
+ // rb_syserr_fail_path(e, fptr->pathv);
391
+ }
392
+ }
393
+ else {
394
+ total += n;
395
+ if (n < len) {
396
+ buf += n;
397
+ len -= n;
398
+ }
399
+ else break;
400
+ }
401
+ }
402
+ }
403
+
404
+ return LONG2FIX(total);
405
+ }