polyphony 0.17 → 0.19

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 (86) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/Gemfile.lock +11 -3
  4. data/README.md +18 -18
  5. data/TODO.md +5 -21
  6. data/examples/core/channel_echo.rb +3 -3
  7. data/examples/core/enumerator.rb +1 -1
  8. data/examples/core/fork.rb +1 -1
  9. data/examples/core/genserver.rb +1 -1
  10. data/examples/core/lock.rb +3 -3
  11. data/examples/core/multiple_spawn.rb +2 -2
  12. data/examples/core/nested_async.rb +1 -1
  13. data/examples/core/nested_multiple_spawn.rb +3 -3
  14. data/examples/core/resource_cancel.rb +1 -1
  15. data/examples/core/sleep_spawn.rb +2 -2
  16. data/examples/core/spawn.rb +1 -1
  17. data/examples/core/spawn_cancel.rb +1 -1
  18. data/examples/core/spawn_error.rb +4 -4
  19. data/examples/core/supervisor.rb +1 -1
  20. data/examples/core/supervisor_with_error.rb +1 -1
  21. data/examples/core/supervisor_with_manual_move_on.rb +1 -1
  22. data/examples/core/thread.rb +2 -2
  23. data/examples/core/thread_cancel.rb +2 -2
  24. data/examples/core/thread_pool.rb +1 -1
  25. data/examples/core/throttle.rb +3 -3
  26. data/examples/core/timeout.rb +10 -0
  27. data/examples/fs/read.rb +1 -1
  28. data/examples/http/http_client.rb +1 -1
  29. data/examples/http/http_get.rb +7 -0
  30. data/examples/http/http_parse_experiment.rb +118 -0
  31. data/examples/http/http_proxy.rb +81 -0
  32. data/examples/http/http_server.rb +15 -4
  33. data/examples/http/http_server_forked.rb +2 -2
  34. data/examples/http/http_server_throttled.rb +1 -1
  35. data/examples/http/http_ws_server.rb +2 -2
  36. data/examples/http/https_server.rb +5 -1
  37. data/examples/http/https_wss_server.rb +1 -1
  38. data/examples/http/rack_server_https_forked.rb +1 -1
  39. data/examples/interfaces/pg_client.rb +1 -1
  40. data/examples/interfaces/pg_pool.rb +1 -1
  41. data/examples/interfaces/redis_channels.rb +5 -5
  42. data/examples/interfaces/redis_pubsub.rb +2 -2
  43. data/examples/interfaces/redis_pubsub_perf.rb +3 -3
  44. data/examples/io/echo_client.rb +2 -2
  45. data/examples/io/echo_pipe.rb +17 -0
  46. data/examples/io/echo_server.rb +1 -1
  47. data/examples/io/echo_server_with_timeout.rb +1 -1
  48. data/examples/io/httparty.rb +10 -0
  49. data/examples/io/httparty_multi.rb +29 -0
  50. data/examples/io/httparty_threaded.rb +25 -0
  51. data/examples/io/irb.rb +15 -0
  52. data/examples/io/net-http.rb +15 -0
  53. data/examples/io/system.rb +1 -1
  54. data/examples/io/tcpsocket.rb +18 -0
  55. data/examples/performance/perf_multi_snooze.rb +2 -2
  56. data/examples/performance/perf_snooze.rb +17 -20
  57. data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -1
  58. data/ext/ev/ev.h +9 -1
  59. data/ext/ev/ev_ext.c +4 -1
  60. data/ext/ev/ev_module.c +36 -22
  61. data/ext/ev/extconf.rb +1 -1
  62. data/ext/ev/io.c +23 -23
  63. data/ext/ev/signal.c +1 -1
  64. data/ext/ev/socket.c +161 -0
  65. data/lib/polyphony/core/coprocess.rb +1 -1
  66. data/lib/polyphony/core/fiber_pool.rb +2 -2
  67. data/lib/polyphony/core/supervisor.rb +2 -18
  68. data/lib/polyphony/extensions/io.rb +19 -6
  69. data/lib/polyphony/extensions/kernel.rb +17 -5
  70. data/lib/polyphony/extensions/socket.rb +40 -1
  71. data/lib/polyphony/http/agent.rb +56 -25
  72. data/lib/polyphony/http/http1_adapter.rb +254 -0
  73. data/lib/polyphony/http/http2_adapter.rb +157 -0
  74. data/lib/polyphony/http/{http2_request.rb → request.rb} +25 -22
  75. data/lib/polyphony/http/server.rb +19 -11
  76. data/lib/polyphony/net.rb +10 -6
  77. data/lib/polyphony/version.rb +1 -1
  78. data/polyphony.gemspec +6 -5
  79. data/test/test_coprocess.rb +9 -9
  80. data/test/test_core.rb +14 -14
  81. data/test/test_io.rb +4 -4
  82. data/test/test_kernel.rb +1 -1
  83. metadata +48 -23
  84. data/lib/polyphony/http/http1.rb +0 -124
  85. data/lib/polyphony/http/http1_request.rb +0 -83
  86. data/lib/polyphony/http/http2.rb +0 -65
