polyphony 0.19 → 0.20

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 (186) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.rubocop.yml +87 -1
  4. data/CHANGELOG.md +35 -0
  5. data/Gemfile.lock +17 -6
  6. data/README.md +200 -139
  7. data/Rakefile +4 -4
  8. data/TODO.md +35 -7
  9. data/bin/poly +11 -0
  10. data/docs/getting-started/getting-started.md +1 -1
  11. data/docs/summary.md +3 -0
  12. data/docs/technical-overview/exception-handling.md +94 -0
  13. data/docs/technical-overview/fiber-scheduling.md +99 -0
  14. data/examples/core/cancel.rb +8 -4
  15. data/examples/core/channel_echo.rb +18 -17
  16. data/examples/core/defer.rb +12 -0
  17. data/examples/core/enumerator.rb +4 -4
  18. data/examples/core/fiber_error.rb +9 -0
  19. data/examples/core/fiber_error_with_backtrace.rb +73 -0
  20. data/examples/core/fork.rb +6 -6
  21. data/examples/core/genserver.rb +16 -8
  22. data/examples/core/lock.rb +3 -3
  23. data/examples/core/move_on.rb +4 -3
  24. data/examples/core/move_on_twice.rb +5 -5
  25. data/examples/core/move_on_with_ensure.rb +8 -11
  26. data/examples/core/move_on_with_value.rb +14 -0
  27. data/examples/core/{multiple_spawn.rb → multiple_spin.rb} +5 -5
  28. data/examples/core/nested_cancel.rb +5 -5
  29. data/examples/core/{nested_multiple_spawn.rb → nested_multiple_spin.rb} +6 -6
  30. data/examples/core/nested_spin.rb +17 -0
  31. data/examples/core/pingpong.rb +21 -0
  32. data/examples/core/pulse.rb +4 -5
  33. data/examples/core/resource.rb +6 -4
  34. data/examples/core/resource_cancel.rb +6 -9
  35. data/examples/core/resource_delegate.rb +3 -3
  36. data/examples/core/sleep.rb +3 -3
  37. data/examples/core/sleep_spin.rb +19 -0
  38. data/examples/core/snooze.rb +32 -0
  39. data/examples/core/spin.rb +14 -0
  40. data/examples/core/{spawn_cancel.rb → spin_cancel.rb} +6 -7
  41. data/examples/core/spin_error.rb +17 -0
  42. data/examples/core/spin_error_backtrace.rb +30 -0
  43. data/examples/core/spin_uncaught_error.rb +15 -0
  44. data/examples/core/supervisor.rb +8 -8
  45. data/examples/core/supervisor_with_cancel_scope.rb +7 -7
  46. data/examples/core/supervisor_with_error.rb +8 -8
  47. data/examples/core/supervisor_with_manual_move_on.rb +6 -7
  48. data/examples/core/suspend.rb +13 -0
  49. data/examples/core/thread.rb +1 -1
  50. data/examples/core/thread_cancel.rb +9 -11
  51. data/examples/core/thread_pool.rb +18 -14
  52. data/examples/core/throttle.rb +7 -7
  53. data/examples/core/timeout.rb +3 -3
  54. data/examples/fs/read.rb +7 -9
  55. data/examples/http/config.ru +7 -3
  56. data/examples/http/cuba.ru +22 -0
  57. data/examples/http/happy_eyeballs.rb +6 -4
  58. data/examples/http/http_client.rb +1 -1
  59. data/examples/http/http_get.rb +1 -1
  60. data/examples/http/http_parse_experiment.rb +21 -16
  61. data/examples/http/http_proxy.rb +28 -26
  62. data/examples/http/http_server.rb +10 -10
  63. data/examples/http/http_server_forked.rb +6 -5
  64. data/examples/http/http_server_throttled.rb +3 -3
  65. data/examples/http/http_ws_server.rb +11 -11
  66. data/examples/http/https_raw_client.rb +1 -1
  67. data/examples/http/https_server.rb +8 -8
  68. data/examples/http/https_wss_server.rb +13 -11
  69. data/examples/http/rack_server.rb +2 -2
  70. data/examples/http/rack_server_https.rb +4 -4
  71. data/examples/http/rack_server_https_forked.rb +5 -5
  72. data/examples/http/websocket_secure_server.rb +6 -6
  73. data/examples/http/websocket_server.rb +5 -5
  74. data/examples/interfaces/pg_client.rb +4 -4
  75. data/examples/interfaces/pg_pool.rb +13 -6
  76. data/examples/interfaces/pg_transaction.rb +5 -4
  77. data/examples/interfaces/redis_channels.rb +15 -11
  78. data/examples/interfaces/redis_client.rb +2 -2
  79. data/examples/interfaces/redis_pubsub.rb +2 -1
  80. data/examples/interfaces/redis_pubsub_perf.rb +13 -9
  81. data/examples/io/backticks.rb +11 -0
  82. data/examples/io/cat.rb +4 -5
  83. data/examples/io/echo_client.rb +9 -4
  84. data/examples/io/echo_client_from_stdin.rb +20 -0
  85. data/examples/io/echo_pipe.rb +7 -8
  86. data/examples/io/echo_server.rb +8 -6
  87. data/examples/io/echo_server_with_timeout.rb +13 -10
  88. data/examples/io/echo_stdin.rb +3 -3
  89. data/examples/io/httparty.rb +2 -2
  90. data/examples/io/httparty_multi.rb +8 -4
  91. data/examples/io/httparty_threaded.rb +6 -2
  92. data/examples/io/io_read.rb +2 -2
  93. data/examples/io/irb.rb +16 -4
  94. data/examples/io/net-http.rb +3 -3
  95. data/examples/io/open.rb +17 -0
  96. data/examples/io/system.rb +3 -3
  97. data/examples/io/tcpserver.rb +15 -0
  98. data/examples/io/tcpsocket.rb +6 -5
  99. data/examples/performance/multi_snooze.rb +29 -0
  100. data/examples/performance/{perf_snooze.rb → snooze.rb} +7 -5
  101. data/examples/performance/snooze_raw.rb +39 -0
  102. data/ext/gyro/async.c +165 -0
  103. data/ext/gyro/child.c +167 -0
  104. data/ext/{ev → gyro}/extconf.rb +4 -3
  105. data/ext/gyro/gyro.c +316 -0
  106. data/ext/{ev/ev.h → gyro/gyro.h} +12 -7
  107. data/ext/gyro/gyro_ext.c +23 -0
  108. data/ext/{ev → gyro}/io.c +65 -57
  109. data/ext/{ev → gyro}/libev.h +0 -0
  110. data/ext/gyro/signal.c +117 -0
  111. data/ext/{ev → gyro}/socket.c +61 -6
  112. data/ext/gyro/timer.c +199 -0
  113. data/ext/libev/Changes +35 -0
  114. data/ext/libev/README +2 -1
  115. data/ext/libev/ev.c +213 -151
  116. data/ext/libev/ev.h +95 -88
  117. data/ext/libev/ev_epoll.c +26 -15
  118. data/ext/libev/ev_kqueue.c +11 -5
  119. data/ext/libev/ev_linuxaio.c +642 -0
  120. data/ext/libev/ev_poll.c +13 -8
  121. data/ext/libev/ev_port.c +5 -2
  122. data/ext/libev/ev_vars.h +14 -3
  123. data/ext/libev/ev_wrap.h +16 -0
  124. data/lib/ev_ext.bundle +0 -0
  125. data/lib/polyphony.rb +46 -50
  126. data/lib/polyphony/auto_run.rb +12 -0
  127. data/lib/polyphony/core/cancel_scope.rb +11 -7
  128. data/lib/polyphony/core/channel.rb +16 -9
  129. data/lib/polyphony/core/coprocess.rb +101 -51
  130. data/lib/polyphony/core/exceptions.rb +14 -12
  131. data/lib/polyphony/core/resource_pool.rb +21 -8
  132. data/lib/polyphony/core/supervisor.rb +10 -5
  133. data/lib/polyphony/core/sync.rb +7 -6
  134. data/lib/polyphony/core/thread.rb +4 -4
  135. data/lib/polyphony/core/thread_pool.rb +4 -4
  136. data/lib/polyphony/core/throttler.rb +6 -4
  137. data/lib/polyphony/extensions/core.rb +253 -0
  138. data/lib/polyphony/extensions/io.rb +28 -16
  139. data/lib/polyphony/extensions/openssl.rb +2 -1
  140. data/lib/polyphony/extensions/socket.rb +47 -52
  141. data/lib/polyphony/http.rb +4 -3
  142. data/lib/polyphony/http/agent.rb +68 -57
  143. data/lib/polyphony/http/server.rb +5 -5
  144. data/lib/polyphony/http/server/http1.rb +268 -0
  145. data/lib/polyphony/http/server/http2.rb +62 -0
  146. data/lib/polyphony/http/server/http2_stream.rb +104 -0
  147. data/lib/polyphony/http/server/rack.rb +64 -0
  148. data/lib/polyphony/http/server/request.rb +119 -0
  149. data/lib/polyphony/net.rb +26 -15
  150. data/lib/polyphony/postgres.rb +17 -13
  151. data/lib/polyphony/redis.rb +16 -15
  152. data/lib/polyphony/version.rb +1 -1
  153. data/lib/polyphony/websocket.rb +11 -4
  154. data/polyphony.gemspec +13 -9
  155. data/test/eg.rb +27 -0
  156. data/test/helper.rb +25 -0
  157. data/test/run.rb +5 -0
  158. data/test/test_async.rb +33 -0
  159. data/test/test_coprocess.rb +239 -77
  160. data/test/test_core.rb +95 -61
  161. data/test/test_gyro.rb +148 -0
  162. data/test/test_http_server.rb +313 -0
  163. data/test/test_io.rb +79 -27
  164. data/test/test_kernel.rb +22 -12
  165. data/test/test_signal.rb +36 -0
  166. data/test/test_timer.rb +24 -0
  167. metadata +89 -33
  168. data/examples/core/nested_async.rb +0 -17
  169. data/examples/core/next_tick.rb +0 -12
  170. data/examples/core/sleep_spawn.rb +0 -19
  171. data/examples/core/spawn.rb +0 -14
  172. data/examples/core/spawn_error.rb +0 -28
  173. data/examples/performance/perf_multi_snooze.rb +0 -21
  174. data/ext/ev/async.c +0 -168
  175. data/ext/ev/child.c +0 -169
  176. data/ext/ev/ev_ext.c +0 -23
  177. data/ext/ev/ev_module.c +0 -242
  178. data/ext/ev/signal.c +0 -119
  179. data/ext/ev/timer.c +0 -197
  180. data/lib/polyphony/core/fiber_pool.rb +0 -98
  181. data/lib/polyphony/extensions/kernel.rb +0 -169
  182. data/lib/polyphony/http/http1_adapter.rb +0 -254
  183. data/lib/polyphony/http/http2_adapter.rb +0 -157
  184. data/lib/polyphony/http/rack.rb +0 -25
  185. data/lib/polyphony/http/request.rb +0 -66
  186. data/test/test_ev.rb +0 -110
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony'
4
+ require 'polyphony/auto_run'
5
5
 
