polyphony 0.16 → 0.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +11 -11
  5. data/TODO.md +14 -5
  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.rb +1 -1
  15. data/examples/core/resource_cancel.rb +1 -1
  16. data/examples/core/resource_delegate.rb +1 -1
  17. data/examples/core/sleep_spawn.rb +2 -2
  18. data/examples/core/spawn.rb +1 -1
  19. data/examples/core/spawn_cancel.rb +1 -1
  20. data/examples/core/spawn_error.rb +5 -5
  21. data/examples/core/supervisor.rb +4 -4
  22. data/examples/core/supervisor_with_cancel_scope.rb +3 -3
  23. data/examples/core/supervisor_with_error.rb +4 -4
  24. data/examples/core/supervisor_with_manual_move_on.rb +4 -4
  25. data/examples/core/thread.rb +2 -2
  26. data/examples/core/thread_cancel.rb +2 -2
  27. data/examples/core/thread_pool.rb +2 -2
  28. data/examples/core/throttle.rb +3 -3
  29. data/examples/fs/read.rb +1 -1
  30. data/examples/http/happy_eyeballs.rb +1 -1
  31. data/examples/http/http_client.rb +1 -1
  32. data/examples/http/http_server.rb +1 -1
  33. data/examples/http/http_server_throttled.rb +1 -1
  34. data/examples/http/http_ws_server.rb +2 -2
  35. data/examples/http/https_wss_server.rb +1 -1
  36. data/examples/interfaces/pg_client.rb +1 -1
  37. data/examples/interfaces/pg_pool.rb +1 -1
  38. data/examples/interfaces/redis_channels.rb +5 -5
  39. data/examples/interfaces/redis_pubsub.rb +2 -2
  40. data/examples/interfaces/redis_pubsub_perf.rb +3 -3
  41. data/examples/io/cat.rb +13 -0
  42. data/examples/io/echo_client.rb +2 -2
  43. data/examples/io/echo_server.rb +1 -1
  44. data/examples/io/echo_server_with_timeout.rb +1 -1
  45. data/examples/io/echo_stdin.rb +1 -1
  46. data/examples/io/io_read.rb +9 -0
  47. data/examples/io/system.rb +11 -0
  48. data/examples/performance/perf_multi_snooze.rb +2 -2
  49. data/examples/performance/perf_snooze.rb +2 -2
  50. data/examples/performance/thread-vs-fiber/polyphony_server.rb +2 -2
  51. data/ext/ev/io.c +53 -4
  52. data/lib/polyphony/core/coprocess.rb +1 -0
  53. data/lib/polyphony/core/supervisor.rb +1 -1
  54. data/lib/polyphony/extensions/io.rb +97 -17
  55. data/lib/polyphony/extensions/kernel.rb +47 -27
  56. data/lib/polyphony/http/server.rb +1 -1
  57. data/lib/polyphony/postgres.rb +0 -4
  58. data/lib/polyphony/version.rb +1 -1
  59. data/test/test_coprocess.rb +13 -13
  60. data/test/test_core.rb +12 -12
  61. data/test/test_io.rb +95 -3
  62. data/test/test_kernel.rb +26 -0
  63. metadata +6 -2
@@ -37,7 +37,7 @@ def compare_performance
37
37
  t0 = Time.now
38
38
  supervise do |s|
39
39
  X.times do
40
- s.spawn Polyphony::ThreadPool.process { lengthy_op }
40
+ s.coproc Polyphony::ThreadPool.process { lengthy_op }
41
41
  end
42
42
  end
43
43
  thread_pool_perf = X / (Time.now - t0)
@@ -54,4 +54,4 @@ rescue Exception => e
54
54
  end
55
55
  end
56
56
 
57
- spawn { compare_performance }
57
+ coproc { compare_performance }
@@ -3,14 +3,14 @@
3
3
  require 'bundler/setup'
4
4
  require 'polyphony'
5
5
 
