polyphony 0.13 → 0.14

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.
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