polyphony 0.17 → 0.19

Sign up to get free protection for your applications and to get access to all the features.
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
+ }