polyphony 0.46.0 → 0.46.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 890abc2b84ed305f591c697764ea16255059aeb3cd4ddd23195b81f78f5f6daf
4
- data.tar.gz: a02442318f82682ba1fa3a87e2b4e7ad8ae06d365a7d4a4b5c45689b2e55472f
3
+ metadata.gz: 831c85a16a22fe3877044430a4d1cc4a270d18dc73e9ceca8d4826623ab0ddc8
4
+ data.tar.gz: f0432473abb769be2805ad354dddf44465789ebe2d5220f194660e4c6521ec87
5
5
  SHA512:
6
- metadata.gz: 506a2fdbdee9e6c5bb94b976489537792941326080441f3d93924cd9657db5188b29e4ced6e1c8a6f8db06ae211da0522b3ac042e45365be1af2e68f8b157b0e
7
- data.tar.gz: cbc0e333731e035c2094abfa72d425e649c210a8c9d3aec467446f615315cfc6e1f6de664321099ccdf93ddd6e1bb0ba484b2b8b53922725b2df8483aedefeab
6
+ metadata.gz: 9bfecbed04a8052c3a885dfde73975694313801d2a8341260041be84e4910ac459af71766f7df6fa96e5afad66a737b97e36531811bd47cab407ad43b239e073
7
+ data.tar.gz: 2caa40f7193cf1954b69adc5810844ad4e578a4472382602a6ee4204d548e8327344f913bc0aef076030e7deacb911cab1733b281f7a2e8849ffcbd009a00ebc
@@ -1,3 +1,10 @@
1
+ ## 0.46.1
2
+
3
+ * Add `TCPServer#accept_loop`, `OpenSSL::SSL::SSLSocket#accept_loop` method
4
+ * Fix compilation error on MacOS (#43)
5
+ * Fix backtrace for `Timeout.timeout`
6
+ * Add `Backend#timer_loop`
7
+
1
8
  ## 0.46.0
2
9
 
3
10
  * Implement [io_uring backend](https://github.com/digital-fabric/polyphony/pull/44)
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.46.0)
4
+ polyphony (0.46.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/TODO.md CHANGED
@@ -1,16 +1,24 @@
1
- - change fiber_trace method to return nil, change trace logic to use provided
2
- arguments instead of return values for fiber events
3
- - allow backend selection at runtime
4
- - add Backend#timer_loop that does what throttled_loop does, on lower level
5
- - Adapter for io/console (what does `IO#raw` do?)
6
- - Adapter for Pry and IRB (Which fixes #5 and #6)
1
+ ## Roadmap for Polyphony 1.0
2
+
3
+ - Check why worker-thread example doesn't work.
4
+ - Add test that mimics the original design for Monocrono:
5
+ - 256 fibers each waiting for a message
6
+ - When message received do some blocking work using a `ThreadPool`
7
+ - Send messages, collect responses, check for correctness
7
8
  - Improve `#supervise`. It does not work as advertised, and seems to exhibit an
8
9
  inconsistent behaviour (see supervisor example).
9
- - Fix backtrace for `Timeout.timeout` API (see timeout example).
10
- - Check why worker-thread example doesn't work.
11
10
 
12
- 0.47
11
+ - io_uring
12
+ - Use playground.c to find out why we when submitting and waiting for
13
+ completion in single syscall signals seem to be blocked until the syscall
14
+ returns. Is this a bug in io_uring/liburing?
13
15
 
16
+ -----------------------------------------------------
17
+
18
+ - Add `Backend#splice(in, out, nbytes)` API
19
+ - Adapter for io/console (what does `IO#raw` do?)
20
+ - Adapter for Pry and IRB (Which fixes #5 and #6)
21
+ - allow backend selection at runtime
14
22
  - Debugging
15
23
  - Eat your own dogfood: need a good tool to check what's going on when some
16
24
  test fails
@@ -148,6 +156,11 @@
148
156
  - `IO.foreach`
149
157
  - `Process.waitpid`
150
158
 
159
+ ### Quic / HTTP/3
160
+
161
+ - Python impl: https://github.com/aiortc/aioquic/
162
+ - Go impl: https://github.com/lucas-clemente/quic-go
163
+
151
164
  ### DNS client
152
165
 
153
166
  ```ruby
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bash
2
+ set -e
3
+ clear && POLYPHONY_USE_LIBEV=1 rake recompile && ruby test/run.rb
4
+ clear && rake recompile && ruby test/run.rb
@@ -12,6 +12,7 @@ end
12
12
  def run(num_fibers)
13
13
  count = 0
14
14
 
15
+ GC.start
15
16
  GC.disable
16
17
 
17
18
  first = nil
@@ -36,13 +37,21 @@ def run(num_fibers)
36
37
  last.next = first
37
38
 
38
39
  t0 = Time.now
40
+ puts "start transfer..."
39
41
  first.transfer
40
42
  elapsed = Time.now - t0
41
43
 
42
- puts "fibers: #{num_fibers} count: #{count} rate: #{count / elapsed}"
43
- GC.start
44
+ rss = `ps -o rss= -p #{Process.pid}`.to_i
45
+
46
+ puts "fibers: #{num_fibers} rss: #{rss} count: #{count} rate: #{count / elapsed}"
47
+ rescue Exception => e
48
+ puts "Stopped at #{count} fibers"
49
+ p e
44
50
  end
45
51
 
52
+ puts "pid: #{Process.pid}"
46
53
  run(100)
47
- run(1000)
48
- run(10000)
54
+ # run(1000)
55
+ # run(10000)
56
+ # run(100000)
57
+ # run(400000)
@@ -4,41 +4,30 @@ require 'bundler/setup'
4
4
  require 'polyphony'
5
5
  require 'http/parser'
6
6
 
7
- $connection_count = 0
8
-
9
7
  def handle_client(socket)
10
- $connection_count += 1
8
+ pending_requests = []
11
9
  parser = Http::Parser.new
12
- reqs = []
13
- parser.on_message_complete = proc do |env|
14
- reqs << Object.new # parser
15
- end
10
+ parser.on_message_complete = proc { pending_requests << parser }
11
+
16
12
  socket.recv_loop do |data|
17
13
  parser << data
18
- while (req = reqs.shift)
19
- handle_request(socket, req)
20
- req = nil
21
- end
14
+ write_response(socket) while pending_requests.shift
22
15
  end
23
16
  rescue IOError, SystemCallError => e
24
17
  # do nothing
25
18
  ensure
26
- $connection_count -= 1
27
19
  socket&.close
28
20
  end
29
21
 
30
- def handle_request(client, parser)
22
+ def write_response(socket)
31
23
  status_code = "200 OK"
32
24
  data = "Hello world!\n"
33
25
  headers = "Content-Type: text/plain\r\nContent-Length: #{data.bytesize}\r\n"
34
- client.write "HTTP/1.1 #{status_code}\r\n#{headers}\r\n#{data}"
26
+ socket.write "HTTP/1.1 #{status_code}\r\n#{headers}\r\n#{data}"
35
27
  end
36
28
 
37
29
  server = TCPServer.open('0.0.0.0', 1234)
38
30
  puts "pid #{Process.pid}"
39
31
  puts "listening on port 1234"
40
32
 
41
- loop do
42
- client = server.accept
43
- spin { handle_client(client) }
44
- end
33
+ server.accept_loop { |c| handle_client(c) }
@@ -1,3 +1,5 @@
1
+ #include <time.h>
2
+
1
3
  #include "ruby.h"
2
4
  #include "ruby/io.h"
3
5
 
@@ -107,3 +109,12 @@ inline void rectify_io_file_pos(rb_io_t *fptr) {
107
109
  fptr->rbuf.len = 0;
108
110
  }
109
111
  }
112
+
113
+ inline double current_time() {
114
+ struct timespec ts;
115
+ clock_gettime(CLOCK_MONOTONIC, &ts);
116
+ long long ns = ts.tv_sec;
117
+ ns = ns * 1000000000 + ts.tv_nsec;
118
+ double t = ns;
119
+ return t / 1e9;
120
+ }
@@ -7,18 +7,18 @@
7
7
  #include <fcntl.h>
8
8
  #include <netinet/in.h>
9
9
  #include <arpa/inet.h>
10
-
11
- #include "polyphony.h"
12
- #include "../liburing/liburing.h"
13
- #include "ruby/thread.h"
14
- #include "backend_io_uring_context.h"
15
-
10
+ #include <stdnoreturn.h>
16
11
  #include <poll.h>
17
12
  #include <sys/types.h>
18
13
  #include <sys/eventfd.h>
19
14
  #include <sys/wait.h>
20
15
  #include <errno.h>
21
16
 
17
+ #include "polyphony.h"
18
+ #include "../liburing/liburing.h"
19
+ #include "ruby/thread.h"
20
+ #include "backend_io_uring_context.h"
21
+
22
22
  #ifndef __NR_pidfd_open
23
23
  #define __NR_pidfd_open 434 /* System call # on most architectures */
24
24
  #endif
@@ -304,6 +304,7 @@ int io_uring_backend_defer_submit_and_await(
304
304
 
305
305
  if (value_ptr) (*value_ptr) = switchpoint_result;
306
306
  RB_GC_GUARD(switchpoint_result);
307
+ RB_GC_GUARD(ctx->fiber);
307
308
  return ctx->result;
308
309
  }
309
310
 
@@ -773,29 +774,62 @@ VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
773
774
  return self;
774
775
  }
775
776
 
776
- VALUE Backend_sleep(VALUE self, VALUE duration) {
777
- Backend_t *backend;
778
- struct io_uring_sqe *sqe;
777
+ // returns true if completed, 0 otherwise
778
+ int io_uring_backend_submit_timeout_and_await(Backend_t *backend, double duration, VALUE *resume_value) {
779
779
  double duration_integral;
780
- double duration_fraction = modf(NUM2DBL(duration), &duration_integral);
780
+ double duration_fraction = modf(duration, &duration_integral);
781
781
  struct __kernel_timespec ts;
782
782
 
783
- GetBackend(self, backend);
784
- sqe = io_uring_get_sqe(&backend->ring);
783
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
785
784
  ts.tv_sec = duration_integral;
786
785
  ts.tv_nsec = floor(duration_fraction * 1000000000);
787
786
 
788
- VALUE resume_value = Qnil;
789
787
  op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_TIMEOUT);
790
788
  io_uring_prep_timeout(sqe, &ts, 0, 0);
791
789
 
792
- io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
790
+ io_uring_backend_defer_submit_and_await(backend, sqe, ctx, resume_value);
793
791
  OP_CONTEXT_RELEASE(&backend->store, ctx);
792
+ return ctx->completed;
793
+ }
794
+
795
+ VALUE Backend_sleep(VALUE self, VALUE duration) {
796
+ Backend_t *backend;
797
+ GetBackend(self, backend);
798
+
799
+ VALUE resume_value = Qnil;
800
+ io_uring_backend_submit_timeout_and_await(backend, NUM2DBL(duration), &resume_value);
794
801
  RAISE_IF_EXCEPTION(resume_value);
795
802
  RB_GC_GUARD(resume_value);
796
803
  return resume_value;
797
804
  }
798
805
 
806
+ VALUE Backend_timer_loop(VALUE self, VALUE interval) {
807
+ Backend_t *backend;
808
+ double interval_d = NUM2DBL(interval);
809
+ GetBackend(self, backend);
810
+ double next_time = 0.;
811
+
812
+ while (1) {
813
+ double now = current_time();
814
+ if (next_time == 0.) next_time = current_time() + interval_d;
815
+ double sleep_duration = next_time - now;
816
+ if (sleep_duration < 0) sleep_duration = 0;
817
+
818
+ VALUE resume_value = Qnil;
819
+ int completed = io_uring_backend_submit_timeout_and_await(backend, sleep_duration, &resume_value);
820
+ RAISE_IF_EXCEPTION(resume_value);
821
+ if (!completed) return resume_value;
822
+ RB_GC_GUARD(resume_value);
823
+
824
+ rb_yield(Qnil);
825
+
826
+ while (1) {
827
+ next_time += interval_d;
828
+ if (next_time > now) break;
829
+ }
830
+ }
831
+ }
832
+
799
833
  VALUE Backend_waitpid(VALUE self, VALUE pid) {
800
834
  Backend_t *backend;
801
835
  int pid_int = NUM2INT(pid);
@@ -864,6 +898,7 @@ void Init_Backend() {
864
898
  rb_define_method(cBackend, "connect", Backend_connect, 3);
865
899
  rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
866
900
  rb_define_method(cBackend, "sleep", Backend_sleep, 1);
901
+ rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
867
902
  rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
868
903
  rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
869
904
 
@@ -7,6 +7,7 @@
7
7
  #include <fcntl.h>
8
8
  #include <netinet/in.h>
9
9
  #include <arpa/inet.h>
10
+ #include <stdnoreturn.h>
10
11
 
11
12
  #include "polyphony.h"
12
13
  #include "../libev/ev.h"
@@ -688,6 +689,39 @@ VALUE Backend_sleep(VALUE self, VALUE duration) {
688
689
  return switchpoint_result;
689
690
  }
690
691
 
692
+ noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
693
+ Backend_t *backend;
694
+ struct libev_timer watcher;
695
+ double interval_d = NUM2DBL(interval);
696
+
697
+ GetBackend(self, backend);
698
+ watcher.fiber = rb_fiber_current();
699
+
700
+ double next_time = 0.;
701
+
702
+ while (1) {
703
+ double now = current_time();
704
+ if (next_time == 0.) next_time = current_time() + interval_d;
705
+ double sleep_duration = next_time - now;
706
+ if (sleep_duration < 0) sleep_duration = 0;
707
+
708
+ VALUE switchpoint_result = Qnil;
709
+ ev_timer_init(&watcher.timer, Backend_timer_callback, sleep_duration, 0.);
710
+ ev_timer_start(backend->ev_loop, &watcher.timer);
711
+ switchpoint_result = backend_await(backend);
712
+ ev_timer_stop(backend->ev_loop, &watcher.timer);
713
+ RAISE_IF_EXCEPTION(switchpoint_result);
714
+ RB_GC_GUARD(switchpoint_result);
715
+
716
+ rb_yield(Qnil);
717
+
718
+ while (1) {
719
+ next_time += interval_d;
720
+ if (next_time > now) break;
721
+ }
722
+ }
723
+ }
724
+
691
725
  struct libev_child {
692
726
  struct ev_child child;
693
727
  VALUE fiber;
@@ -777,6 +811,7 @@ void Init_Backend() {
777
811
  rb_define_method(cBackend, "send", Backend_write, 2);
778
812
  rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
779
813
  rb_define_method(cBackend, "sleep", Backend_sleep, 1);
814
+ rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
780
815
  rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
781
816
  rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
782
817
 
@@ -21,7 +21,7 @@ VALUE SYM_fiber_terminate;
21
21
 
22
22
  static VALUE Fiber_safe_transfer(int argc, VALUE *argv, VALUE self) {
23
23
  VALUE arg = (argc == 0) ? Qnil : argv[0];
24
- VALUE ret = rb_funcall(self, ID_transfer, 1, arg);
24
+ VALUE ret = FIBER_TRANSFER(self, arg);
25
25
 
26
26
  RAISE_IF_EXCEPTION(ret);
27
27
  RB_GC_GUARD(ret);
@@ -42,10 +42,6 @@ inline VALUE Fiber_auto_watcher(VALUE self) {
42
42
  void Fiber_make_runnable(VALUE fiber, VALUE value) {
43
43
  VALUE thread = rb_ivar_get(fiber, ID_ivar_thread);
44
44
  if (thread == Qnil) {
45
- INSPECT("Fiber with no thread", fiber);
46
- TRACE_CALLER();
47
- TRACE_C_STACK();
48
- exit(-1);
49
45
  rb_raise(rb_eRuntimeError, "No thread set for fiber");
50
46
  // rb_warn("No thread set for fiber");
51
47
  return;
@@ -57,7 +53,6 @@ void Fiber_make_runnable(VALUE fiber, VALUE value) {
57
53
  void Fiber_make_runnable_with_priority(VALUE fiber, VALUE value) {
58
54
  VALUE thread = rb_ivar_get(fiber, ID_ivar_thread);
59
55
  if (thread == Qnil) {
60
- INSPECT("Fiber with no thread", fiber);
61
56
  rb_raise(rb_eRuntimeError, "No thread set for fiber");
62
57
  // rb_warn("No thread set for fiber");
63
58
  return;
@@ -23,12 +23,15 @@
23
23
  #define TRACE(...) rb_funcall(rb_cObject, ID_fiber_trace, __VA_ARGS__)
24
24
  #define COND_TRACE(...) if (__tracing_enabled__) { TRACE(__VA_ARGS__); }
25
25
 
26
+ // exceptions
26
27
  #define TEST_EXCEPTION(ret) (RTEST(rb_obj_is_kind_of(ret, rb_eException)))
27
-
28
28
  #define RAISE_EXCEPTION(e) rb_funcall(e, ID_invoke, 0);
29
29
  #define RAISE_IF_EXCEPTION(ret) if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) { RAISE_EXCEPTION(ret); }
30
30
  #define RAISE_IF_NOT_NIL(ret) if (ret != Qnil) { RAISE_EXCEPTION(ret); }
31
31
 
32
+ // Fiber#transfer
33
+ #define FIBER_TRANSFER(fiber, value) rb_funcall(fiber, ID_transfer, 1, value)
34
+
32
35
  extern backend_interface_t backend_interface;
33
36
  #define __BACKEND__ (backend_interface)
34
37
 
@@ -4,7 +4,6 @@ ID ID_deactivate_all_watchers_post_fork;
4
4
  ID ID_ivar_backend;
5
5
  ID ID_ivar_join_wait_queue;
6
6
  ID ID_ivar_main_fiber;
7
- ID ID_ivar_result;
8
7
  ID ID_ivar_terminated;
9
8
  ID ID_ivar_runqueue;
10
9
  ID ID_stop;
@@ -116,7 +115,7 @@ VALUE Thread_switch_fiber(VALUE self) {
116
115
  RB_GC_GUARD(next.fiber);
117
116
  RB_GC_GUARD(next.value);
118
117
  return (next.fiber == current_fiber) ?
119
- next.value : rb_funcall(next.fiber, ID_transfer, 1, next.value);
118
+ next.value : FIBER_TRANSFER(next.fiber, next.value);
120
119
  }
121
120
 
122
121
  VALUE Thread_reset_fiber_scheduling(VALUE self) {
@@ -162,7 +161,6 @@ void Init_Thread() {
162
161
  ID_ivar_backend = rb_intern("@backend");
163
162
  ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
164
163
  ID_ivar_main_fiber = rb_intern("@main_fiber");
165
- ID_ivar_result = rb_intern("@result");
166
164
  ID_ivar_terminated = rb_intern("@terminated");
167
165
  ID_ivar_runqueue = rb_intern("@runqueue");
168
166
  ID_stop = rb_intern("stop");
@@ -14,10 +14,6 @@ module Polyphony
14
14
  @caller_backtrace = caller
15
15
  @value = value
16
16
  end
17
-
18
- def backtrace
19
- sanitize(@caller_backtrace)
20
- end
21
17
  end
22
18
 
23
19
  # MoveOn is used to interrupt a long-running blocking operation, while
@@ -20,15 +20,20 @@ module Polyphony
20
20
  canceller = spin do
21
21
  sleep interval
22
22
  exception = cancel_exception(with_exception)
23
+ # we don't want the cancelling fiber caller location as part of the
24
+ # exception backtrace
25
+ exception.__raising_fiber__ = nil
23
26
  fiber.schedule exception
24
27
  end
25
28
  block ? cancel_after_wrap_block(canceller, &block) : canceller
26
29
  end
27
30
 
28
31
  def cancel_exception(exception)
29
- return exception.new if exception.is_a?(Class)
30
-
31
- RuntimeError.new(exception)
32
+ case exception
33
+ when Class then exception.new
34
+ when Array then exception[0].new(exception[1])
35
+ else RuntimeError.new(exception)
36
+ end
32
37
  end
33
38
 
34
39
  def cancel_after_wrap_block(canceller, &block)
@@ -12,7 +12,7 @@ class ::Exception
12
12
  attr_accessor :__disable_sanitized_backtrace__
13
13
  end
14
14
 
15
- attr_accessor :source_fiber
15
+ attr_accessor :source_fiber, :__raising_fiber__
16
16
 
17
17
  alias_method :orig_initialize, :initialize
18
18
  def initialize(*args)
@@ -22,8 +22,8 @@ class ::Exception
22
22
 
23
23
  alias_method :orig_backtrace, :backtrace
24
24
  def backtrace
25
- unless @first_backtrace_call
26
- @first_backtrace_call = true
25
+ unless @backtrace_called
26
+ @backtrace_called = true
27
27
  return orig_backtrace
28
28
  end
29
29
 
@@ -31,12 +31,10 @@ class ::Exception
31
31
  end
32
32
 
33
33
  def sanitized_backtrace
34
- if @__raising_fiber__
35
- backtrace = orig_backtrace || []
36
- sanitize(backtrace + @__raising_fiber__.caller)
37
- else
38
- sanitize(orig_backtrace)
39
- end
34
+ return sanitize(orig_backtrace) unless @__raising_fiber__
35
+
36
+ backtrace = orig_backtrace || []
37
+ sanitize(backtrace + @__raising_fiber__.caller)
40
38
  end
41
39
 
42
40
  POLYPHONY_DIR = File.expand_path(File.join(__dir__, '..'))
@@ -164,11 +162,7 @@ end
164
162
 
165
163
  # Override Timeout to use cancel scope
166
164
  module ::Timeout
167
- def self.timeout(sec, klass = nil, message = nil, &block)
168
- cancel_after(sec, &block)
169
- rescue Polyphony::Cancel => e
170
- error = klass ? klass.new(message) : ::Timeout::Error.new
171
- error.set_backtrace(e.backtrace)
172
- raise error
165
+ def self.timeout(sec, klass = Timeout::Error, message = 'execution expired', &block)
166
+ cancel_after(sec, with_exception: [klass, message], &block)
173
167
  end
174
168
  end
@@ -36,6 +36,12 @@ class ::OpenSSL::SSL::SSLSocket
36
36
  end
37
37
  end
38
38
 
39
+ def accept_loop
40
+ loop do
41
+ yield accept
42
+ end
43
+ end
44
+
39
45
  alias_method :orig_sysread, :sysread
40
46
  def sysread(maxlen, buf = +'')
41
47
  loop do
@@ -191,6 +191,10 @@ class ::TCPServer
191
191
  @io.accept
192
192
  end
193
193
 
194
+ def accept_loop(&block)
195
+ Thread.current.backend.accept_loop(@io, &block)
196
+ end
197
+
194
198
  alias_method :orig_close, :close
195
199
  def close
196
200
  @io.close
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.46.0'
4
+ VERSION = '0.46.1'
5
5
  end
@@ -85,4 +85,4 @@ module Minitest::Assertions
85
85
  end
86
86
  end
87
87
 
88
- puts "Polyphony backend: #{Thread.current.backend.kind}"
88
+ puts "Polyphony backend: #{Thread.current.backend.kind}"
@@ -2,7 +2,7 @@
2
2
 
3
3
  count = ARGV[0] ? ARGV[0].to_i : 100
4
4
 
5
- TEST_CMD = 'ruby test/test_backend.rb' #'ruby test/run.rb'
5
+ TEST_CMD = 'ruby test/run.rb'
6
6
 
7
7
  def run_test(count)
8
8
  puts "#{count}: running tests..."
@@ -125,4 +125,15 @@ class BackendTest < MiniTest::Test
125
125
  snooze
126
126
  server&.close
127
127
  end
128
+
129
+ def test_timer_loop
130
+ i = 0
131
+ f = spin do
132
+ @backend.timer_loop(0.01) { i += 1 }
133
+ end
134
+ @backend.sleep(0.05)
135
+ f.stop
136
+ f.await # TODO: check why this test sometimes segfaults if we don't a<wait fiber
137
+ assert_in_range 4..6, i
138
+ end
128
139
  end
@@ -202,6 +202,19 @@ class CancelAfterTest < MiniTest::Test
202
202
  end
203
203
  end
204
204
 
205
+ begin
206
+ err = nil
207
+ cancel_after(0.01, with_exception: [CustomException, 'custom message']) do
208
+ sleep 1
209
+ :foo
210
+ end
211
+ rescue Exception => err
212
+ ensure
213
+ assert_kind_of CustomException, err
214
+ assert_equal 'custom message', err.message
215
+ end
216
+
217
+
205
218
  begin
206
219
  e = nil
207
220
  cancel_after(0.01, with_exception: 'foo') do
@@ -129,4 +129,4 @@ class QueueTest < MiniTest::Test
129
129
 
130
130
  assert_equal 0, @queue.size
131
131
  end
132
- end
132
+ end
@@ -40,12 +40,12 @@ class HTTPClientTest < MiniTest::Test
40
40
  def test_http
41
41
  res = HTTParty.get('http://worldtimeapi.org/api/timezone/Europe/Paris')
42
42
  response = JSON.load(res.body)
43
- assert_equal "CEST", response['abbreviation']
43
+ assert_equal "CET", response['abbreviation']
44
44
  end
45
45
 
46
46
  def test_https
47
47
  res = HTTParty.get('https://worldtimeapi.org/api/timezone/Europe/Paris')
48
48
  response = JSON.load(res.body)
49
- assert_equal "CEST", response['abbreviation']
49
+ assert_equal "CET", response['abbreviation']
50
50
  end
51
51
  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.46.0
4
+ version: 0.46.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-08 00:00:00.000000000 Z
11
+ date: 2020-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -290,6 +290,7 @@ files:
290
290
  - TODO.md
291
291
  - bin/polyphony-debug
292
292
  - bin/stress.rb
293
+ - bin/test
293
294
  - docs/_config.yml
294
295
  - docs/_includes/head.html
295
296
  - docs/_includes/title.html