@@ -3,7 +3,7 @@
3
3
  require 'bundler/setup'
4
4
  require 'polyphony/redis'
5
5
 
6
- coproc do
6
+ spin do
7
7
  redis = Redis.new
8
8
  redis.subscribe('redis-channel') do |on|
9
9
  on.message do |channel, message|
@@ -13,7 +13,7 @@ coproc do
13
13
  end
14
14
  end
15
15
 
16
- coproc do
16
+ spin do
17
17
  redis = Redis.new
18
18
  move_on_after(3) do
19
19
  throttled_loop(1) do
@@ -17,7 +17,7 @@ X_SESSIONS.times do
17
17
  }
18
18
  end
19
19
 
20
- coproc do
20
+ spin do
21
21
  redis = Redis.new
22
22
  redis.subscribe('events') do |on|
23
23
  on.message do |_, message|
@@ -40,14 +40,14 @@ def distribute_event(event)
40
40
  # puts "elapsed: #{elapsed} (#{rate}/s)" if $update_count % 100 == 0
41
41
  end
42
42
 
43
- coproc do
43
+ spin do
44
44
  redis = Redis.new
45
45
  throttled_loop(1000) do
46
46
  redis.publish('events', {path: "node#{rand(X_NODES)}"}.to_json)
47
47
  end
48
48
  end
49
49
 
50
- coproc do
50
+ spin do
51
51
  last_count = 0
52
52
  last_stamp = Time.now
53
53
  throttled_loop(1) do
@@ -5,11 +5,11 @@ require 'polyphony'
5
5
 
6
6
  socket = Polyphony::Net.tcp_connect('127.0.0.1', 1234)
7
7
 
8
- writer = coproc do
8
+ writer = spin do
9
9
  throttled_loop(1) { socket << "#{Time.now}\n" rescue nil }
10
10
  end
11
11
 
12
- reader = coproc do
12
+ reader = spin do
13
13
  puts "received from echo server:"
14
14
  while data = socket.readpartial(8192)
15
15
  STDOUT << data
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ i, o = IO.pipe
7
+
8
+ puts "Write something:"
9
+ spin {
10
+ throttled_loop(1, count: 3) { o << STDIN.gets }
11
+ o.close
12
+ }
13
+
14
+ while data = i.readpartial(8192)
15
+ STDOUT << "You wrote: #{data}"
16
+ end
17
+
@@ -6,7 +6,7 @@ require 'polyphony'
6
6
  server = TCPServer.open(1234)
7
7
  puts "Echoing on port 1234..."
8
8
  while client = server.accept
9
- coproc do
9
+ spin do
10
10
  while data = client.readpartial(8192) rescue nil
11
11
  client.write("you said: ", data.chomp, "!\n")
12
12
  end
@@ -9,7 +9,7 @@ begin
9
9
 
10
10
  loop do
11
11
  client = server.accept
12
- coproc do
12
+ spin do
13
13
  cancel_scope = nil
14
14
  move_on_after(5) do |s|