6
6
  puts "parent pid: #{Process.pid}"
7
7
 
@@ -9,14 +9,14 @@ pid = Polyphony.fork do
9
9
  puts "child pid: #{Process.pid}"
10
10
 
11
11
  spin do
12
- puts "child going to sleep 1..."
12
+ puts 'child going to sleep 1...'
13
13
  sleep 1
14
- puts "child woke up 1"
14
+ puts 'child woke up 1'
15
15
  end
16
16
  end
17
17
 
18
18
  puts "got child pid #{pid}"
19
19
 
20
- puts "waiting for child"
21
- EV::Child.new(pid).await
22
- puts "child is done"
20
+ puts 'parent waiting for child'
21
+ Gyro::Child.new(pid).await
22
+ puts 'parent done waiting'
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony'
4
+ require 'polyphony/auto_run'
5
5
 
6
6
  class GenServer
7
7
  def self.start(receiver, *args)
@@ -33,11 +33,19 @@ class GenServer
33
33
  end
34
34
 
35
35
  def self.cast(process, method, *args)
36
- process << {from: Polyphony::Coprocess.current, method: method, args: args}
36
+ process << {
37
+ from: Polyphony::Coprocess.current,
38
+ method: method,
39
+ args: args
40
+ }
37
41
  end
38
42
 
