polyphony 0.16 → 0.17
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/Gemfile.lock +1 -1
- data/README.md +11 -11
- data/TODO.md +14 -5
- data/examples/core/channel_echo.rb +3 -3
- data/examples/core/enumerator.rb +1 -1
- data/examples/core/fork.rb +1 -1
- data/examples/core/genserver.rb +1 -1
- data/examples/core/lock.rb +3 -3
- data/examples/core/multiple_spawn.rb +2 -2
- data/examples/core/nested_async.rb +1 -1
- data/examples/core/nested_multiple_spawn.rb +3 -3
- data/examples/core/resource.rb +1 -1
- data/examples/core/resource_cancel.rb +1 -1
- data/examples/core/resource_delegate.rb +1 -1
- data/examples/core/sleep_spawn.rb +2 -2
- data/examples/core/spawn.rb +1 -1
- data/examples/core/spawn_cancel.rb +1 -1
- data/examples/core/spawn_error.rb +5 -5
- data/examples/core/supervisor.rb +4 -4
- data/examples/core/supervisor_with_cancel_scope.rb +3 -3
- data/examples/core/supervisor_with_error.rb +4 -4
- data/examples/core/supervisor_with_manual_move_on.rb +4 -4
- data/examples/core/thread.rb +2 -2
- data/examples/core/thread_cancel.rb +2 -2
- data/examples/core/thread_pool.rb +2 -2
- data/examples/core/throttle.rb +3 -3
- data/examples/fs/read.rb +1 -1
- data/examples/http/happy_eyeballs.rb +1 -1
- data/examples/http/http_client.rb +1 -1
- data/examples/http/http_server.rb +1 -1
- data/examples/http/http_server_throttled.rb +1 -1
- data/examples/http/http_ws_server.rb +2 -2
- data/examples/http/https_wss_server.rb +1 -1
- data/examples/interfaces/pg_client.rb +1 -1
- data/examples/interfaces/pg_pool.rb +1 -1
- data/examples/interfaces/redis_channels.rb +5 -5
- data/examples/interfaces/redis_pubsub.rb +2 -2
- data/examples/interfaces/redis_pubsub_perf.rb +3 -3
- data/examples/io/cat.rb +13 -0
- data/examples/io/echo_client.rb +2 -2
- data/examples/io/echo_server.rb +1 -1
- data/examples/io/echo_server_with_timeout.rb +1 -1
- data/examples/io/echo_stdin.rb +1 -1
- data/examples/io/io_read.rb +9 -0
- data/examples/io/system.rb +11 -0
- data/examples/performance/perf_multi_snooze.rb +2 -2
- data/examples/performance/perf_snooze.rb +2 -2
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +2 -2
- data/ext/ev/io.c +53 -4
- data/lib/polyphony/core/coprocess.rb +1 -0
- data/lib/polyphony/core/supervisor.rb +1 -1
- data/lib/polyphony/extensions/io.rb +97 -17
- data/lib/polyphony/extensions/kernel.rb +47 -27
- data/lib/polyphony/http/server.rb +1 -1
- data/lib/polyphony/postgres.rb +0 -4
- data/lib/polyphony/version.rb +1 -1
- data/test/test_coprocess.rb +13 -13
- data/test/test_core.rb +12 -12
- data/test/test_io.rb +95 -3
- data/test/test_kernel.rb +26 -0
- 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.
|
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
|
-
|
57
|
+
coproc { compare_performance }
|
data/examples/core/throttle.rb
CHANGED
@@ -3,14 +3,14 @@
|
|
3
3
|
require 'bundler/setup'
|
4
4
|
require 'polyphony'
|
5
5
|
|
6
|
-
|
6
|
+
coproc {
|
7
7
|
throttled_loop(3) { STDOUT << '.' }
|
8
8
|
}
|
9
9
|
|
10
|
-
|
10
|
+
coproc {
|
11
11
|
throttled_loop(rate: 2) { STDOUT << '?' }
|
12
12
|
}
|
13
13
|
|
14
|
-
|
14
|
+
coproc {
|
15
15
|
throttled_loop(interval: 1) { STDOUT << '*' }
|
16
16
|
}
|
data/examples/fs/read.rb
CHANGED
@@ -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.
|
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.
|
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"
|
@@ -7,7 +7,7 @@ require 'polyphony/http'
|
|
7
7
|
require 'polyphony/websocket'
|
8
8
|
|
9
9
|
def ws_handler(conn)
|
10
|
-
timer =
|
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
|
-
|
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
|
@@ -28,7 +28,7 @@ DBPOOL.preheat!
|
|
28
28
|
t0 = Time.now
|
29
29
|
count = 0
|
30
30
|
coprocs = CONCURRENCY.times.map {
|
31
|
-
|
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 =
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
50
|
+
coproc do
|
51
51
|
last_count = 0
|
52
52
|
last_stamp = Time.now
|
53
53
|
throttled_loop(1) do
|
data/examples/io/cat.rb
ADDED
@@ -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
|
data/examples/io/echo_client.rb
CHANGED
@@ -5,11 +5,11 @@ require 'polyphony'
|
|
5
5
|
|
6
6
|
socket = Polyphony::Net.tcp_connect('127.0.0.1', 1234)
|
7
7
|
|
8
|
-
writer =
|
8
|
+
writer = coproc do
|
9
9
|
throttled_loop(1) { socket << "#{Time.now}\n" rescue nil }
|
10
10
|
end
|
11
11
|
|
12
|
-
reader =
|
12
|
+
reader = coproc do
|
13
13
|
puts "received from echo server:"
|
14
14
|
while data = socket.readpartial(8192)
|
15
15
|
STDOUT << data
|
data/examples/io/echo_server.rb
CHANGED
data/examples/io/echo_stdin.rb
CHANGED
@@ -14,7 +14,7 @@ X = 1_000_000
|
|
14
14
|
# puts "#{X / dt.to_f}/s"
|
15
15
|
|
16
16
|
# sleep
|
17
|
-
#
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
+
}
|