polyphony 0.43.8

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 (221) hide show
  1. checksums.yaml +7 -0
  2. data/.gitbook.yaml +4 -0
  3. data/.github/workflows/test.yml +29 -0
  4. data/.gitignore +59 -0
  5. data/.rubocop.yml +175 -0
  6. data/CHANGELOG.md +393 -0
  7. data/Gemfile +3 -0
  8. data/Gemfile.lock +141 -0
  9. data/LICENSE +21 -0
  10. data/README.md +51 -0
  11. data/Rakefile +26 -0
  12. data/TODO.md +201 -0
  13. data/bin/polyphony-debug +87 -0
  14. data/docs/_config.yml +64 -0
  15. data/docs/_includes/head.html +40 -0
  16. data/docs/_includes/title.html +1 -0
  17. data/docs/_sass/custom/custom.scss +10 -0
  18. data/docs/_sass/overrides.scss +0 -0
  19. data/docs/_user-guide/all-about-timers.md +126 -0
  20. data/docs/_user-guide/index.md +9 -0
  21. data/docs/_user-guide/web-server.md +136 -0
  22. data/docs/api-reference/exception.md +27 -0
  23. data/docs/api-reference/fiber.md +425 -0
  24. data/docs/api-reference/index.md +9 -0
  25. data/docs/api-reference/io.md +36 -0
  26. data/docs/api-reference/object.md +99 -0
  27. data/docs/api-reference/polyphony-baseexception.md +33 -0
  28. data/docs/api-reference/polyphony-cancel.md +26 -0
  29. data/docs/api-reference/polyphony-moveon.md +24 -0
  30. data/docs/api-reference/polyphony-net.md +20 -0
  31. data/docs/api-reference/polyphony-process.md +28 -0
  32. data/docs/api-reference/polyphony-resourcepool.md +59 -0
  33. data/docs/api-reference/polyphony-restart.md +18 -0
  34. data/docs/api-reference/polyphony-terminate.md +18 -0
  35. data/docs/api-reference/polyphony-threadpool.md +67 -0
  36. data/docs/api-reference/polyphony-throttler.md +77 -0
  37. data/docs/api-reference/polyphony.md +36 -0
  38. data/docs/api-reference/thread.md +88 -0
  39. data/docs/assets/img/echo-fibers.svg +1 -0
  40. data/docs/assets/img/sleeping-fiber.svg +1 -0
  41. data/docs/faq.md +195 -0
  42. data/docs/favicon.ico +0 -0
  43. data/docs/getting-started/index.md +10 -0
  44. data/docs/getting-started/installing.md +34 -0
  45. data/docs/getting-started/overview.md +486 -0
  46. data/docs/getting-started/tutorial.md +359 -0
  47. data/docs/index.md +94 -0
  48. data/docs/main-concepts/concurrency.md +151 -0
  49. data/docs/main-concepts/design-principles.md +161 -0
  50. data/docs/main-concepts/exception-handling.md +291 -0
  51. data/docs/main-concepts/extending.md +89 -0
  52. data/docs/main-concepts/fiber-scheduling.md +197 -0
  53. data/docs/main-concepts/index.md +9 -0
  54. data/docs/polyphony-logo.png +0 -0
  55. data/examples/adapters/concurrent-ruby.rb +9 -0
  56. data/examples/adapters/pg_client.rb +36 -0
  57. data/examples/adapters/pg_notify.rb +35 -0
  58. data/examples/adapters/pg_pool.rb +43 -0
  59. data/examples/adapters/pg_transaction.rb +31 -0
  60. data/examples/adapters/redis_blpop.rb +12 -0
  61. data/examples/adapters/redis_channels.rb +122 -0
  62. data/examples/adapters/redis_client.rb +19 -0
  63. data/examples/adapters/redis_pubsub.rb +26 -0
  64. data/examples/adapters/redis_pubsub_perf.rb +68 -0
  65. data/examples/core/01-spinning-up-fibers.rb +18 -0
  66. data/examples/core/02-awaiting-fibers.rb +20 -0
  67. data/examples/core/03-interrupting.rb +39 -0
  68. data/examples/core/04-handling-signals.rb +19 -0
  69. data/examples/core/xx-agent.rb +102 -0
  70. data/examples/core/xx-at_exit.rb +29 -0
  71. data/examples/core/xx-caller.rb +12 -0
  72. data/examples/core/xx-channels.rb +45 -0
  73. data/examples/core/xx-daemon.rb +14 -0
  74. data/examples/core/xx-deadlock.rb +8 -0
  75. data/examples/core/xx-deferring-an-operation.rb +14 -0
  76. data/examples/core/xx-erlang-style-genserver.rb +81 -0
  77. data/examples/core/xx-exception-backtrace.rb +40 -0
  78. data/examples/core/xx-fork-cleanup.rb +22 -0
  79. data/examples/core/xx-fork-spin.rb +42 -0
  80. data/examples/core/xx-fork-terminate.rb +27 -0
  81. data/examples/core/xx-forking.rb +24 -0
  82. data/examples/core/xx-move_on.rb +23 -0
  83. data/examples/core/xx-pingpong.rb +18 -0
  84. data/examples/core/xx-queue-async.rb +120 -0
  85. data/examples/core/xx-readpartial.rb +18 -0
  86. data/examples/core/xx-recurrent-timer.rb +12 -0
  87. data/examples/core/xx-resource_delegate.rb +31 -0
  88. data/examples/core/xx-signals.rb +16 -0
  89. data/examples/core/xx-sleep-forever.rb +9 -0
  90. data/examples/core/xx-sleeping.rb +25 -0
  91. data/examples/core/xx-snooze-starve.rb +16 -0
  92. data/examples/core/xx-spin-fork.rb +49 -0
  93. data/examples/core/xx-spin_error_backtrace.rb +33 -0
  94. data/examples/core/xx-state-machine.rb +51 -0
  95. data/examples/core/xx-stop.rb +20 -0
  96. data/examples/core/xx-supervise-process.rb +30 -0
  97. data/examples/core/xx-supervisors.rb +21 -0
  98. data/examples/core/xx-thread-selector-sleep.rb +51 -0
  99. data/examples/core/xx-thread-selector-snooze.rb +46 -0
  100. data/examples/core/xx-thread-sleep.rb +17 -0
  101. data/examples/core/xx-thread-snooze.rb +34 -0
  102. data/examples/core/xx-thread_pool.rb +17 -0
  103. data/examples/core/xx-throttling.rb +18 -0
  104. data/examples/core/xx-timeout.rb +10 -0
  105. data/examples/core/xx-timer-gc.rb +17 -0
  106. data/examples/core/xx-trace.rb +79 -0
  107. data/examples/core/xx-using-a-mutex.rb +21 -0
  108. data/examples/core/xx-worker-thread.rb +30 -0
  109. data/examples/io/tunnel.rb +48 -0
  110. data/examples/io/xx-backticks.rb +11 -0
  111. data/examples/io/xx-echo_client.rb +25 -0
  112. data/examples/io/xx-echo_client_from_stdin.rb +21 -0
  113. data/examples/io/xx-echo_pipe.rb +16 -0
  114. data/examples/io/xx-echo_server.rb +17 -0
  115. data/examples/io/xx-echo_server_with_timeout.rb +34 -0
  116. data/examples/io/xx-echo_stdin.rb +14 -0
  117. data/examples/io/xx-happy-eyeballs.rb +36 -0
  118. data/examples/io/xx-httparty.rb +38 -0
  119. data/examples/io/xx-irb.rb +17 -0
  120. data/examples/io/xx-net-http.rb +15 -0
  121. data/examples/io/xx-open.rb +16 -0
  122. data/examples/io/xx-switch.rb +15 -0
  123. data/examples/io/xx-system.rb +11 -0
  124. data/examples/io/xx-tcpserver.rb +15 -0
  125. data/examples/io/xx-tcpsocket.rb +18 -0
  126. data/examples/io/xx-zip.rb +19 -0
  127. data/examples/performance/fiber_transfer.rb +47 -0
  128. data/examples/performance/fs_read.rb +38 -0
  129. data/examples/performance/mem-usage.rb +56 -0
  130. data/examples/performance/messaging.rb +29 -0
  131. data/examples/performance/multi_snooze.rb +33 -0
  132. data/examples/performance/snooze.rb +39 -0
  133. data/examples/performance/snooze_raw.rb +39 -0
  134. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +74 -0
  135. data/examples/performance/thread-vs-fiber/polyphony_server.rb +45 -0
  136. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +58 -0
  137. data/examples/performance/thread-vs-fiber/threaded_server.rb +27 -0
  138. data/examples/performance/thread-vs-fiber/xx-httparty_multi.rb +36 -0
  139. data/examples/performance/thread-vs-fiber/xx-httparty_threaded.rb +29 -0
  140. data/examples/performance/thread_pool_perf.rb +63 -0
  141. data/examples/performance/xx-array.rb +11 -0
  142. data/examples/performance/xx-fiber-switch.rb +9 -0
  143. data/examples/performance/xx-snooze.rb +15 -0
  144. data/examples/xx-spin.rb +32 -0
  145. data/ext/libev/Changes +548 -0
  146. data/ext/libev/LICENSE +37 -0
  147. data/ext/libev/README +59 -0
  148. data/ext/libev/README.embed +3 -0
  149. data/ext/libev/ev.c +5279 -0
  150. data/ext/libev/ev.h +856 -0
  151. data/ext/libev/ev_epoll.c +296 -0
  152. data/ext/libev/ev_kqueue.c +224 -0
  153. data/ext/libev/ev_linuxaio.c +642 -0
  154. data/ext/libev/ev_poll.c +156 -0
  155. data/ext/libev/ev_port.c +192 -0
  156. data/ext/libev/ev_select.c +316 -0
  157. data/ext/libev/ev_vars.h +215 -0
  158. data/ext/libev/ev_win32.c +162 -0
  159. data/ext/libev/ev_wrap.h +216 -0
  160. data/ext/libev/test_libev_win32.c +123 -0
  161. data/ext/polyphony/extconf.rb +20 -0
  162. data/ext/polyphony/fiber.c +109 -0
  163. data/ext/polyphony/libev.c +2 -0
  164. data/ext/polyphony/libev.h +9 -0
  165. data/ext/polyphony/libev_agent.c +882 -0
  166. data/ext/polyphony/polyphony.c +71 -0
  167. data/ext/polyphony/polyphony.h +97 -0
  168. data/ext/polyphony/polyphony_ext.c +21 -0
  169. data/ext/polyphony/queue.c +168 -0
  170. data/ext/polyphony/ring_buffer.c +96 -0
  171. data/ext/polyphony/ring_buffer.h +28 -0
  172. data/ext/polyphony/thread.c +208 -0
  173. data/ext/polyphony/tracing.c +11 -0
  174. data/lib/polyphony.rb +136 -0
  175. data/lib/polyphony/adapters/fs.rb +19 -0
  176. data/lib/polyphony/adapters/irb.rb +52 -0
  177. data/lib/polyphony/adapters/postgres.rb +110 -0
  178. data/lib/polyphony/adapters/process.rb +33 -0
  179. data/lib/polyphony/adapters/redis.rb +67 -0
  180. data/lib/polyphony/adapters/trace.rb +138 -0
  181. data/lib/polyphony/core/channel.rb +46 -0
  182. data/lib/polyphony/core/exceptions.rb +36 -0
  183. data/lib/polyphony/core/global_api.rb +124 -0
  184. data/lib/polyphony/core/resource_pool.rb +117 -0
  185. data/lib/polyphony/core/sync.rb +21 -0
  186. data/lib/polyphony/core/thread_pool.rb +64 -0
  187. data/lib/polyphony/core/throttler.rb +41 -0
  188. data/lib/polyphony/event.rb +17 -0
  189. data/lib/polyphony/extensions/core.rb +174 -0
  190. data/lib/polyphony/extensions/fiber.rb +379 -0
  191. data/lib/polyphony/extensions/io.rb +221 -0
  192. data/lib/polyphony/extensions/openssl.rb +81 -0
  193. data/lib/polyphony/extensions/socket.rb +150 -0
  194. data/lib/polyphony/extensions/thread.rb +108 -0
  195. data/lib/polyphony/net.rb +77 -0
  196. data/lib/polyphony/version.rb +5 -0
  197. data/polyphony.gemspec +40 -0
  198. data/test/coverage.rb +54 -0
  199. data/test/eg.rb +27 -0
  200. data/test/helper.rb +56 -0
  201. data/test/q.rb +24 -0
  202. data/test/run.rb +5 -0
  203. data/test/stress.rb +25 -0
  204. data/test/test_agent.rb +130 -0
  205. data/test/test_event.rb +59 -0
  206. data/test/test_ext.rb +196 -0
  207. data/test/test_fiber.rb +988 -0
  208. data/test/test_global_api.rb +352 -0
  209. data/test/test_io.rb +249 -0
  210. data/test/test_kernel.rb +57 -0
  211. data/test/test_process_supervision.rb +46 -0
  212. data/test/test_queue.rb +112 -0
  213. data/test/test_resource_pool.rb +138 -0
  214. data/test/test_signal.rb +100 -0
  215. data/test/test_socket.rb +34 -0
  216. data/test/test_supervise.rb +103 -0
  217. data/test/test_thread.rb +170 -0
  218. data/test/test_thread_pool.rb +101 -0
  219. data/test/test_throttler.rb +50 -0
  220. data/test/test_trace.rb +68 -0
  221. metadata +482 -0
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ class KernelTest < MiniTest::Test
6
+ def test_system_method
7
+ fn = '/tmp/test_system_method'
8
+ FileUtils.rm(fn) rescue nil
9
+
10
+ counter = 0
11
+ timer = spin { throttled_loop(200) { counter += 1 } }
12
+
13
+ system('sleep 0.01')
14
+ assert(counter >= 2)
15
+
16
+ system('echo "hello" > ' + fn)
17
+ assert_equal "hello\n", IO.read(fn)
18
+ ensure
19
+ timer&.stop
20
+ end
21
+
22
+ def patch_open3
23
+ class << Open3
24
+ alias_method :orig_popen2, :popen2
25
+ def popen2(*args)
26
+ raise SystemCallError, 'foo'
27
+ end
28
+ end
29
+ end
30
+
31
+ def unpatch_open3
32
+ class << Open3
33
+ alias_method :popen2, :orig_popen2
34
+ end
35
+ end
36
+
37
+ def test_system_method_with_system_call_error
38
+ patch_open3
39
+ result = system('foo')
40
+ assert_nil result
41
+ ensure
42
+ unpatch_open3
43
+ end
44
+
45
+ def test_backtick_method
46
+ counter = 0
47
+ timer = spin { throttled_loop(200) { counter += 1 } }
48
+
49
+ `sleep 0.01`
50
+ assert(counter >= 2)
51
+
52
+ result = `echo "hello"`
53
+ assert_equal "hello\n", result
54
+ ensure
55
+ timer&.stop
56
+ end
57
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ class ProcessSupervisionTest < MiniTest::Test
6
+ def test_process_supervisor_with_block
7
+ i, o = IO.pipe
8
+
9
+ f = spin do
10
+ Polyphony.watch_process do
11
+ i.close
12
+ sleep 5
13
+ ensure
14
+ o << 'foo'
15
+ o.close
16
+ end
17
+ supervise(on_error: :restart)
18
+ end
19
+
20
+ sleep 0.05
21
+ f.terminate
22
+ f.await
23
+
24
+ o.close
25
+ msg = i.read
26
+ i.close
27
+ assert_equal 'foo', msg
28
+ end
29
+
30
+ def test_process_supervisor_with_cmd
31
+ fn = '/tmp/test_process_supervisor_with_cmd'
32
+ FileUtils.rm(fn) rescue nil
33
+
34
+ f = spin do
35
+ Polyphony.watch_process("echo foo >> #{fn}")
36
+ supervise(on_error: :restart)
37
+ end
38
+
39
+ sleep 0.05
40
+ f.terminate
41
+ f.await
42
+
43
+ assert_equal "foo\n", IO.read(fn)
44
+
45
+ end
46
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ class QueueTest < MiniTest::Test
6
+ def setup
7
+ super
8
+ @queue = Polyphony::Queue.new
9
+ end
10
+
11
+ def test_push_shift
12
+ spin {
13
+ @queue << 42
14
+ }
15
+ v = @queue.shift
16
+ assert_equal 42, v
17
+
18
+ (1..4).each { |i| @queue << i }
19
+ buf = []
20
+ 4.times { buf << @queue.shift }
21
+ assert_equal [1, 2, 3, 4], buf
22
+ end
23
+
24
+ def test_unshift
25
+ @queue.push 1
26
+ @queue.push 2
27
+ @queue.push 3
28
+ @queue.unshift 4
29
+
30
+ buf = []
31
+ buf << @queue.shift while !@queue.empty?
32
+
33
+ assert_equal [4, 1, 2, 3], buf
34
+ end
35
+
36
+ def test_multiple_waiters
37
+ a = spin { @queue.shift }
38
+ b = spin { @queue.shift }
39
+
40
+ @queue << :foo
41
+ @queue << :bar
42
+
43
+ assert_equal [:foo, :bar], Fiber.await(a, b)
44
+ end
45
+
46
+ def test_multi_thread_usage
47
+ t = Thread.new { @queue.push :foo }
48
+ assert_equal :foo, @queue.shift
49
+ end
50
+
51
+ def test_shift_each
52
+ (1..4).each { |i| @queue << i }
53
+ buf = []
54
+ @queue.shift_each { |i| buf << i }
55
+ assert_equal [1, 2, 3, 4], buf
56
+
57
+ buf = []
58
+ @queue.shift_each { |i| buf << i }
59
+ assert_equal [], buf
60
+ end
61
+
62
+ def test_shift_all
63
+ (1..4).each { |i| @queue << i }
64
+ buf = @queue.shift_all
65
+ assert_equal [1, 2, 3, 4], buf
66
+
67
+ buf = @queue.shift_all
68
+ assert_equal [], buf
69
+ end
70
+
71
+ def test_empty?
72
+ assert @queue.empty?
73
+
74
+ @queue << :foo
75
+ assert !@queue.empty?
76
+
77
+ assert_equal :foo, @queue.shift
78
+ assert @queue.empty?
79
+ end
80
+
81
+ def test_fiber_removal_from_queue
82
+ f1 = spin { @queue.shift }
83
+ f2 = spin { @queue.shift }
84
+ f3 = spin { @queue.shift }
85
+
86
+ # let fibers run
87
+ snooze
88
+
89
+ f2.stop
90
+ snooze
91
+
92
+ @queue << :foo
93
+ @queue << :bar
94
+
95
+ assert_equal :foo, f1.await
96
+ assert_nil f2.await
97
+ assert_equal :bar, f3.await
98
+ end
99
+
100
+ def test_fiber_removal_from_queue_simple
101
+ f1 = spin { @queue.shift }
102
+
103
+ # let fibers run
104
+ snooze
105
+
106
+ f1.stop
107
+ snooze
108
+
109
+ @queue << :foo
110
+ assert_nil f1.await
111
+ end
112
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ class ResourcePoolTest < MiniTest::Test
6
+ def test_resource_pool_limit
7
+ resources = [+'a', +'b']
8
+ pool = Polyphony::ResourcePool.new(limit: 2) { resources.shift }
9
+
10
+ assert_equal 2, pool.limit
11
+ assert_equal 0, pool.available
12
+ assert_equal 0, pool.size
13
+
14
+ results = []
15
+ 4.times {
16
+ spin {
17
+ snooze
18
+ pool.acquire { |resource|
19
+ results << resource
20
+ snooze
21
+ }
22
+ }
23
+ }
24
+ 2.times { snooze }
25
+ assert_equal 2, pool.limit
26
+ assert_equal 0, pool.available
27
+ assert_equal 2, pool.size
28
+
29
+ 2.times { snooze }
30
+
31
+ assert_equal ['a', 'b', 'a', 'b'], results
32
+
33
+ 2.times { snooze }
34
+
35
+ assert_equal 2, pool.limit
36
+ assert_equal 2, pool.available
37
+ assert_equal 2, pool.size
38
+ end
39
+
40
+ def test_discard
41
+ resources = [+'a', +'b']
42
+ pool = Polyphony::ResourcePool.new(limit: 2) { resources.shift }
43
+
44
+ results = []
45
+ 4.times {
46
+ spin {
47
+ snooze
48
+ pool.acquire { |resource|
49
+ results << resource
50
+ resource.__discard__ if resource == 'b'
51
+ snooze
52
+ }
53
+ }
54
+ }
55
+ 6.times { snooze }
56
+
57
+ assert_equal ['a', 'b', 'a', 'a'], results
58
+ assert_equal 1, pool.size
59
+ end
60
+
61
+ def test_add
62
+ resources = [+'a', +'b']
63
+ pool = Polyphony::ResourcePool.new(limit: 2) { resources.shift }
64
+
65
+ pool << +'c'
66
+
67
+ results = []
68
+ 4.times {
69
+ spin {
70
+ snooze
71
+ pool.acquire { |resource|
72
+ results << resource
73
+ resource.__discard__ if resource == 'b'
74
+ snooze
75
+ }
76
+ }
77
+ }
78
+ 6.times { snooze }
79
+
80
+ assert_equal ['c', 'a', 'c', 'a'], results
81
+ end
82
+
83
+ def test_single_resource_limit
84
+ resources = [+'a', +'b']
85
+ pool = Polyphony::ResourcePool.new(limit: 1) { resources.shift }
86
+
87
+ results = []
88
+ 10.times {
89
+ spin {
90
+ snooze
91
+ pool.acquire { |resource|
92
+ results << resource
93
+ snooze
94
+ }
95
+ }
96
+ }
97
+ 20.times { snooze }
98
+
99
+ assert_equal ['a'] * 10, results
100
+ end
101
+
102
+ def test_failing_allocator
103
+ pool = Polyphony::ResourcePool.new(limit: 4) { raise }
104
+
105
+ assert_raises { pool.acquire { } }
106
+ end
107
+
108
+ def test_method_delegation
109
+ resources = [+'a', +'b']
110
+ pool = Polyphony::ResourcePool.new(limit: 2) { resources.shift }
111
+
112
+ assert_respond_to pool, :upcase
113
+ assert_equal 'A', pool.upcase
114
+ end
115
+
116
+ def test_preheat
117
+ resources = [+'a', +'b']
118
+ pool = Polyphony::ResourcePool.new(limit: 2) { resources.shift }
119
+
120
+ assert_equal 2, pool.limit
121
+ assert_equal 0, pool.size
122
+
123
+ pool.preheat!
124
+ assert_equal 2, pool.size
125
+ end
126
+
127
+ def test_reentrant_resource_pool
128
+ resources = [+'a', +'b']
129
+ pool = Polyphony::ResourcePool.new(limit: 1) { resources.shift }
130
+
131
+ pool.acquire do |r|
132
+ assert_equal 'a', r
133
+ pool.acquire do |r|
134
+ assert_equal 'a', r
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ class SignalTrapTest < Minitest::Test
6
+ def test_signal_exception_handling
7
+ i, o = IO.pipe
8
+ pid = Polyphony.fork do
9
+ i.close
10
+ spin do
11
+ spin do
12
+ sleep 5
13
+ rescue ::Interrupt => e
14
+ # the signal should be raised only in the main fiber
15
+ o.puts "1-interrupt"
16
+ end.await
17
+ end.await
18
+ rescue ::Interrupt => e
19
+ o.puts "3-interrupt"
20
+ ensure
21
+ o.close
22
+ end
23
+ sleep 0.01
24
+ o.close
25
+ Process.kill('INT', pid)
26
+ Thread.current.agent.waitpid(pid)
27
+ buffer = i.read
28
+ assert_equal "3-interrupt\n", buffer
29
+ end
30
+
31
+ def test_signal_exception_with_cleanup
32
+ i, o = IO.pipe
33
+ pid = Polyphony.fork do
34
+ i.close
35
+ spin do
36
+ spin do
37
+ sleep
38
+ rescue Polyphony::Terminate
39
+ o.puts "1 - terminated"
40
+ end.await
41
+ rescue Polyphony::Terminate
42
+ o.puts "2 - terminated"
43
+ end.await
44
+ rescue Interrupt
45
+ o.puts "3 - interrupted"
46
+ Fiber.current.terminate_all_children
47
+ Fiber.current.await_all_children
48
+ ensure
49
+ o.close
50
+ end
51
+ sleep 0.02
52
+ o.close
53
+ Process.kill('INT', pid)
54
+ Thread.current.agent.waitpid(pid)
55
+ buffer = i.read
56
+ assert_equal "3 - interrupted\n2 - terminated\n1 - terminated\n", buffer
57
+ end
58
+
59
+ def test_signal_exception_possible_race_condition
60
+ i, o = IO.pipe
61
+ pid = Polyphony.fork do
62
+ i.close
63
+ f1 = nil
64
+ f2 = spin do
65
+ # this fiber will try to create a race condition by
66
+ # - being scheduled before f1 is scheduled with the Interrupt exception
67
+ # - scheduling f1 without an exception
68
+ suspend
69
+ f1.schedule
70
+ rescue ::Interrupt => e
71
+ o.puts '2-interrupt'
72
+ raise e
73
+ end
74
+ f1 = spin do
75
+ # this fiber is the one that will be current when the
76
+ # signal is trapped
77
+ sleep 1
78
+ o << 'boom'
79
+ rescue ::Interrupt => e
80
+ o.puts '1-interrupt'
81
+ raise e
82
+ end
83
+ old_trap = trap('INT') do
84
+ f2.schedule
85
+ old_trap.()
86
+ end
87
+ Fiber.current.await_all_children
88
+ rescue ::Interrupt => e
89
+ o.puts '3-interrupt'
90
+ ensure
91
+ o.close
92
+ end
93
+ o.close
94
+ sleep 0.1
95
+ Process.kill('INT', pid)
96
+ Thread.current.agent.waitpid(pid)
97
+ buffer = i.read
98
+ assert_equal "3-interrupt\n", buffer
99
+ end
100
+ end