polyphony 0.20 → 0.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.gitbook.yaml +1 -2
  3. data/.rubocop.yml +1 -0
  4. data/CHANGELOG.md +10 -0
  5. data/Gemfile.lock +1 -1
  6. data/README.md +18 -449
  7. data/TODO.md +0 -10
  8. data/docs/README.md +39 -0
  9. data/docs/getting-started/installing.md +28 -0
  10. data/docs/getting-started/tutorial.md +133 -0
  11. data/docs/summary.md +37 -3
  12. data/docs/technical-overview/concurrency.md +47 -0
  13. data/docs/technical-overview/design-principles.md +112 -0
  14. data/docs/technical-overview/exception-handling.md +34 -41
  15. data/docs/technical-overview/extending.md +80 -0
  16. data/docs/technical-overview/faq.md +74 -0
  17. data/docs/technical-overview/fiber-scheduling.md +23 -52
  18. data/docs/user-guide/web-server.md +129 -0
  19. data/examples/core/01-spinning-up-coprocesses.rb +21 -0
  20. data/examples/core/02-awaiting-coprocesses.rb +18 -0
  21. data/examples/core/03-interrupting.rb +34 -0
  22. data/examples/core/04-no-auto-run.rb +18 -0
  23. data/examples/core/mem-usage.rb +34 -0
  24. data/examples/core/spin_error.rb +0 -1
  25. data/examples/core/spin_uncaught_error.rb +0 -1
  26. data/examples/core/wait_for_signal.rb +14 -0
  27. data/examples/http/http_server_graceful.rb +25 -0
  28. data/examples/http/http_server_simple.rb +11 -0
  29. data/examples/interfaces/redis_pubsub_perf.rb +1 -1
  30. data/ext/gyro/async.c +4 -40
  31. data/ext/gyro/child.c +0 -42
  32. data/ext/gyro/io.c +0 -41
  33. data/lib/polyphony/core/coprocess.rb +8 -0
  34. data/lib/polyphony/core/supervisor.rb +29 -10
  35. data/lib/polyphony/extensions/core.rb +1 -1
  36. data/lib/polyphony/http/server/http2.rb +20 -4
  37. data/lib/polyphony/http/server/http2_stream.rb +35 -3
  38. data/lib/polyphony/version.rb +1 -1
  39. data/lib/polyphony.rb +17 -5
  40. data/test/test_async.rb +14 -7
  41. data/test/test_coprocess.rb +42 -12
  42. data/test/test_core.rb +26 -0
  43. data/test/test_io.rb +14 -5
  44. data/test/test_signal.rb +6 -10
  45. metadata +17 -5
  46. data/docs/getting-started/getting-started.md +0 -10
  47. data/examples/core/spin.rb +0 -14
  48. data/examples/core/spin_cancel.rb +0 -17
data/ext/gyro/io.c CHANGED
@@ -10,7 +10,6 @@ struct Gyro_IO {
10
10
  struct ev_io ev_io;
11
11
  int active;
12
12
  int event_mask;
13
- VALUE callback;
14
13
  VALUE fiber;
15
14
  };
16
15
 
@@ -23,9 +22,6 @@ static size_t Gyro_IO_size(const void *ptr);
23
22
 
24
23
  static VALUE Gyro_IO_initialize(VALUE self, VALUE io, VALUE event_mask);
25
24
 
26
- static VALUE Gyro_IO_start(VALUE self);
27
- static VALUE Gyro_IO_stop(VALUE self);
28
-
29
25
  void Gyro_IO_callback(struct ev_loop *ev_loop, struct ev_io *io, int revents);
30
26
 
31
27
  static int Gyro_IO_symbol2event_mask(VALUE sym);
