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