39
43
  def self.call(process, method, *args)
40
- process << {from: Polyphony::Coprocess.current, method: method, args: args}
44
+ process << {
45
+ from: Polyphony::Coprocess.current,
46
+ method: method,
47
+ args: args
48
+ }
41
49
  receive
42
50
  end
43
51
  end
@@ -57,17 +65,17 @@ module Map
57
65
  end
58
66
  end
59
67
 
60
- map_server = GenServer.start(Map, {foo: :bar})
68
+ map_server = GenServer.start(Map, foo: :bar)
61
69
 
62
- puts "getting value from map server"
70
+ puts 'getting value from map server'
63
71
  v = map_server.get(:foo)
64
72
  puts "value: #{v.inspect}"
65
73
 
66
- puts "putting value in map server"
74
+ puts 'putting value in map server'
67
75
  map_server.put!(:foo, 42)
68
76
 
69
- puts "getting value from map server"
77
+ puts 'getting value from map server'
70
78
  v = map_server.get(:foo)
71
79
  puts "value: #{v.inspect}"
72
80
 
73
- map_server.stop
81
+ map_server.stop
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony'
4
+ require 'polyphony/auto_run'
5
5
 
6
6
  def loop_it(number, lock)
7
7
  loop do
8
- sleep(rand*0.2)
8
+ sleep(rand * 0.2)
9
9
  lock.synchronize do