6
- spawn {
6
+ coproc {
7
7
  throttled_loop(3) { STDOUT << '.' }
8
8
  }
9
9
 
10
- spawn {
10
+ coproc {
11
11
  throttled_loop(rate: 2) { STDOUT << '?' }
12
12
  }
13
13
 
14
- spawn {
14
+ coproc {
15
15
  throttled_loop(interval: 1) { STDOUT << '*' }
16
16
  }
data/examples/fs/read.rb CHANGED
@@ -26,7 +26,7 @@ def thread_pool_read_file(x, y)
26
26
  t0 = Time.now
27
27
  supervise do |s|
28
28
  y.times {
29
- s.spawn { x.times { IO.read(PATH) } }
29
+ s.coproc { x.times { IO.read(PATH) } }
30
30
  }
31
31
  end
32
32
  puts "thread_pool_read_file: #{Time.now - t0}"
@@ -20,7 +20,7 @@ def happy_eyeballs(hostname, port, max_wait_time: 0.025)
20
20
  success = supervise do |supervisor|
21
21
  targets.each_with_index do |t, idx|
22
22
  sleep(max_wait_time) if idx > 0
23
- supervisor.spawn try_connect(t, supervisor)
23
+ supervisor.coproc try_connect(t, supervisor)
24
24
  end
25
25
  end
26
26
  if success
@@ -11,7 +11,7 @@ X = 10
11
11
  puts "Making #{X} requests..."
12
12
  t0 = Time.now
13
13
  supervise do |s|
14
- X.times { s.spawn { get_server_time } }
14
+ X.times { s.coproc { get_server_time } }
15
15
  end
16
16
  elapsed = Time.now - t0
17
17
  puts "count: #{X} elapsed: #{elapsed} rate: #{X / elapsed} reqs/s"
@@ -4,7 +4,7 @@ require 'bundler/setup'
4
4
  require 'polyphony/http'
5
5
 
6
6
  opts = { reuse_addr: true, dont_linger: true }
7
- spawn {
7
+ coproc {
8
8
  Polyphony::HTTP::Server.serve('0.0.0.0', 1234, opts) do |req|
9
9
  req.respond("Hello world!\n")
10
10
  end
@@ -5,7 +5,7 @@ require 'polyphony/http'
5
5
 
6
6
  $throttler = throttle(1000)
7
7
  opts = { reuse_addr: true, dont_linger: true }
8
- spawn {
8
+ coproc {
9
9
  Polyphony::HTTP::Server.serve('0.0.0.0', 1234, opts) do |req|
10
10
  $throttler.call { req.respond("Hello world!\n") }
11
11
  end
@@ -7,7 +7,7 @@ require 'polyphony/http'
7
7
  require 'polyphony/websocket'
8
8
 
9
9
  def ws_handler(conn)
10
- timer = spawn {
10
+ timer = coproc {
11
11
  throttled_loop(1) {
12
12
  conn << Time.now.to_s
13
13
  }
@@ -29,7 +29,7 @@ opts = {
29
29
 
30
30
  HTML = IO.read(File.join(__dir__, 'ws_page.html'))
31
31
 
32
- spawn {
32
+ coproc {
33
33
  server = Polyphony::HTTP::Server.serve('0.0.0.0', 1234, opts) do |req|
34
34
  req.respond(HTML, 'Content-Type' => 'text/html')
35
35
  end
@@ -7,7 +7,7 @@ require 'localhost/authority'
7
7
  STDOUT.sync = true
8
8
 
9
9
  def ws_handler(conn)
10
- timer = spawn {
10
+ timer = coproc {
11
11
  throttled_loop(1) {
12
12
  conn << Time.now.to_s rescue nil
13
13
  }
@@ -11,7 +11,7 @@ rescue => e
11
11
  puts e.backtrace.join("\n")
12
12
  end
13
13
 
14
- time_printer = spawn do
14
+ time_printer = coproc do
15
15
  last = Time.now
16
16
  throttled_loop(10) do
17
17
  now = Time.now
@@ -28,7 +28,7 @@ DBPOOL.preheat!
28
28
  t0 = Time.now
29
29
  count = 0
30
30
  coprocs = CONCURRENCY.times.map {
31
- spawn { loop { DBPOOL.acquire { |db| get_records(db); count += 1 } } }
31
+ coproc { loop { DBPOOL.acquire { |db| get_records(db); count += 1 } } }
32
32
  }
33
33
  sleep 5
34
34
  puts "count: #{count} query rate: #{count / (Time.now - t0)} queries/s"
@@ -16,7 +16,7 @@ class RedisChannel < Polyphony::Channel
16
16
 
17
17
  def self.start_monitor
18
18
  @channels = {}
19
- @monitor = spawn do
19
+ @monitor = coproc do
20
20
  subscribe_connection.subscribe(CHANNEL_MASTER_TOPIC) do |on|
21
21
  on.message do |topic, message|
22
22
  message = Marshal.load(message)
@@ -47,7 +47,7 @@ class RedisChannel < Polyphony::Channel
47
47
 
48
48
  def self.watch(channel)
49
49
  @channels[channel.topic] = channel
50
- spawn do
50
+ coproc do
51
51
  publish_connection.publish(CHANNEL_MASTER_TOPIC, Marshal.dump({
52
52
  kind: :subscribe,
53
53
  topic: channel.topic
@@ -57,7 +57,7 @@ class RedisChannel < Polyphony::Channel
57
57
 
58
58
  def self.unwatch(channel)
59
59
  @channels.delete(channel.topic)
60
- spawn do
60
+ coproc do
61
61
  publish_connection.publish(CHANNEL_MASTER_TOPIC, Marshal.dump({
62
62
  kind: :unsubscribe,
63
63
  topic: channel.topic
@@ -99,14 +99,14 @@ end
99
99
  RedisChannel.start_monitor
100
100
  channel = RedisChannel.new('channel1')
101
101
 
102
- spawn do
102
+ coproc do
103
103
  loop do
104
104
  message = channel.receive
105
105
  puts "got message: #{message}"
106
106
  end
107
107
  end
108
108
 
109
- spawn do
109
+ coproc do
110
110
  move_on_after(3) do
111
111
  throttled_loop(1) do
112
112
  channel << Time.now
@@ -3,7 +3,7 @@
3
3
  require 'bundler/setup'
4
4
  require 'polyphony/redis'
5
5
 
6
- spawn do
6
+ coproc 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 @@ spawn do
13
13
  end
14
14
  end
15
15
 
16
- spawn do
16
+ coproc 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
- spawn do
20
+ coproc 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
- spawn do
43
+ coproc 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
- spawn do
50
+ coproc do
51
51
  last_count = 0
52
52
  last_stamp = Time.now
53
53
  throttled_loop(1) do
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ f = File.open(__FILE__, 'r') do |f|
7
+ line_number = 1
8
+ while (l = f.gets)
9
+ puts "encoding: #{l.encoding.inspect}"
10
+ STDOUT.puts '%03d %s' % [line_number, l]
11
+ line_number += 1
12
+ end
13
+ end
@@ -5,11 +5,11 @@ require 'polyphony'
5
5
 
6
6
  socket = Polyphony::Net.tcp_connect('127.0.0.1', 1234)
7
7
 
8
- writer = spawn do
8
+ writer = coproc do
9
9
  throttled_loop(1) { socket << "#{Time.now}\n" rescue nil }
10
10
  end
11
11
 
12
- reader = spawn do
12
+ reader = coproc do
13
13
  puts "received from echo server:"
14
14
  while data = socket.readpartial(8192)
15
15
  STDOUT << data
@@ -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
- spawn do
9
+ coproc 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
- spawn do
12
+ coproc do
13
13
  cancel_scope = nil
14
14
  move_on_after(5) do |s|
15
15
  cancel_scope = s
@@ -6,7 +6,7 @@ require 'polyphony'
6
6
  puts "Write something..."
7
7
  move_on_after(5) do |scope|
8
8
  loop do
9
- data = STDIN.readpartial(8192)
9
+ data = STDIN.gets
10
10
  scope.reset_timeout
11
11
  puts "you wrote: #{data}"
12
12
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ s = IO.read(__FILE__)
7
+ puts "encoding: #{s.encoding.inspect}"
8
+ puts s
9
+ puts
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ timer = coproc {
7
+ throttled_loop(5) { STDOUT << '.' }
8
+ }
9
+
10
+ puts system('ruby -e "sleep 1; puts :done; STDOUT.close"')
11
+ timer.stop
@@ -6,12 +6,12 @@ require 'polyphony'
6
6
  ITERATIONS = 1_000
7
7
  FIBERS = 1_000
8
8
 
9
- spawn do
9
+ coproc do
10
10
  count = 0
11
11
  t0 = Time.now
12
12
  supervise do |s|
13
13
  FIBERS.times do
14
- s.spawn do
14
+ s.coproc do
15
15
  ITERATIONS.times { snooze; count += 1 }
16
16
  end
17
17
  end
@@ -14,7 +14,7 @@ X = 1_000_000
14
14
  # puts "#{X / dt.to_f}/s"
15
15
 
16
16
  # sleep
17
- # spawn do
17
+ # coproc do
18
18
  # t0 = Time.now
19
19
  # X.times { sleep(0) }
20
20
  # dt = Time.now - t0
@@ -22,7 +22,7 @@ X = 1_000_000
22
22
  # end
23
23
 
24
24
  # snooze
25
- spawn do
25
+ coproc do
26
26
  t0 = Time.now
27
27
  X.times { snooze }
28
28
  dt = Time.now - t0
@@ -46,13 +46,13 @@ 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
- spawn do
49
+ coproc do
50
50
  server = TCPServer.open(1234)
51
51
  puts "listening on port 1234"
52
52
 
53
53
  loop do
54
54
  client = server.accept
55
- spawn handle_client(client)
55
+ coproc handle_client(client)
56
56
  end
57
57
  rescue Exception => e
58
58
  puts "uncaught exception: #{e.inspect}"
data/ext/ev/io.c CHANGED
@@ -32,11 +32,15 @@ void EV_IO_callback(ev_loop *ev_loop, struct ev_io *io, int revents);
32
32
 
33
33
  static int EV_IO_symbol2event_mask(VALUE sym);
34
34
 
35
+ // static VALUE IO_gets(int argc, VALUE *argv, VALUE io);
35
36
  static VALUE IO_read(int argc, VALUE *argv, VALUE io);
36
37
  static VALUE IO_readpartial(int argc, VALUE *argv, VALUE io);
37
38
  static VALUE IO_write(int argc, VALUE *argv, VALUE io);
38
39
  static VALUE IO_write_chevron(VALUE io, VALUE str);
39
40
 
41
+ static VALUE IO_read_watcher(VALUE self);
42
+ static VALUE IO_write_watcher(VALUE self);
43
+
40
44
  void Init_EV_IO() {
41
45
  mEV = rb_define_module("EV");
42
46
  cEV_IO = rb_define_class_under(mEV, "IO", rb_cData);
@@ -48,11 +52,14 @@ void Init_EV_IO() {
48
52
  rb_define_method(cEV_IO, "await", EV_IO_await, 0);
49
53
 
50
54
  VALUE cIO = rb_const_get(rb_cObject, rb_intern("IO"));
55
+ // rb_define_method(cIO, "gets", IO_gets, -1);
51
56
  rb_define_method(cIO, "read", IO_read, -1);
52
57
  rb_define_method(cIO, "readpartial", IO_readpartial, -1);
53
58
  rb_define_method(cIO, "write", IO_write, -1);
54
59
  rb_define_method(cIO, "write_nonblock", IO_write, -1);
55
60
  rb_define_method(cIO, "<<", IO_write_chevron, 1);
61
+ rb_define_method(cIO, "read_watcher", IO_read_watcher, 0);
62
+ rb_define_method(cIO, "write_watcher", IO_write_watcher, 0);
56
63
  }
57
64
 
58
65
  static const rb_data_type_t EV_IO_type = {
@@ -249,6 +256,23 @@ static void io_set_read_length(VALUE str, long n, int shrinkable) {
249
256
  if (shrinkable) io_shrink_read_string(str, n);
250
257
  }
251
258
  }
259
+
260
+ static rb_encoding*
261
+ io_read_encoding(rb_io_t *fptr)
262
+ {
263
+ if (fptr->encs.enc) {
264
+ return fptr->encs.enc;
265
+ }
266
+ return rb_default_external_encoding();
267
+ }
268
+
269
+ static VALUE io_enc_str(VALUE str, rb_io_t *fptr)
270
+ {
271
+ OBJ_TAINT(str);
272
+ rb_enc_associate(str, io_read_encoding(fptr));
273
+ return str;
274
+ }
275
+
252
276
  //////////////////////////////////////////////////////////////////////
253
277
  //////////////////////////////////////////////////////////////////////
254
278
 
@@ -267,9 +291,10 @@ static VALUE IO_read(int argc, VALUE *argv, VALUE io) {
267
291
  VALUE str = argc >= 2 ? argv[1] : Qnil;
268
292
 
269
293
  shrinkable = io_setstrbuf(&str, len);
270
- OBJ_TAINT(str);
294
+ // OBJ_TAINT(str);
271
295
  GetOpenFile(io, fptr);
272
296
  rb_io_check_byte_readable(fptr);
297
+ rb_io_set_nonblock(fptr);
273
298
 
274
299
  if (len == 0)
275
300
  return str;
@@ -283,6 +308,7 @@ static VALUE IO_read(int argc, VALUE *argv, VALUE io) {
283
308
  int e = errno;
284
309
  if ((e == EWOULDBLOCK || e == EAGAIN)) {
285
310
  if (read_watcher == Qnil)
311
+ // read_watcher = IO_read_watcher(io);
286
312
  read_watcher = rb_funcall(io, ID_read_watcher, 0);
287
313
  EV_IO_await(read_watcher);
288
314
  }
@@ -301,11 +327,12 @@ static VALUE IO_read(int argc, VALUE *argv, VALUE io) {
301
327
  }
302
328
  }
303
329
 
304
- io_set_read_length(str, total, shrinkable);
305
-
306
330
  if (total == 0)
307
331
  return Qnil;
308
332
 
333
+ io_set_read_length(str, total, shrinkable);
334
+ io_enc_str(str, fptr);
335
+
309
336
  return str;
310
337
  }
311
338
 
@@ -326,6 +353,7 @@ static VALUE IO_readpartial(int argc, VALUE *argv, VALUE io) {
326
353
  shrinkable = io_setstrbuf(&str, len);
327
354
  OBJ_TAINT(str);
328
355
  GetOpenFile(io, fptr);
356
+ rb_io_set_nonblock(fptr);
329
357
  rb_io_check_byte_readable(fptr);
330
358
 
331
359
  if (len == 0)
@@ -337,6 +365,7 @@ static VALUE IO_readpartial(int argc, VALUE *argv, VALUE io) {
337
365
  int e = errno;
338
366
  if ((e == EWOULDBLOCK || e == EAGAIN)) {
339
367
  if (read_watcher == Qnil)
368
+ // read_watcher = IO_read_watcher(io);
340
369
  read_watcher = rb_funcall(io, ID_read_watcher, 0);
341
370
  EV_IO_await(read_watcher);
342
371
  }
@@ -349,6 +378,7 @@ static VALUE IO_readpartial(int argc, VALUE *argv, VALUE io) {
349
378
  }
350
379
 
351
380
  io_set_read_length(str, n, shrinkable);
381
+ io_enc_str(str, fptr);
352
382
 
353
383
  if (n == 0)
354
384
  return Qnil;
@@ -383,6 +413,7 @@ static VALUE IO_write(int argc, VALUE *argv, VALUE io) {
383
413
  int e = errno;
384
414
  if (e == EWOULDBLOCK || e == EAGAIN) {
385
415
  if (write_watcher == Qnil)
416
+ // write_watcher = IO_write_watcher(io);
386
417
  write_watcher = rb_funcall(io, ID_write_watcher, 0);
387
418
  EV_IO_await(write_watcher);
388
419
  }
@@ -408,4 +439,22 @@ static VALUE IO_write(int argc, VALUE *argv, VALUE io) {
408
439
  static VALUE IO_write_chevron(VALUE io, VALUE str) {
409
440
  IO_write(1, &str, io);
410
441
  return io;
411
- }
442
+ }
443
+
444
+ static VALUE IO_read_watcher(VALUE self) {
445
+ VALUE watcher = rb_iv_get(self, "@read_watcher");
446
+ if (watcher == Qnil) {
447
+ watcher = rb_funcall(cEV_IO, rb_intern("new"), 2, self, ID2SYM(rb_intern("r")));
448
+ rb_iv_set(self, "@read_watcher", watcher);
449
+ }
450
+ return watcher;
451
+ }
452
+
453
+ static VALUE IO_write_watcher(VALUE self) {
454
+ VALUE watcher = rb_iv_get(self, "@write_watcher");
455
+ if (watcher == Qnil) {
456
+ watcher = rb_funcall(cEV_IO, rb_intern("new"), 2, self, ID2SYM(rb_intern("w")));
457
+ rb_iv_set(self, "@write_watcher", watcher);
458
+ }
459
+ return watcher;
460
+ }