polyphony 0.13 → 0.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitbook.yaml +5 -0
- data/.gitignore +55 -0
- data/.rubocop.yml +49 -0
- data/CHANGELOG.md +13 -2
- data/Gemfile +3 -0
- data/Gemfile.lock +31 -0
- data/LICENSE +21 -0
- data/README.md +35 -18
- data/Rakefile +20 -0
- data/TODO.md +49 -0
- data/docs/getting-started/getting-started.md +10 -0
- data/docs/getting-started/tutorial.md +2 -0
- data/docs/summary.md +9 -0
- data/examples/core/cancel.rb +10 -0
- data/examples/core/channel_echo.rb +43 -0
- data/examples/core/enumerator.rb +14 -0
- data/examples/core/fork.rb +22 -0
- data/examples/core/genserver.rb +74 -0
- data/examples/core/lock.rb +20 -0
- data/examples/core/move_on.rb +11 -0
- data/examples/core/move_on_twice.rb +17 -0
- data/examples/core/move_on_with_ensure.rb +17 -0
- data/examples/core/multiple_async.rb +17 -0
- data/examples/core/nested_async.rb +18 -0
- data/examples/core/nested_cancel.rb +41 -0
- data/examples/core/nested_multiple_async.rb +19 -0
- data/examples/core/next_tick.rb +13 -0
- data/examples/core/pulse.rb +13 -0
- data/examples/core/resource.rb +29 -0
- data/examples/core/resource_cancel.rb +34 -0
- data/examples/core/resource_delegate.rb +32 -0
- data/examples/core/sleep.rb +9 -0
- data/examples/core/sleep2.rb +13 -0
- data/examples/core/spawn.rb +15 -0
- data/examples/core/spawn_cancel.rb +19 -0
- data/examples/core/spawn_error.rb +28 -0
- data/examples/core/supervisor.rb +22 -0
- data/examples/core/supervisor_with_cancel_scope.rb +24 -0
- data/examples/core/supervisor_with_error.rb +23 -0
- data/examples/core/supervisor_with_manual_move_on.rb +25 -0
- data/examples/core/thread.rb +30 -0
- data/examples/core/thread_cancel.rb +30 -0
- data/examples/core/thread_pool.rb +60 -0
- data/examples/core/throttle.rb +17 -0
- data/examples/fs/read.rb +37 -0
- data/examples/interfaces/pg_client.rb +38 -0
- data/examples/interfaces/pg_pool.rb +37 -0
- data/examples/interfaces/pg_query.rb +32 -0
- data/examples/interfaces/redis_channels.rb +119 -0
- data/examples/interfaces/redis_client.rb +21 -0
- data/examples/interfaces/redis_pubsub.rb +26 -0
- data/examples/interfaces/redis_pubsub_perf.rb +65 -0
- data/examples/io/config.ru +3 -0
- data/examples/io/echo_client.rb +22 -0
- data/examples/io/echo_server.rb +14 -0
- data/examples/io/echo_server_with_timeout.rb +33 -0
- data/examples/io/echo_stdin.rb +15 -0
- data/examples/io/happy_eyeballs.rb +32 -0
- data/examples/io/http_client.rb +19 -0
- data/examples/io/http_server.js +24 -0
- data/examples/io/http_server.rb +16 -0
- data/examples/io/http_server_forked.rb +27 -0
- data/examples/io/http_server_throttled.rb +16 -0
- data/examples/io/http_ws_server.rb +42 -0
- data/examples/io/https_client.rb +17 -0
- data/examples/io/https_server.rb +23 -0
- data/examples/io/https_wss_server.rb +46 -0
- data/examples/io/rack_server.rb +19 -0
- data/examples/io/rack_server_https.rb +24 -0
- data/examples/io/rack_server_https_forked.rb +32 -0
- data/examples/io/websocket_server.rb +33 -0
- data/examples/io/ws_page.html +34 -0
- data/examples/io/wss_page.html +34 -0
- data/examples/performance/perf_multi_snooze.rb +21 -0
- data/examples/performance/perf_snooze.rb +30 -0
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +63 -0
- data/examples/performance/thread-vs-fiber/threaded_server.rb +27 -0
- data/examples/streams/lines.rb +27 -0
- data/examples/streams/stdio.rb +18 -0
- data/ext/ev/async.c +168 -0
- data/ext/ev/child.c +169 -0
- data/ext/ev/ev.h +32 -0
- data/ext/ev/ev_ext.c +20 -0
- data/ext/ev/ev_module.c +222 -0
- data/ext/ev/io.c +405 -0
- data/ext/ev/libev.h +9 -0
- data/ext/ev/signal.c +119 -0
- data/ext/ev/timer.c +197 -0
- data/ext/libev/Changes +513 -0
- data/ext/libev/LICENSE +37 -0
- data/ext/libev/README +58 -0
- data/ext/libev/README.embed +3 -0
- data/ext/libev/ev.c +5214 -0
- data/ext/libev/ev.h +849 -0
- data/ext/libev/ev_epoll.c +285 -0
- data/ext/libev/ev_kqueue.c +218 -0
- data/ext/libev/ev_poll.c +151 -0
- data/ext/libev/ev_port.c +189 -0
- data/ext/libev/ev_select.c +316 -0
- data/ext/libev/ev_vars.h +204 -0
- data/ext/libev/ev_win32.c +162 -0
- data/ext/libev/ev_wrap.h +200 -0
- data/ext/libev/test_libev_win32.c +123 -0
- data/lib/polyphony.rb +7 -2
- data/lib/polyphony/core.rb +1 -1
- data/lib/polyphony/core/{coroutine.rb → coprocess.rb} +10 -10
- data/lib/polyphony/core/exceptions.rb +5 -5
- data/lib/polyphony/core/supervisor.rb +16 -16
- data/lib/polyphony/core/thread.rb +1 -1
- data/lib/polyphony/extensions/io.rb +43 -42
- data/lib/polyphony/extensions/kernel.rb +10 -34
- data/lib/polyphony/extensions/postgres.rb +3 -2
- data/lib/polyphony/extensions/redis.rb +1 -1
- data/lib/polyphony/extensions/socket.rb +8 -4
- data/lib/polyphony/extensions/ssl.rb +0 -54
- data/lib/polyphony/http/agent.rb +4 -10
- data/lib/polyphony/http/http1.rb +25 -25
- data/lib/polyphony/http/http1_request.rb +38 -26
- data/lib/polyphony/http/http2.rb +4 -5
- data/lib/polyphony/http/http2_request.rb +12 -18
- data/lib/polyphony/http/rack.rb +1 -3
- data/lib/polyphony/http/server.rb +9 -9
- data/lib/polyphony/net.rb +2 -2
- data/lib/polyphony/resource_pool.rb +5 -1
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony/websocket.rb +52 -0
- data/polyphony.gemspec +31 -0
- data/test/test_coprocess.rb +131 -0
- data/test/test_core.rb +274 -0
- data/test/test_ev.rb +117 -0
- data/test/test_io.rb +38 -0
- metadata +113 -7
- data/lib/polyphony/core/async.rb +0 -36
- data/lib/polyphony/net_old.rb +0 -299
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'modulation'
|
4
|
+
Polyphony = import('../../lib/polyphony')
|
5
|
+
|
6
|
+
puts "parent pid: #{Process.pid}"
|
7
|
+
|
8
|
+
pid = Polyphony.fork do
|
9
|
+
puts "child pid: #{Process.pid}"
|
10
|
+
|
11
|
+
spawn do
|
12
|
+
puts "child going to sleep 1..."
|
13
|
+
sleep 1
|
14
|
+
puts "child woke up 1"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
puts "got child pid #{pid}"
|
19
|
+
|
20
|
+
puts "waiting for child"
|
21
|
+
EV::Child.new(pid).await
|
22
|
+
puts "child is done"
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'modulation'
|
4
|
+
|
5
|
+
Polyphony = import('../../lib/polyphony')
|
6
|
+
|
7
|
+
class GenServer
|
8
|
+
def self.start(receiver, *args)
|
9
|
+
coprocess = spawn do
|
10
|
+
state = receiver.initial_state(*args)
|
11
|
+
loop do
|
12
|
+
msg = receive
|
13
|
+
reply, state = receiver.send(msg[:method], state, *msg[:args])
|
14
|
+
msg[:from] << reply unless reply == :noreply
|
15
|
+
end
|
16
|
+
end
|
17
|
+
build_api(coprocess, receiver)
|
18
|
+
EV.snooze
|
19
|
+
coprocess
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.build_api(coprocess, receiver)
|
23
|
+
receiver.methods(false).each do |m|
|
24
|
+
if m =~ /!$/
|
25
|
+
coprocess.define_singleton_method(m) do |*args|
|
26
|
+
GenServer.cast(coprocess, m, *args)
|
27
|
+
end
|
28
|
+
else
|
29
|
+
coprocess.define_singleton_method(m) do |*args|
|
30
|
+
GenServer.call(coprocess, m, *args)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.cast(process, method, *args)
|
37
|
+
process << {from: Polyphony::Coprocess.current, method: method, args: args}
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.call(process, method, *args)
|
41
|
+
process << {from: Polyphony::Coprocess.current, method: method, args: args}
|
42
|
+
receive
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module Map
|
47
|
+
def self.initial_state(hash = {})
|
48
|
+
hash
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.get(state, key)
|
52
|
+
[state[key], state]
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.put!(state, key, value)
|
56
|
+
state[key] = value
|
57
|
+
[:noreply, state]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
map_server = GenServer.start(Map, {foo: :bar})
|
62
|
+
|
63
|
+
puts "getting value from map server"
|
64
|
+
v = map_server.get(:foo)
|
65
|
+
puts "value: #{v.inspect}"
|
66
|
+
|
67
|
+
puts "putting value in map server"
|
68
|
+
map_server.put!(:foo, 42)
|
69
|
+
|
70
|
+
puts "getting value from map server"
|
71
|
+
v = map_server.get(:foo)
|
72
|
+
puts "value: #{v.inspect}"
|
73
|
+
|
74
|
+
map_server.stop
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'modulation'
|
4
|
+
|
5
|
+
Polyphony = import('../../lib/polyphony')
|
6
|
+
|
7
|
+
def loop_it(number, lock)
|
8
|
+
loop do
|
9
|
+
sleep(rand*0.2)
|
10
|
+
lock.synchronize do
|
11
|
+
puts "child #{number} has the lock"
|
12
|
+
sleep(rand*0.05)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
lock = Polyphony::Sync::Mutex.new
|
18
|
+
spawn { loop_it(1, lock) }
|
19
|
+
spawn { loop_it(2, lock) }
|
20
|
+
spawn { loop_it(3, lock) }
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'modulation'
|
4
|
+
|
5
|
+
Polyphony = import('../../lib/polyphony')
|
6
|
+
|
7
|
+
puts "going to sleep..."
|
8
|
+
move_on_after(1) do
|
9
|
+
sleep 60
|
10
|
+
end
|
11
|
+
puts "woke up"
|
12
|
+
|
13
|
+
puts "going to sleep..."
|
14
|
+
move_on_after(1) do
|
15
|
+
sleep 60
|
16
|
+
end
|
17
|
+
puts "woke up"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'modulation'
|
4
|
+
|
5
|
+
Polyphony = import('../../lib/polyphony')
|
6
|
+
|
7
|
+
puts "going to sleep..."
|
8
|
+
move_on_after(0.5) do |scope|
|
9
|
+
begin
|
10
|
+
sleep 60
|
11
|
+
ensure
|
12
|
+
puts "in ensure (is it going to block?)"
|
13
|
+
# this should not block, since the scope was cancelled
|
14
|
+
sleep 10 unless scope.cancelled?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
puts "woke up"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'modulation'
|
4
|
+
|
5
|
+
Polyphony = import('../../lib/polyphony')
|
6
|
+
|
7
|
+
spawn do
|
8
|
+
puts "going to sleep"
|
9
|
+
result = async do
|
10
|
+
async do
|
11
|
+
async do
|
12
|
+
puts "Fiber count: #{Polyphony::FiberPool.size}"
|
13
|
+
sleep(1)
|
14
|
+
end.await
|
15
|
+
end.await
|
16
|
+
end.await
|
17
|
+
puts "result: #{result}"
|
18
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'modulation'
|
4
|
+
|
5
|
+
Polyphony = import('../../lib/polyphony')
|
6
|
+
|
7
|
+
async def sleep_and_cancel
|
8
|
+
puts "#{Time.now} going to sleep with cancel..."
|
9
|
+
cancel_after(1) do
|
10
|
+
puts "#{Time.now} outer cancel scope"
|
11
|
+
cancel_after(10) do
|
12
|
+
puts "#{Time.now} inner cancel scope"
|
13
|
+
sleep 60
|
14
|
+
rescue Exception => e
|
15
|
+
puts "#{Time.now} inner scope got error: #{e}"
|
16
|
+
raise e
|
17
|
+
end
|
18
|
+
rescue Exception => e
|
19
|
+
puts "#{Time.now} outer scope got error: #{e}"
|
20
|
+
end
|
21
|
+
ensure
|
22
|
+
puts "#{Time.now} woke up"
|
23
|
+
end
|
24
|
+
|
25
|
+
async def sleep_and_move_on
|
26
|
+
puts "#{Time.now} going to sleep with move_on..."
|
27
|
+
move_on_after(1) do
|
28
|
+
puts "#{Time.now} outer cancel scope"
|
29
|
+
move_on_after(10) do
|
30
|
+
puts "#{Time.now} inner cancel scope"
|
31
|
+
sleep 60
|
32
|
+
puts "#{Time.now} inner scope done"
|
33
|
+
end
|
34
|
+
puts "#{Time.now} outer scope done"
|
35
|
+
end
|
36
|
+
puts "#{Time.now} woke up"
|
37
|
+
end
|
38
|
+
|
39
|
+
sleep_and_cancel.await
|
40
|
+
puts
|
41
|
+
sleep_and_move_on.await
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'modulation'
|
4
|
+
|
5
|
+
Polyphony = import('../../lib/polyphony')
|
6
|
+
|
7
|
+
resource_count = 0
|
8
|
+
Pool = Polyphony::ResourcePool.new(limit: 3) do
|
9
|
+
:"resource#{resource_count += 1}"
|
10
|
+
end
|
11
|
+
|
12
|
+
async def user(number)
|
13
|
+
loop do
|
14
|
+
# puts "user #{number} >"
|
15
|
+
Pool.acquire do |r|
|
16
|
+
puts "user #{number} #{r.inspect} >"
|
17
|
+
sleep(0.05 + rand * 0.2)
|
18
|
+
# STDOUT << '.'
|
19
|
+
# puts "#{number}: #{r.inspect}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
100.times do |x|
|
25
|
+
spawn user(x)
|
26
|
+
end
|
27
|
+
|
28
|
+
t0 = Time.now
|
29
|
+
every(10) { puts "uptime: #{Time.now - t0}" }
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'modulation'
|
4
|
+
|
5
|
+
Polyphony = import('../../lib/polyphony')
|
6
|
+
|
7
|
+
resource_count = 0
|
8
|
+
Pool = Polyphony::ResourcePool.new(limit: 3) do
|
9
|
+
:"resource#{resource_count += 1}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def user(number)
|
13
|
+
loop do
|
14
|
+
move_on_after(0.2) do |scope|
|
15
|
+
scope.when_cancelled do
|
16
|
+
puts "#{number} (cancelled)"
|
17
|
+
end
|
18
|
+
|
19
|
+
Pool.acquire do |r|
|
20
|
+
scope.disable
|
21
|
+
puts "#{number} #{r.inspect} >"
|
22
|
+
sleep(0.4 + rand * 0.2)
|
23
|
+
puts "#{number} #{r.inspect} <"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
6.times do |x|
|
30
|
+
spawn { user(x) }
|
31
|
+
end
|
32
|
+
|
33
|
+
t0 = Time.now
|
34
|
+
every(10) { puts "uptime: #{Time.now - t0}" }
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'modulation'
|
4
|
+
|
5
|
+
Polyphony = import('../../lib/polyphony')
|
6
|
+
|
7
|
+
class Number
|
8
|
+
def initialize(id)
|
9
|
+
@id = id
|
10
|
+
end
|
11
|
+
|
12
|
+
def greet(other)
|
13
|
+
puts "You are number #{other}, I am number #{@id}"
|
14
|
+
sleep(0.05 + rand * 0.2)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
resource_count = 0
|
19
|
+
Pool = Polyphony::ResourcePool.new(limit: 3) do
|
20
|
+
Number.new(resource_count += 1)
|
21
|
+
end
|
22
|
+
|
23
|
+
async def meet(number)
|
24
|
+
loop do
|
25
|
+
Pool.greet(number)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
3.times { |x| spawn meet(x) }
|
30
|
+
|
31
|
+
t0 = Time.now
|
32
|
+
every(10) { puts "uptime: #{Time.now - t0}" }
|