10
10
  puts "child #{number} has the lock"
11
- sleep(rand*0.05)
11
+ sleep(rand * 0.05)
12
12
  end
13
13
  end
14
14
  end
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony'
4
+ require 'polyphony/auto_run'
5
+ Exception.__disable_sanitized_backtrace__ = true
5
6
 
6
- puts "going to sleep..."
7
+ puts 'going to sleep...'
7
8
  move_on_after(1) do
8
9
  sleep 60
9
10
  end
10
- puts "woke up"
11
+ puts 'woke up'
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony'
4
+ require 'polyphony/auto_run'
5
5
 
6
- puts "going to sleep..."
6
+ puts 'going to sleep...'
7
7
  move_on_after(1) do
8
8
  sleep 60
9
9
  end
10
- puts "woke up"
10
+ puts 'woke up'
11
11
 
12
- puts "going to sleep..."
12
+ puts 'going to sleep...'
13
13
  move_on_after(1) do
14
14
  sleep 60
15
15
  end
16
- puts "woke up"
16
+ puts 'woke up'
@@ -1,16 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony'
4
+ require 'polyphony/auto_run'
5
5
 
6
- puts "going to sleep..."
7
- move_on_after(0.5) do |scope|
8
- begin
9
- sleep 60
10
- ensure
11
- puts "in ensure (is it going to block?)"
12
- # this should not block, since the scope was cancelled
13
- sleep 10 unless scope.cancelled?
14
- end
6
+ puts 'going to sleep...'
7
+ move_on_after(0.5) do
8
+ t0 = Time.now
9
+ sleep(60)
10
+ ensure
11
+ puts "slept for #{Time.now - t0} seconds"
15
12
  end
16
- puts "woke up"
13
+ puts 'woke up'
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/auto_run'
5
+ Exception.__disable_sanitized_backtrace__ = true
6
+
7
+ def blocking_operation
8
+ sleep 60
9
+ :foo
10
+ end
11
+
12
+ puts 'going to sleep...'
13
+ value = move_on_after(1, with_value: :bar) { blocking_operation }
14
+ puts "got value #{value.inspect}"
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony'
4
+ require 'polyphony/auto_run'
5
5
 
6
6
  spin do
7
- puts "1 >"
7
+ puts '1 >'
8
8
  sleep(1)
9
- puts "1 <"
9
+ puts '1 <'
10
10
  end
11
11
 
12
12
  spin do
13
- puts "2 >"
13
+ puts '2 >'
14
14
  sleep(1)
15
- puts "2 <"
15
+ puts '2 <'
16
16
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony'
4
+ require 'polyphony/auto_run'
5
5
 
6
- async def sleep_and_cancel
6
+ def sleep_and_cancel
7
7
  puts "#{Time.now} going to sleep with cancel..."
8
8
  cancel_after(1) do
9
9
  puts "#{Time.now} outer cancel scope"
@@ -21,7 +21,7 @@ ensure
21
21
  puts "#{Time.now} woke up"
22
22
  end
23
23
 
24
- async def sleep_and_move_on
24
+ def sleep_and_move_on
25
25
  puts "#{Time.now} going to sleep with move_on..."
26
26
  move_on_after(1) do
27
27
  puts "#{Time.now} outer cancel scope"
@@ -35,6 +35,6 @@ async def sleep_and_move_on
35
35
  puts "#{Time.now} woke up"
36
36
  end
37
37
 
38
- sleep_and_cancel.await
38
+ sleep_and_cancel
39
39
  puts
40
- sleep_and_move_on.await
40
+ sleep_and_move_on
@@ -1,18 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony'
4
+ require 'polyphony/auto_run'
5
5
 
6
6
  spin do
7
7
  spin do
8
- puts "1 >"
8
+ puts '1 >'
9
9
  sleep(1)
10
- puts "1 <"
10
+ puts '1 <'
11
11
  end
12
12
 
13
13
  spin do
14
- puts "2 >"
14
+ puts '2 >'
15
15
  sleep(1)
16
- puts "2 <"
16
+ puts '2 <'
17
17
  end
