polyphony 0.13 → 0.14

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.
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
+ }