polyphony 0.46.0 → 0.46.1

Sign up to get free protection for your applications and to get access to all the features.
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