15
15
  cancel_scope = s
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+ require 'httparty'
6
+
7
+ timer = spin { throttled_loop(10) { STDOUT << '.' } }
8
+
9
+ puts HTTParty.get('http://realiteq.net/?q=time')
10
+ timer.stop
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+ require 'httparty'
6
+
7
+ url = 'http://127.0.0.1:4411/?q=time'
8
+ results = []
9
+
10
+ t0 = Time.now
11
+ move_on_after(3) do
12
+ supervise do |s|
13
+ 10.times do
14
+ s.spin do
15
+ loop do
16
+ STDOUT << '!'
17
+ if (result = HTTParty.get(url))
18
+ results << result
19
+ STDOUT << '.'
20
+ end
21
+ rescue => e
22
+ p e
23
+ end
24
+ end
25
+ end
26
+ end
27
+ puts "done"
28
+ end
29
+ puts "got #{results.size} (#{results.size / (Time.now - t0)}/s)"
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'httparty'
4
+
5
+ url = 'http://127.0.0.1:4411/?q=time'
6
+ results = Queue.new
7
+
8
+ t0 = Time.now
9
+ threads = []
10
+ 10.times do
11
+ threads << Thread.new do
12
+ loop do
13
+ STDOUT << '!'
14
+ if (result = HTTParty.get(url))
15
+ results << result
16
+ STDOUT << '.'
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ sleep 3
23
+ threads.each(&:kill)
24
+ puts "done"
25
+ puts "got #{results.size} (#{results.size / (Time.now - t0)}/s)"
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+ require 'irb'
6
+
7
+ $counter = 0
8
+
9
+ timer = spin do
10
+ throttled_loop(10) { $counter += 1 }
11
+ end
12
+
13
+ at_exit { timer.stop }
14
+
15
+ IRB.start
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+ require 'net/http'
6
+
7
+ uri = URI('http://realiteq.net/?q=time')
8
+
9
+ begin
10
+ puts Net::HTTP.get(uri)
11
+ rescue => e
12
+ p e
13
+ puts "*" * 40
14
+ puts e.backtrace[0..4].join("\n")
15
+ end
@@ -3,7 +3,7 @@
3
3
  require 'bundler/setup'
4
4
  require 'polyphony'
5
5
 