@@ -41,8 +37,6 @@ void Init_Gyro_IO() {
41
37
  rb_define_alloc_func(cGyro_IO, Gyro_IO_allocate);
42
38
 
43
39
  rb_define_method(cGyro_IO, "initialize", Gyro_IO_initialize, 2);
44
- rb_define_method(cGyro_IO, "start", Gyro_IO_start, 0);
45
- rb_define_method(cGyro_IO, "stop", Gyro_IO_stop, 0);
46
40
  rb_define_method(cGyro_IO, "await", Gyro_IO_await, 0);
47
41
 
48
42
  VALUE cIO = rb_const_get(rb_cObject, rb_intern("IO"));
@@ -71,9 +65,6 @@ static VALUE Gyro_IO_allocate(VALUE klass) {
71
65
 
72
66
  static void Gyro_IO_mark(void *ptr) {
73
67
  struct Gyro_IO *io = ptr;
74
- if (io->callback != Qnil) {
75
- rb_gc_mark(io->callback);
76
- }
77
68
  if (io->fiber != Qnil) {
78
69
  rb_gc_mark(io->fiber);
79
70
  }
@@ -101,7 +92,6 @@ static VALUE Gyro_IO_initialize(VALUE self, VALUE io_obj, VALUE event_mask) {
101
92
  GetGyro_IO(self, io);
102
93
 
103
94
  io->event_mask = Gyro_IO_symbol2event_mask(event_mask);
104
- io->callback = Qnil;
105
95
  io->fiber = Qnil;
106
96
  io->active = 0;
107
97
 
@@ -122,42 +112,11 @@ void Gyro_IO_callback(struct ev_loop *ev_loop, struct ev_io *ev_io, int revents)
122
112
  io->fiber = Qnil;
123
113
  SCHEDULE_FIBER(fiber, 0);
124
114
  }
125
- else if (io->callback != Qnil) {
126
- rb_funcall(io->callback, ID_call, 1, INT2NUM(revents));
127
- }
128
115
  else {
129
116
  ev_io_stop(EV_DEFAULT, ev_io);
130
117
  }
131
118
  }
132
119
 
133
- static VALUE Gyro_IO_start(VALUE self) {
134
- struct Gyro_IO *io;
135
- GetGyro_IO(self, io);
136
-
137
- if (rb_block_given_p()) {
138
- io->callback = rb_block_proc();
139
- }
140
-
141
- if (!io->active) {
142
- ev_io_start(EV_DEFAULT, &io->ev_io);
143
- io->active = 1;
144
- }
145
-
146
- return self;
147
- }
148
-
149
- static VALUE Gyro_IO_stop(VALUE self) {
150
- struct Gyro_IO *io;
151
- GetGyro_IO(self, io);
152
-
153
- if (io->active) {
154
- ev_io_stop(EV_DEFAULT, &io->ev_io);
155
- io->active = 0;
156
- }
157
-
158
- return self;
159
- }
160
-
161
120
  VALUE Gyro_IO_await(VALUE self) {
162
121
  struct Gyro_IO *io;
163
122
  VALUE ret;
@@ -100,6 +100,14 @@ class Coprocess
100
100
  @fiber
101
101
  end
102
102
 
103
+ def caller
104
+ @fiber&.__caller__[2..]
105
+ end
106
+
107
+ def location
108
+ caller[0]
109
+ end
110
+
103
111
  # Kernel.await expects the given argument / block to be a callable, so #call
104
112
  # in fact waits for the coprocess to finish
105
113
  def await
@@ -9,12 +9,14 @@ Exceptions = import('./exceptions')
9
9
  class Supervisor
10
10
  def initialize
11
11
  @coprocesses = []
12
+ @pending = []
12
13
  end
13
14
 
14
15
  def await(&block)
15
16
  @supervisor_fiber = Fiber.current
16
17
  block&.(self)
17
18
  suspend
19
+ @coprocesses.map { |cp| cp.result }
18
20
  rescue Exceptions::MoveOn => e
19
21
  e.value
20
22
  ensure
@@ -30,12 +32,21 @@ class Supervisor
30
32
  end
31
33
  end
32
34
 
33
- def spin(proc = nil, &block)
34
- proc = Coprocess.new(&(proc || block)) unless proc.is_a?(Coprocess)
35
- @coprocesses << proc
36
- proc.when_done { task_completed(proc) }
37
- proc.run unless proc.alive?
38
- proc
35
+ def spin(coproc = nil, &block)
36
+ coproc = Coprocess.new(&(coproc || block)) unless coproc.is_a?(Coprocess)
37
+ @coprocesses << coproc
38
+ @pending << coproc
39
+ coproc.when_done { task_completed(coproc) }
40
+ coproc.run unless coproc.alive?
41
+ coproc
42
+ end
43
+
44
+ def add(coproc)
45
+ @coprocesses << coproc
46
+ @pending << coproc
47
+ coproc.when_done { task_completed(coproc) }
48
+ coproc.run unless coproc.alive?
49
+ coproc
39
50
  end
40
51
 
41
52
  def still_running?
@@ -51,15 +62,23 @@ class Supervisor
51
62
 
52
63
  def stop_all_tasks
53
64
  exception = Exceptions::MoveOn.new
54
- @coprocesses.each do |c|
65
+ @pending.each do |c|
55
66
  c.transfer(exception)
56
67
  end
57
68
  end
58
69
 
59
70
  def task_completed(coprocess)
60
- return unless @coprocesses.include?(coprocess)
71
+ return unless @pending.include?(coprocess)
61
72
 
62
- @coprocesses.delete(coprocess)
63
- @supervisor_fiber&.transfer if @coprocesses.empty?
73
+ @pending.delete(coprocess)
74
+ @supervisor_fiber&.transfer if @pending.empty?
64
75
  end
65
76
  end
77
+
78
+ class Coprocess
79
+ def self.await(*coprocs)
80
+ supervise do |s|
81
+ coprocs.each { |cp| s.add cp }
82
+ end
83
+ end
84
+ end
@@ -12,7 +12,7 @@ Throttler = import('../core/throttler')
12
12
  # Fiber extensions
13
13
  class ::Fiber
14
14
  attr_accessor :__calling_fiber__
15
- attr_writer :__caller__
15
+ attr_accessor :__caller__
16
16
  attr_writer :cancelled
17
17
  attr_accessor :coprocess, :scheduled_value
18
18
 
@@ -19,10 +19,16 @@ class Protocol
19
19
  @upgrade_headers = upgrade_headers
20
20
 
21
21
  @interface = ::HTTP2::Server.new
22
- @interface.on(:frame) { |bytes| @conn << bytes }
22
+ @connection_fiber = Fiber.current
23
+ @interface.on(:frame, &method(:send_frame))
24
+ @streams = {}
23
25
  end
24
26
 
25
- # request API
27
+ def send_frame(data)
28
+ @conn << data
29
+ rescue Exception => e
30
+ @connection_fiber.transfer e
31
+ end
26
32
 
27
33
  UPGRADE_MESSAGE = <<~HTTP.gsub("\n", "\r\n")
28
34
  HTTP/1.1 101 Switching Protocols
@@ -34,6 +40,7 @@ class Protocol
34
40
  def upgrade
35
41
  @conn << UPGRADE_MESSAGE
36
42
  settings = @upgrade_headers['HTTP2-Settings']
43
+ Fiber.current.schedule(nil)
37
44
  @interface.upgrade(settings, @upgrade_headers, '')
38
45
  ensure
39
46
  @upgrade_headers = nil
@@ -41,7 +48,7 @@ class Protocol
41
48
 
42
49
  # Iterates over incoming requests
43
50
  def each(&block)
44
- @interface.on(:stream) { |stream| Stream.new(stream, &block) }
51
+ @interface.on(:stream) { |stream| start_stream(stream, &block) }
45
52
  upgrade if @upgrade_headers
46
53
 
47
54
  while (data = @conn.readpartial(8192))
@@ -51,8 +58,17 @@ class Protocol
51
58
  rescue SystemCallError, IOError
52
59
  # ignore
53
60
  ensure
54
- # release references to various objects
61
+ finalize_client_loop
62
+ end
63
+
64
+ def start_stream(stream, &block)
65
+ stream = Stream.new(stream, &block)
66
+ @streams[stream] = true
67
+ end
68
+
69
+ def finalize_client_loop
55
70
  @interface = nil
71
+ @streams.each_key(&:stop)
56
72
  @conn.close
57
73
  end
58
74
 
@@ -5,6 +5,7 @@ export_default :StreamHandler
5
5
  require 'http/2'
6
6
 
7
7
  Request = import './request'
8
+ Exceptions = import '../../core/exceptions'
8
9
 
9
10
  # Manages an HTTP 2 stream
10
11
  class StreamHandler
@@ -12,14 +13,38 @@ class StreamHandler
12
13
 
13
14
  def initialize(stream, &block)
14
15
  @stream = stream
15
- @stream_fiber = Fiber.new(&block)
16
-
17
- # stream callbacks occur on connection fiber
16
+ @calling_fiber = Fiber.current
17
+ @stream_fiber = Fiber.new { |req| handle_request(req, &block) }
18
+
19
+ # Stream callbacks occur on the connection fiber (see HTTP2::Protocol#each).
20
+ # The request handler is run on a separate fiber for each stream, allowing
21
+ # concurrent handling of incoming requests on the same HTTP/2 connection.
22
+ #
23
+ # The different stream adapter APIs suspend the stream fiber, waiting for
24
+ # stream callbacks to be called. The callbacks, in turn, transfer control to
25
+ # the stream fiber, effectively causing the return of the adapter API calls.
26
+ #
27
+ # Note: the request handler is run once headers are received. Reading the
28
+ # request body, if present, is at the discretion of the request handler.
29
+ # This mirrors the behaviour of the HTTP/1 adapter.
18
30
  stream.on(:headers, &method(:on_headers))
19
31
  stream.on(:data, &method(:on_data))
20
32
  stream.on(:half_close, &method(:on_half_close))
21
33
  end
22
34
 
35
+ def handle_request(request, &block)
36
+ error = nil
37
+ block.(request)
38
+ @calling_fiber.transfer
39
+ rescue Exceptions::MoveOn
40
+ # ignore
41
+ rescue Exception => e
42
+ error = e
43
+ ensure
44
+ @done = true
45
+ @calling_fiber.transfer error
46
+ end
47
+
23
48
  def on_headers(headers)
24
49
  @request = Request.new(headers.to_h, self)
25
50
  @stream_fiber.transfer(@request)
@@ -101,4 +126,11 @@ class StreamHandler
101
126
  @stream.headers(headers, end_stream: true)
102
127
  end
103
128
  end
129
+
130
+ def stop
131
+ return if @done
132
+
133
+ @stream.close
134
+ @stream_fiber.schedule(Polyphony::MoveOn.new)
135
+ end
104
136
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.20'
4
+ VERSION = '0.21'
5
5
  end
data/lib/polyphony.rb CHANGED
@@ -32,11 +32,23 @@ module Polyphony
32
32
  )
33
33
 
34
34
  class << self
35
- def trap(sig, ref = false, &callback)
36
- sig = Signal.list[sig.to_s.upcase] if sig.is_a?(Symbol)
37
- watcher = Gyro::Signal.new(sig, &callback)
38
- Gyro.unref unless ref
39
- watcher
35
+ # def trap(sig, ref = false, &callback)
36
+ # sig = Signal.list[sig.to_s.upcase] if sig.is_a?(Symbol)
37
+ # puts "sig = #{sig.inspect}"
38
+ # watcher = Gyro::Signal.new(sig, &callback)
39
+ # # Gyro.unref unless ref
40
+ # watcher
41
+ # end
42
+
43
+ def wait_for_signal(sig)
44
+ fiber = Fiber.current
45
+ Gyro.ref
46
+ trap(sig) do
47
+ trap(sig, :DEFAULT)
48
+ Gyro.unref
49
+ fiber.transfer(sig)
50
+ end
51
+ suspend
40
52
  end
41
53
 
42
54
  def fork(&block)
data/test/test_async.rb CHANGED
@@ -5,10 +5,12 @@ require_relative 'helper'
5
5
  class AsyncTest < MiniTest::Test
6
6
  def test_that_async_watcher_receives_signal_across_threads
7
7
  count = 0
8
- a = Gyro::Async.new do
8
+ a = Gyro::Async.new
9
+ spin {
10
+ a.await
9
11
  count += 1
10
- a.stop
11
- end
12
+ }
13
+ snooze
12
14
  Thread.new do
13
15
  sync_sleep 0.001
14
16
  a.signal!
@@ -19,10 +21,15 @@ class AsyncTest < MiniTest::Test
19
21
 
20
22
  def test_that_async_watcher_coalesces_signals
21
23
  count = 0
22
- a = Gyro::Async.new do
23
- count += 1
24
- Gyro::Timer.new(0.01, 0).start { a.stop }
25
- end
24
+ a = Gyro::Async.new
25
+ coproc = spin {
26
+ loop {
27
+ a.await
28
+ count += 1
29
+ after(0.01) { coproc.stop }
30
+ }
31
+ }
32
+ snooze
26
33
  Thread.new do
27
34
  sync_sleep 0.001
28
35
  3.times { a.signal! }
@@ -229,22 +229,52 @@ class CoprocessTest < MiniTest::Test
229
229
  assert(raised_error)
230
230
  assert_equal('foo', raised_error.message)
231
231
  end
232
- end
233
232
 
234
- def test_exception_propagation_for_orphan_fiber
235
- raised_error = nil
236
- spin do
233
+ def test_exception_propagation_for_orphan_fiber
234
+ raised_error = nil
237
235
  spin do
238
- snooze
239
- raise 'bar'
236
+ spin do
237
+ snooze
238
+ raise 'bar'
239
+ end
240
+ end
241
+ suspend
242
+ rescue Exception => e
243
+ raised_error = e
244
+ ensure
245
+ assert(raised_error)
246
+ assert_equal('bar', raised_error.message)
247
+ end
248
+
249
+ def test_await_multiple_coprocesses
250
+ cp1 = spin { sleep 0.01; :foo }
251
+ cp2 = spin { sleep 0.01; :bar }
252
+ cp3 = spin { sleep 0.01; :baz }
253
+
254
+ result = Polyphony::Coprocess.await(cp1, cp2, cp3)
255
+ assert_equal %i{foo bar baz}, result
256
+ end
257
+
258
+ def test_caller
259
+ location = /^#{__FILE__}:#{__LINE__ + 1}/
260
+ cp = spin do
261
+ sleep 0.01
262
+ end
263
+ snooze
264
+
265
+ caller = cp.caller
266
+ assert caller[0] =~ location
267
+ end
268
+
269
+ def test_location
270
+ location = /^#{__FILE__}:#{__LINE__ + 1}/
271
+ cp = spin do
272
+ sleep 0.01
240
273
  end
274
+ snooze
275
+
276
+ assert cp.location =~ location
241
277
  end
242
- suspend
243
- rescue Exception => e
244
- raised_error = e
245
- ensure
246
- assert(raised_error)
247
- assert_equal('bar', raised_error.message)
248
278
  end
249
279
 
250
280
  class MailboxTest < MiniTest::Test
data/test/test_core.rb CHANGED
@@ -216,3 +216,29 @@ class ExceptionTest < MiniTest::Test
216
216
  assert_kind_of RuntimeError, error
217
217
  end
218
218
  end
219
+
220
+ class MoveOnAfterTest < MiniTest::Test
221
+ def test_move_on_after
222
+ t0 = Time.now
223
+ v = move_on_after(0.01) do
224
+ sleep 1
225
+ :foo
226
+ end
227
+ t1 = Time.now
228
+
229
+ assert t1 - t0 < 0.02
230
+ assert_nil v
231
+ end
232
+
233
+ def test_move_on_after_with_value
234
+ t0 = Time.now
235
+ v = move_on_after(0.01, with_value: :bar) do
236
+ sleep 1
237
+ :foo
238
+ end
239
+ t1 = Time.now
240
+
241
+ assert t1 - t0 < 0.02
242
+ assert_equal :bar, v
243
+ end
244
+ end
data/test/test_io.rb CHANGED
@@ -6,14 +6,23 @@ class GyroIOTest < MiniTest::Test
6
6
  def test_that_reading_works
7
7
  i, o = IO.pipe
8
8
  data = +''
9
- w = Gyro::IO.new(i, :r)
10
- w.start do
9
+ sequence = []
10
+ watcher = Gyro::IO.new(i, :r)
11
+ spin {
12
+ sequence << 1
13
+ watcher.await
14
+ sequence << 2
11
15
  i.read_nonblock(8192, data)
12
- w.stop unless data.empty?
16
+ }
17
+ snooze
18
+ sequence << 3
19
+ defer do
20
+ o << 'hello'
21
+ sequence << 4
13
22
  end
14
- defer { o << 'hello' }
15
23
  suspend
16
- assert_equal('hello', data)
24
+ assert_equal 'hello', data
25
+ assert_equal [1, 3, 4, 2], sequence
17
26
  end
18
27
  end
19
28
 
data/test/test_signal.rb CHANGED
@@ -18,19 +18,15 @@ class SignalTest < MiniTest::Test
18
18
  assert_equal(1, count)
19
19
  end
20
20
 
21
- def test_trap_api
21
+ def test_wait_for_signal_api
22
22
  count = 0
23
- w = Polyphony.trap(:usr1, true) do
23
+ spin do
24
+ Polyphony.wait_for_signal 'SIGHUP'
24
25
  count += 1
25
- w.stop
26
26
  end
27
27
 
28
- assert_kind_of(Gyro::Signal, w)
29
- Thread.new do
30
- sync_sleep 0.001
31
- Process.kill(:USR1, Process.pid)
32
- end
33
- suspend
34
- assert_equal(1, count)
28
+ snooze
29
+ Process.kill(:HUP, Process.pid)
30
+ assert_equal 1, count
35
31
  end
36
32
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: polyphony
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.20'
4
+ version: '0.21'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-11-27 00:00:00.000000000 Z
11
+ date: 2019-12-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: modulation
@@ -213,11 +213,21 @@ files:
213
213
  - Rakefile
214
214
  - TODO.md
215
215
  - bin/poly
216
- - docs/getting-started/getting-started.md
216
+ - docs/README.md
217
+ - docs/getting-started/installing.md
217
218
  - docs/getting-started/tutorial.md
218
219
  - docs/summary.md
220
+ - docs/technical-overview/concurrency.md
221
+ - docs/technical-overview/design-principles.md
219
222
  - docs/technical-overview/exception-handling.md
223
+ - docs/technical-overview/extending.md
224
+ - docs/technical-overview/faq.md
220
225
  - docs/technical-overview/fiber-scheduling.md
226
+ - docs/user-guide/web-server.md
227
+ - examples/core/01-spinning-up-coprocesses.rb
228
+ - examples/core/02-awaiting-coprocesses.rb
229
+ - examples/core/03-interrupting.rb
230
+ - examples/core/04-no-auto-run.rb
221
231
  - examples/core/cancel.rb
222
232
  - examples/core/channel_echo.rb
223
233
  - examples/core/defer.rb
@@ -227,6 +237,7 @@ files:
227
237
  - examples/core/fork.rb
228
238
  - examples/core/genserver.rb
229
239
  - examples/core/lock.rb
240
+ - examples/core/mem-usage.rb
230
241
  - examples/core/move_on.rb
231
242
  - examples/core/move_on_twice.rb
232
243
  - examples/core/move_on_with_ensure.rb
@@ -243,8 +254,6 @@ files:
243
254
  - examples/core/sleep.rb
244
255
  - examples/core/sleep_spin.rb
245
256
  - examples/core/snooze.rb
246
- - examples/core/spin.rb
247
- - examples/core/spin_cancel.rb
248
257
  - examples/core/spin_error.rb
249
258
  - examples/core/spin_error_backtrace.rb
250
259
  - examples/core/spin_uncaught_error.rb
@@ -258,6 +267,7 @@ files:
258
267
  - examples/core/thread_pool.rb
259
268
  - examples/core/throttle.rb
260
269
  - examples/core/timeout.rb
270
+ - examples/core/wait_for_signal.rb
261
271
  - examples/fs/read.rb
262
272
  - examples/http/config.ru
263
273
  - examples/http/cuba.ru
@@ -269,6 +279,8 @@ files:
269
279
  - examples/http/http_server.js
270
280
  - examples/http/http_server.rb
271
281
  - examples/http/http_server_forked.rb
282
+ - examples/http/http_server_graceful.rb
283
+ - examples/http/http_server_simple.rb
272
284
  - examples/http/http_server_throttled.rb
273
285
  - examples/http/http_ws_server.rb
274
286
  - examples/http/https_raw_client.rb
@@ -1,10 +0,0 @@
1
- # Installing
2
-
3
- ## Installing
4
-
5
- ```bash
6
- $ gem install polyphony
7
- ```
8
-
9
- ## Tutorial
10
-
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony/auto_run'
5
-
6
- def my_sleep(t)
7
- puts 'going to sleep...'
8
- sleep t
9
- puts 'woke up'
10
- end
11
-
12
- spin do
13
- spin { my_sleep(1) }.await
14
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony/auto_run'
5
-
6
- spin do
7
- cancel_after(1) do
8
- spin do
9
- puts 'going to sleep...'
10
- sleep(2)
11
- ensure
12
- puts 'woke up'
13
- end.await
14
- end
15
- rescue Polyphony::Cancel => e
16
- puts "got error: #{e}"
17
- end