polyphony 0.13 → 0.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.gitbook.yaml +5 -0
  3. data/.gitignore +55 -0
  4. data/.rubocop.yml +49 -0
  5. data/CHANGELOG.md +13 -2
  6. data/Gemfile +3 -0
  7. data/Gemfile.lock +31 -0
  8. data/LICENSE +21 -0
  9. data/README.md +35 -18
  10. data/Rakefile +20 -0
  11. data/TODO.md +49 -0
  12. data/docs/getting-started/getting-started.md +10 -0
  13. data/docs/getting-started/tutorial.md +2 -0
  14. data/docs/summary.md +9 -0
  15. data/examples/core/cancel.rb +10 -0
  16. data/examples/core/channel_echo.rb +43 -0
  17. data/examples/core/enumerator.rb +14 -0
  18. data/examples/core/fork.rb +22 -0
  19. data/examples/core/genserver.rb +74 -0
  20. data/examples/core/lock.rb +20 -0
  21. data/examples/core/move_on.rb +11 -0
  22. data/examples/core/move_on_twice.rb +17 -0
  23. data/examples/core/move_on_with_ensure.rb +17 -0
  24. data/examples/core/multiple_async.rb +17 -0
  25. data/examples/core/nested_async.rb +18 -0
  26. data/examples/core/nested_cancel.rb +41 -0
  27. data/examples/core/nested_multiple_async.rb +19 -0
  28. data/examples/core/next_tick.rb +13 -0
  29. data/examples/core/pulse.rb +13 -0
  30. data/examples/core/resource.rb +29 -0
  31. data/examples/core/resource_cancel.rb +34 -0
  32. data/examples/core/resource_delegate.rb +32 -0
  33. data/examples/core/sleep.rb +9 -0
  34. data/examples/core/sleep2.rb +13 -0
  35. data/examples/core/spawn.rb +15 -0
  36. data/examples/core/spawn_cancel.rb +19 -0
  37. data/examples/core/spawn_error.rb +28 -0
  38. data/examples/core/supervisor.rb +22 -0
  39. data/examples/core/supervisor_with_cancel_scope.rb +24 -0
  40. data/examples/core/supervisor_with_error.rb +23 -0
  41. data/examples/core/supervisor_with_manual_move_on.rb +25 -0
  42. data/examples/core/thread.rb +30 -0
  43. data/examples/core/thread_cancel.rb +30 -0
  44. data/examples/core/thread_pool.rb +60 -0
  45. data/examples/core/throttle.rb +17 -0
  46. data/examples/fs/read.rb +37 -0
  47. data/examples/interfaces/pg_client.rb +38 -0
  48. data/examples/interfaces/pg_pool.rb +37 -0
  49. data/examples/interfaces/pg_query.rb +32 -0
  50. data/examples/interfaces/redis_channels.rb +119 -0
  51. data/examples/interfaces/redis_client.rb +21 -0
  52. data/examples/interfaces/redis_pubsub.rb +26 -0
  53. data/examples/interfaces/redis_pubsub_perf.rb +65 -0
  54. data/examples/io/config.ru +3 -0
  55. data/examples/io/echo_client.rb +22 -0
  56. data/examples/io/echo_server.rb +14 -0
  57. data/examples/io/echo_server_with_timeout.rb +33 -0
  58. data/examples/io/echo_stdin.rb +15 -0
  59. data/examples/io/happy_eyeballs.rb +32 -0
  60. data/examples/io/http_client.rb +19 -0
  61. data/examples/io/http_server.js +24 -0
  62. data/examples/io/http_server.rb +16 -0
  63. data/examples/io/http_server_forked.rb +27 -0
  64. data/examples/io/http_server_throttled.rb +16 -0
  65. data/examples/io/http_ws_server.rb +42 -0
  66. data/examples/io/https_client.rb +17 -0
  67. data/examples/io/https_server.rb +23 -0
  68. data/examples/io/https_wss_server.rb +46 -0
  69. data/examples/io/rack_server.rb +19 -0
  70. data/examples/io/rack_server_https.rb +24 -0
  71. data/examples/io/rack_server_https_forked.rb +32 -0
  72. data/examples/io/websocket_server.rb +33 -0
  73. data/examples/io/ws_page.html +34 -0
  74. data/examples/io/wss_page.html +34 -0
  75. data/examples/performance/perf_multi_snooze.rb +21 -0
  76. data/examples/performance/perf_snooze.rb +30 -0
  77. data/examples/performance/thread-vs-fiber/polyphony_server.rb +63 -0
  78. data/examples/performance/thread-vs-fiber/threaded_server.rb +27 -0
  79. data/examples/streams/lines.rb +27 -0
  80. data/examples/streams/stdio.rb +18 -0
  81. data/ext/ev/async.c +168 -0
  82. data/ext/ev/child.c +169 -0
  83. data/ext/ev/ev.h +32 -0
  84. data/ext/ev/ev_ext.c +20 -0
  85. data/ext/ev/ev_module.c +222 -0
  86. data/ext/ev/io.c +405 -0
  87. data/ext/ev/libev.h +9 -0
  88. data/ext/ev/signal.c +119 -0
  89. data/ext/ev/timer.c +197 -0
  90. data/ext/libev/Changes +513 -0
  91. data/ext/libev/LICENSE +37 -0
  92. data/ext/libev/README +58 -0
  93. data/ext/libev/README.embed +3 -0
  94. data/ext/libev/ev.c +5214 -0
  95. data/ext/libev/ev.h +849 -0
  96. data/ext/libev/ev_epoll.c +285 -0
  97. data/ext/libev/ev_kqueue.c +218 -0
  98. data/ext/libev/ev_poll.c +151 -0
  99. data/ext/libev/ev_port.c +189 -0
  100. data/ext/libev/ev_select.c +316 -0
  101. data/ext/libev/ev_vars.h +204 -0
  102. data/ext/libev/ev_win32.c +162 -0
  103. data/ext/libev/ev_wrap.h +200 -0
  104. data/ext/libev/test_libev_win32.c +123 -0
  105. data/lib/polyphony.rb +7 -2
  106. data/lib/polyphony/core.rb +1 -1
  107. data/lib/polyphony/core/{coroutine.rb → coprocess.rb} +10 -10
  108. data/lib/polyphony/core/exceptions.rb +5 -5
  109. data/lib/polyphony/core/supervisor.rb +16 -16
  110. data/lib/polyphony/core/thread.rb +1 -1
  111. data/lib/polyphony/extensions/io.rb +43 -42
  112. data/lib/polyphony/extensions/kernel.rb +10 -34
  113. data/lib/polyphony/extensions/postgres.rb +3 -2
  114. data/lib/polyphony/extensions/redis.rb +1 -1
  115. data/lib/polyphony/extensions/socket.rb +8 -4
  116. data/lib/polyphony/extensions/ssl.rb +0 -54
  117. data/lib/polyphony/http/agent.rb +4 -10
  118. data/lib/polyphony/http/http1.rb +25 -25
  119. data/lib/polyphony/http/http1_request.rb +38 -26
  120. data/lib/polyphony/http/http2.rb +4 -5
  121. data/lib/polyphony/http/http2_request.rb +12 -18
  122. data/lib/polyphony/http/rack.rb +1 -3
  123. data/lib/polyphony/http/server.rb +9 -9
  124. data/lib/polyphony/net.rb +2 -2
  125. data/lib/polyphony/resource_pool.rb +5 -1
  126. data/lib/polyphony/version.rb +1 -1
  127. data/lib/polyphony/websocket.rb +52 -0
  128. data/polyphony.gemspec +31 -0
  129. data/test/test_coprocess.rb +131 -0
  130. data/test/test_core.rb +274 -0
  131. data/test/test_ev.rb +117 -0
  132. data/test/test_io.rb +38 -0
  133. metadata +113 -7
  134. data/lib/polyphony/core/async.rb +0 -36
  135. data/lib/polyphony/net_old.rb +0 -299
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'modulation'
4
+
5
+ Polyphony = import('../../lib/polyphony')
6
+
7
+ enum = [1,2,3].each
8
+
9
+ spawn do
10
+ while e = enum.next rescue nil
11
+ puts e
12
+ sleep 1
13
+ end
14
+ end
@@ -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,11 @@
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"
@@ -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,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'modulation'
4
+
5
+ Polyphony = import('../../lib/polyphony')
6
+
7
+ spawn do
8
+ puts "1 >"
9
+ sleep(1)
10
+ puts "1 <"
11
+ end
12
+
13
+ spawn do
14
+ puts "2 >"
15
+ sleep(1)
16
+ puts "2 <"
17
+ end
@@ -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,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'modulation'
4
+
5
+ Polyphony = import('../../lib/polyphony')
6
+
7
+ spawn do
8
+ spawn do
9
+ puts "1 >"
10
+ sleep(1)
11
+ puts "1 <"
12
+ end
13
+
14
+ spawn do
15
+ puts "2 >"
16
+ sleep(1)
17
+ puts "2 <"
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'modulation'
4
+
5
+ Polyphony = import('../../lib/polyphony')
6
+
7
+ EV.next_tick do
8
+ puts "two"
9
+ EV.next_tick { puts "four" }
10
+ puts "three"
11
+ end
12
+
13
+ puts "one"
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'modulation'
4
+
5
+ Polyphony = import('../../lib/polyphony')
6
+
7
+ move_on_after(3) do
8
+ pulser = pulse(1)
9
+ while pulser.await
10
+ puts Time.now
11
+ end
12
+ end
13
+ puts "done!"
@@ -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}" }
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'modulation'
4
+
5
+ Polyphony = import('../../lib/polyphony')
6
+
7
+ puts "going to sleep..."
8
+ sleep 1
9
+ puts "woke up"
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'modulation'
4
+
5
+ Polyphony = import('../../lib/polyphony')
6
+
7
+ spawn {
8
+ puts "going to sleep..."
9
+ sleep 1
10
+ puts "woke up"
11
+ }.await
12
+
13
+ puts "done"
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'modulation'
4
+
5
+ Polyphony = import('../../lib/polyphony')
6
+
7
+ def my_sleep(t)
8
+ puts "going to sleep..."
9
+ sleep t
10
+ puts "woke up"
11
+ end
12
+
13
+ spawn do
14
+ async { my_sleep(1) }.await
15
+ end