6
- timer = coproc {
6
+ timer = spin {
7
7
  throttled_loop(5) { STDOUT << '.' }
8
8
  }
9
9
 
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ socket = TCPSocket.new('realiteq.net', 80)
7
+
8
+ timer = spin { throttled_loop(20) { STDOUT << '.' } }
9
+
10
+ 5.times do
11
+ socket.send("GET /?q=time HTTP/1.1\r\nHost: realiteq.net\r\n\r\n", 0)
12
+ socket.recv(8192)
13
+ STDOUT << "*"
14
+ end
15
+
16
+ timer.stop
17
+ socket.close
18
+ puts
@@ -6,12 +6,12 @@ require 'polyphony'
6
6
  ITERATIONS = 1_000
7
7
  FIBERS = 1_000
8
8
 
9
- coproc do
9
+ spin do
10
10
  count = 0
11
11
  t0 = Time.now
12
12
  supervise do |s|
13
13
  FIBERS.times do
14
- s.coproc do
14
+ s.spin do
15
15
  ITERATIONS.times { snooze; count += 1 }
16
16
  end
17
17
  end
@@ -5,26 +5,23 @@ require 'polyphony'
5
5
 
6
6
  X = 1_000_000
7
7
 
8
- # f = Fiber.new do
9
- # loop { Fiber.yield }
10
- # end
8
+ STDOUT << "Fiber.yield: "
9
+ f = Fiber.new do
10
+ loop { Fiber.yield }
11
+ end
12
+ t0 = Time.now
13
+ X.times { f.resume }
14
+ dt = Time.now - t0
15
+ puts "%d/s" % (X / dt)
16
+
17
+ # STDOUT << "Kernel#sleep: "
11
18
  # t0 = Time.now
12
- # X.times { f.resume }
19
+ # X.times { sleep(0) }
13
20
  # dt = Time.now - t0
14
- # puts "#{X / dt.to_f}/s"
21
+ # puts "%d/s" % (X / dt)
15
22
 
16
- # sleep
17
- # coproc do
18
- # t0 = Time.now
19
- # X.times { sleep(0) }
20
- # dt = Time.now - t0
21
- # puts "#{X / dt.to_f}/s"
22
- # end
23
-
24
- # snooze
25
- coproc do
26
- t0 = Time.now
27
- X.times { snooze }
28
- dt = Time.now - t0
29
- puts "#{X / dt.to_f}/s"
30
- end
23
+ STDOUT << "Kernel#snooze: "
24
+ t0 = Time.now
25
+ X.times { snooze }
26
+ dt = Time.now - t0
27
+ puts "%d/s" % (X / dt)
@@ -46,7 +46,7 @@ def handle_request(client, parser)
46
46
  client.write "HTTP/1.1 #{status_code}\r\n#{headers}\r\n#{data}"
47
47
  end
48
48
 
49
- coproc do
49
+ spin do
50
50
  server = TCPServer.open(1234)
51
51
  puts "listening on port 1234"
52
52
 
@@ -9,6 +9,14 @@ void EV_add_watcher_ref(VALUE obj);
9
9
  void EV_del_watcher_ref(VALUE obj);
10
10
  void EV_async_free(void *p);
11
11
 
12
+ VALUE IO_read_watcher(VALUE io);
13
+ VALUE IO_write_watcher(VALUE io);
14
+ VALUE EV_IO_await(VALUE self);
15
+
16
+ int io_setstrbuf(VALUE *str, long len);
17
+ void io_set_read_length(VALUE str, long n, int shrinkable);
18
+ VALUE io_enc_str(VALUE str, rb_io_t *fptr);
19
+
12
20
  #define SCHEDULE_FIBER(obj, args...) rb_funcall(obj, ID_transfer, args)
13
21
  #define YIELD_TO_REACTOR() rb_funcall(EV_reactor_fiber, ID_transfer, 0)
14
22
 
@@ -29,4 +37,4 @@ extern ID ID_R;
29
37
  extern ID ID_W;
30
38
  extern ID ID_RW;
31
39
 
32
- #endif /* RUBY_EV_H */
40
+ #endif /* RUBY_EV_H */
@@ -7,6 +7,7 @@ void Init_EV_Child();
7
7
  void Init_EV_IO();
8
8
  void Init_EV_Signal();
9
9
  void Init_EV_Timer();
10
+ void Init_Socket();
10
11
 
11
12
  void Init_ev_ext() {
12
13
  ev_set_allocator(xrealloc);
@@ -17,4 +18,6 @@ void Init_ev_ext() {
17
18
  Init_EV_IO();
18
19
  Init_EV_Signal();
19
20
  Init_EV_Timer();
20
- }
21
+
22
+ Init_Socket();
23
+ }
@@ -17,12 +17,14 @@ static VALUE EV_post_fork(VALUE self);
17
17
  static VALUE EV_suspend(VALUE self);
18
18
  static VALUE EV_schedule_fiber(VALUE self, VALUE fiber, VALUE value);
19
19
 
20
+ static VALUE Fiber_safe_transfer(int argc, VALUE *argv, VALUE self);
21
+
20
22
  static VALUE watcher_refs;
21
- static VALUE next_tick_procs;
23
+ static VALUE next_tick_items;
22
24
 
23
- static struct ev_timer next_tick_timer;
25
+ static struct ev_idle next_tick_watcher;
24
26
  static int next_tick_active;
25
- void EV_next_tick_callback(ev_loop *ev_loop, struct ev_timer *timer, int revents);
27
+ void EV_next_tick_callback(ev_loop *ev_loop, struct ev_idle *watcher, int revents);
26
28
 
27
29
  VALUE EV_reactor_fiber;
28
30
  VALUE EV_root_fiber;
@@ -58,6 +60,9 @@ void Init_EV() {
58
60
  rb_define_global_function("snooze", EV_snooze, 0);
59
61
  rb_define_global_function("next_tick", EV_next_tick, 0);
60
62
 
63
+ VALUE cFiber = rb_const_get(rb_cObject, rb_intern("Fiber"));
64
+ rb_define_method(cFiber, "safe_transfer", Fiber_safe_transfer, -1);
65
+
61
66
  ID_call = rb_intern("call");
62
67
  ID_caller = rb_intern("caller");
63
68
  ID_clear = rb_intern("clear");
@@ -80,10 +85,10 @@ void Init_EV() {
80
85
  watcher_refs = rb_hash_new();
81
86
  rb_global_variable(&watcher_refs);
82
87
 
83
- next_tick_procs = rb_ary_new();
84
- rb_global_variable(&next_tick_procs);
88
+ next_tick_items = rb_ary_new();
89
+ rb_global_variable(&next_tick_items);
85
90
 
86
- ev_timer_init(&next_tick_timer, EV_next_tick_callback, 0., 0.);
91
+ ev_idle_init(&next_tick_watcher, EV_next_tick_callback);
87
92
  next_tick_active = 0;
88
93
  }
89
94
 
@@ -136,10 +141,10 @@ void EV_del_watcher_ref(VALUE obj) {
136
141
  static VALUE EV_next_tick(VALUE self) {
137
142
  VALUE proc = rb_block_proc();
138
143
  if (RTEST(proc)) {
139
- rb_ary_push(next_tick_procs, proc);
144
+ rb_ary_push(next_tick_items, proc);
140
145
  if (!next_tick_active) {
141
146
  next_tick_active = 1;
142
- ev_timer_start(EV_DEFAULT, &next_tick_timer);
147
+ ev_idle_start(EV_DEFAULT, &next_tick_watcher);
143
148
  }
144
149
  }
145
150
  return Qnil;
@@ -149,17 +154,17 @@ static VALUE EV_snooze(VALUE self) {
149
154
  VALUE ret;
150
155
  VALUE fiber = rb_fiber_current();
151
156
 
152
- rb_ary_push(next_tick_procs, fiber);
157
+ rb_ary_push(next_tick_items, fiber);
153
158
  if (!next_tick_active) {
154
159
  next_tick_active = 1;
155
- ev_timer_start(EV_DEFAULT, &next_tick_timer);
160
+ ev_idle_start(EV_DEFAULT, &next_tick_watcher);
156
161
  }
157
162
 
158
163
  ret = YIELD_TO_REACTOR();
159
164
 
160
165
  // fiber is resumed, check if resumed value is an exception
161
166
  if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
162
- rb_ary_delete(next_tick_procs, fiber);
167
+ rb_ary_delete(next_tick_items, fiber);
163
168
  return rb_funcall(ret, ID_raise, 1, ret);
164
169
  }
165
170
  else {
@@ -187,23 +192,23 @@ VALUE EV_next_tick_runner(VALUE proc) {
187
192
  return Qnil;
188
193
  }
189
194
 
190
- void EV_next_tick_callback(ev_loop *ev_loop, struct ev_timer *timer, int revents) {
191
- VALUE scheduled_procs = next_tick_procs;
192
- next_tick_procs = rb_ary_new();
195
+ void EV_next_tick_callback(ev_loop *ev_loop, struct ev_idle *watcher, int revents) {
196
+ VALUE scheduled_items = next_tick_items;
197
+ next_tick_items = rb_ary_new();
193
198
 
194
- for (long i = 0; i < RARRAY_LEN(scheduled_procs); i++) {
195
- EV_next_tick_runner(RARRAY_AREF(scheduled_procs, i));
199
+ long len = RARRAY_LEN(scheduled_items);
200
+ for (long i = 0; i < len; i++) {
201
+ EV_next_tick_runner(RARRAY_AREF(scheduled_items, i));
196
202
  }
197
203
 
198
- if (rb_array_len(next_tick_procs) > 0) {
199
- ev_timer_start(EV_DEFAULT, &next_tick_timer);
200
- } else {
204
+ // if no next tick items were added during callback, stop the idle watcher
205
+ if (rb_array_len(next_tick_items) == 0) {
201
206
  next_tick_active = 0;
207
+ ev_idle_stop(EV_DEFAULT, &next_tick_watcher);
202
208
  }
203
209
  }
204
210
 
205
211
  static VALUE EV_suspend(VALUE self) {
206
- // make sure reactor fiber is alive
207
212
  if (!RTEST(rb_fiber_alive_p(EV_reactor_fiber))) {
208
213
  return Qnil;
209
214
  }
@@ -218,11 +223,20 @@ static VALUE EV_suspend(VALUE self) {
218
223
  static VALUE EV_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
219
224
  rb_ivar_set(fiber, ID_scheduled_value, value);
220
225
 
221
- rb_ary_push(next_tick_procs, fiber);
226
+ rb_ary_push(next_tick_items, fiber);
222
227
  if (!next_tick_active) {
223
228
  next_tick_active = 1;
224
- ev_timer_start(EV_DEFAULT, &next_tick_timer);
229
+ ev_idle_start(EV_DEFAULT, &next_tick_watcher);
225
230
  }
226
231
 
227
232
  return Qnil;
228
233
  }
234
+
235
+ static VALUE Fiber_safe_transfer(int argc, VALUE *argv, VALUE self) {
236
+ VALUE arg = (argc == 0) ? Qnil : argv[0];
237
+ VALUE ret = rb_funcall(self, ID_transfer, 1, arg);
238
+
239
+ // fiber is resumed, check if resumed value is an exception
240
+ return RTEST(rb_obj_is_kind_of(ret, rb_eException)) ?
241
+ rb_funcall(ret, ID_raise, 1, ret) : ret;
242
+ }