18
- end
18
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/auto_run'
5
+
6
+ spin do
7
+ puts 'going to sleep'
8
+ result = spin do
9
+ spin do
10
+ spin do
11
+ puts "Coprocess count: #{Polyphony::Coprocess.list.size}"
12
+ sleep(1)
13
+ end.await
14
+ end.await
15
+ end.await
16
+ puts "result: #{result}"
17
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fiber'
4
+
5
+ ping = Fiber.new do |peer|
6
+ loop do
7
+ puts 'ping'
8
+ sleep 0.3
9
+ peer.transfer Fiber.current
10
+ end
11
+ end
12
+
13
+ pong = Fiber.new do |peer|
14
+ loop do
15
+ puts 'pong'
16
+ sleep 0.3
17
+ peer.transfer Fiber.current
18
+ end
19
+ end
20
+
21
+ ping.resume(pong)
@@ -1,12 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony'
4
+ require 'polyphony/auto_run'
5
5
 
6
6
  move_on_after(3) do
7
+ puts 'Start...'
7
8
  pulser = pulse(1)
8
- while pulser.await
9
- puts Time.now
10
- end
9
+ puts Time.now while pulser.await
11
10
  end
12
- puts "done!"
11
+ puts 'done!'
@@ -1,27 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony'
4
+ require 'polyphony/auto_run'
5
5
 
6
6
  resource_count = 0
7
7
  Pool = Polyphony::ResourcePool.new(limit: 3) do
8
8
  :"resource#{resource_count += 1}"
9
9
  end
10
10
 
11
- async def user(number)
11
+ def user(number)
12
12
  loop do
13
13
  # puts "user #{number} >"
14
14
  Pool.acquire do |r|
15
15
  puts "user #{number} #{r.inspect} >"
16
16
  sleep(0.05 + rand * 0.2)
17
+ puts "user #{number} #{r.inspect} <"
18
+ # raise if rand > 0.9
17
19
  # STDOUT << '.'
18
20
  # puts "#{number}: #{r.inspect}"
19
21
  end
20
22
  end
21
23
  end
22
24
 
23
- 100.times do |x|
24
- coproc user(x)
25
+ 3.times do |x|
26
+ spin { user(x) }
25
27
  end
26
28
 
27
29
  t0 = Time.now
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony'
4
+ require 'polyphony/auto_run'
5
5
 
6
6
  resource_count = 0
7
7
  Pool = Polyphony::ResourcePool.new(limit: 3) do
@@ -10,23 +10,20 @@ end
10
10
 
11
11
  def user(number)
12
12
  loop do
13
- move_on_after(0.2) do |scope|
14
- scope.when_cancelled do
15
- puts "#{number} (cancelled)"
16
- end
17
-
13
+ Polyphony::CancelScope.new(timeout: 0.2) do |scope|
14
+ scope.on_cancel { puts "#{number} (cancelled)" }
18
15
  Pool.acquire do |r|
19
16
  scope.disable
20
17
  puts "#{number} #{r.inspect} >"
21
- sleep(0.4 + rand * 0.2)
18
+ sleep(0.1 + rand * 0.2)
22
19
  puts "#{number} #{r.inspect} <"
23
20
  end
24
21
  end
25
22
  end
26
23
  end
27
24
 
28
- 6.times do |x|
29
- spin { user(x) }
25
+ 10.times do |x|
26
+ spin { user(x + 1) }
30
27
  end
31
28
 
32
29
  t0 = Time.now
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony'
4
+ require 'polyphony/auto_run'
5
5
 
6
6
  class Number
7
7
  def initialize(id)
@@ -19,13 +19,13 @@ Pool = Polyphony::ResourcePool.new(limit: 3) do
19
19
  Number.new(resource_count += 1)
20
20
  end
21
21
 
22
- async def meet(number)
22
+ def meet(number)
23
23
  loop do
24
24
  Pool.greet(number)
25
25
  end
26
26
  end
27
27
 
28
- 3.times { |x| coproc meet(x) }
28
+ 3.times { |x| spin { meet(x) } }
29
29
 
30
30
  t0 = Time.now
31
31
  every(10) { puts "uptime: #{Time.now - t0}" }
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony'
4
+ require 'polyphony/auto_run'
5
5
 
6
- puts "going to sleep..."
6
+ puts 'going to sleep...'
7
7
  sleep 1
8
- puts "woke up"
8
+ puts 'woke up'
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/auto_run'
5
+
6
+ spin do
7
+ 10.times do |i|
8
+ sleep 0.1
9
+ p i
10
+ end
11
+ end
12
+
13
+ spin do
14
+ puts 'going to sleep...'
15
+ sleep 0.8
16
+ puts 'woke up'
17
+ end.await
18
+
19
+ puts 'done'