polyphony 0.73.1 → 0.77

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +13 -10
  3. data/.github/workflows/test_io_uring.yml +32 -0
  4. data/CHANGELOG.md +22 -0
  5. data/Gemfile.lock +10 -7
  6. data/bin/pdbg +0 -0
  7. data/bin/polyphony-debug +0 -0
  8. data/bin/stress.rb +0 -0
  9. data/bin/test +0 -0
  10. data/examples/core/trap1.rb +21 -0
  11. data/examples/core/trap2.rb +14 -0
  12. data/ext/polyphony/backend_common.c +84 -12
  13. data/ext/polyphony/backend_common.h +8 -0
  14. data/ext/polyphony/backend_io_uring.c +231 -107
  15. data/ext/polyphony/backend_io_uring_context.c +1 -0
  16. data/ext/polyphony/backend_io_uring_context.h +2 -1
  17. data/ext/polyphony/backend_libev.c +12 -9
  18. data/ext/polyphony/event.c +5 -2
  19. data/ext/polyphony/polyphony.c +11 -1
  20. data/ext/polyphony/polyphony.h +4 -1
  21. data/ext/polyphony/queue.c +10 -5
  22. data/ext/polyphony/runqueue_ring_buffer.c +3 -1
  23. data/ext/polyphony/socket_extensions.c +5 -2
  24. data/ext/test_eintr.c +50 -0
  25. data/lib/polyphony/extensions/fiber.rb +85 -5
  26. data/lib/polyphony/extensions/openssl.rb +5 -1
  27. data/lib/polyphony/extensions/socket.rb +12 -6
  28. data/lib/polyphony/extensions/thread.rb +9 -3
  29. data/lib/polyphony/version.rb +1 -1
  30. data/lib/polyphony.rb +4 -1
  31. data/test/helper.rb +2 -6
  32. data/test/stress.rb +1 -1
  33. data/test/test_backend.rb +3 -5
  34. data/test/test_fiber.rb +6 -4
  35. data/test/test_global_api.rb +10 -14
  36. data/test/test_io.rb +2 -2
  37. data/test/test_kernel.rb +2 -2
  38. data/test/test_signal.rb +57 -0
  39. data/test/test_socket.rb +35 -2
  40. data/test/test_thread.rb +1 -1
  41. data/test/test_thread_pool.rb +1 -1
  42. data/test/test_throttler.rb +3 -3
  43. data/test/test_timer.rb +2 -2
  44. data/test/test_trace.rb +7 -1
  45. metadata +11 -7
@@ -17,6 +17,7 @@ const char *op_type_to_str(enum op_type type) {
17
17
  case OP_ACCEPT: return "ACCEPT";
18
18
  case OP_CONNECT: return "CONNECT";
19
19
  case OP_CHAIN: return "CHAIN";
20
+ case OP_CLOSE: return "CLOSE";
20
21
  default: return "";
21
22
  };
22
23
  }
@@ -15,7 +15,8 @@ enum op_type {
15
15
  OP_POLL,
16
16
  OP_ACCEPT,
17
17
  OP_CONNECT,
18
- OP_CHAIN
18
+ OP_CHAIN,
19
+ OP_CLOSE
19
20
  };
20
21
 
21
22
  typedef struct op_context {
@@ -169,9 +169,14 @@ inline VALUE Backend_poll(VALUE self, VALUE blocking) {
169
169
  backend->base.poll_count++;
170
170
 
171
171
  COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_enter, rb_fiber_current());
172
+
173
+ ev_run:
172
174
  backend->base.currently_polling = 1;
175
+ errno = 0;
173
176
  ev_run(backend->ev_loop, blocking == Qtrue ? EVRUN_ONCE : EVRUN_NOWAIT);
174
177
  backend->base.currently_polling = 0;
178
+ if (errno == EINTR && runqueue_empty_p(&backend->base.runqueue)) goto ev_run;
179
+
175
180
  COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_leave, rb_fiber_current());
176
181
 
177
182
  return self;
@@ -697,10 +702,13 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
697
702
  Backend_t *backend;
698
703
  struct libev_io watcher;
699
704
  rb_io_t *fptr;
700
- struct sockaddr_in addr;
701
- char *host_buf = StringValueCStr(host);
705
+ struct sockaddr *ai_addr;
706
+ int ai_addrlen;
702
707
  VALUE switchpoint_result = Qnil;
703
708
  VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
709
+
710
+ ai_addrlen = backend_getaddrinfo(host, port, &ai_addr);
711
+
704
712
  if (underlying_sock != Qnil) sock = underlying_sock;
705
713
 
706
714
  GetBackend(self, backend);
@@ -708,12 +716,8 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
708
716
  io_verify_blocking_mode(fptr, sock, Qfalse);
709
717
  watcher.fiber = Qnil;
710
718
 
711
- addr.sin_family = AF_INET;
712
- addr.sin_addr.s_addr = inet_addr(host_buf);
713
- addr.sin_port = htons(NUM2INT(port));
714
-
715
719
  backend->base.op_count++;
716
- int result = connect(fptr->fd, (struct sockaddr *)&addr, sizeof(addr));
720
+ int result = connect(fptr->fd, ai_addr, ai_addrlen);
717
721
  if (result < 0) {
718
722
  int e = errno;
719
723
  if (e != EINPROGRESS) rb_syserr_fail(e, strerror(e));
@@ -1156,7 +1160,6 @@ noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
1156
1160
  while (1) {
1157
1161
  uint64_t now_ns = current_time_ns();
1158
1162
  if (next_time_ns == 0) next_time_ns = now_ns + interval_ns;
1159
- double sleep_duration = ((double)(next_time_ns - now_ns))/1e9;
1160
1163
 
1161
1164
  if (next_time_ns > now_ns) {
1162
1165
  double sleep_duration = ((double)(next_time_ns - now_ns))/1e9;
@@ -1370,7 +1373,7 @@ inline VALUE Backend_run_idle_tasks(VALUE self) {
1370
1373
  return self;
1371
1374
  }
1372
1375
 
1373
- inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct libev_rw_io *watcher, VALUE *result) {
1376
+ static inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct libev_rw_io *watcher, VALUE *result) {
1374
1377
  char *buf = RSTRING_PTR(str);
1375
1378
  int len = RSTRING_LEN(str);
1376
1379
  int left = len;
@@ -59,14 +59,17 @@ VALUE Event_signal(int argc, VALUE *argv, VALUE self) {
59
59
 
60
60
  VALUE Event_await(VALUE self) {
61
61
  Event_t *event;
62
+ VALUE switchpoint_result;
63
+ VALUE backend;
64
+
62
65
  GetEvent(self, event);
63
66
 
64
67
  if (event->waiting_fiber != Qnil)
65
68
  rb_raise(rb_eRuntimeError, "Event is already awaited by another fiber");
66
69
 
67
- VALUE backend = rb_ivar_get(rb_thread_current(), ID_ivar_backend);
70
+ backend = rb_ivar_get(rb_thread_current(), ID_ivar_backend);
68
71
  event->waiting_fiber = rb_fiber_current();
69
- VALUE switchpoint_result = Backend_wait_event(backend, Qnil);
72
+ switchpoint_result = Backend_wait_event(backend, Qnil);
70
73
  event->waiting_fiber = Qnil;
71
74
 
72
75
  RAISE_IF_EXCEPTION(switchpoint_result);
@@ -9,16 +9,18 @@ ID ID_clear;
9
9
  ID ID_each;
10
10
  ID ID_inspect;
11
11
  ID ID_invoke;
12
- ID ID_new;
13
12
  ID ID_ivar_blocking_mode;
14
13
  ID ID_ivar_io;
15
14
  ID ID_ivar_parked;
16
15
  ID ID_ivar_runnable;
17
16
  ID ID_ivar_running;
18
17
  ID ID_ivar_thread;
18
+ ID ID_new;
19
+ ID ID_raise;
19
20
  ID ID_size;
20
21
  ID ID_signal;
21
22
  ID ID_switch_fiber;
23
+ ID ID_to_s;
22
24
  ID ID_transfer;
23
25
  ID ID_R;
24
26
  ID ID_W;
@@ -123,6 +125,10 @@ VALUE Polyphony_backend_write(int argc, VALUE *argv, VALUE self) {
123
125
  return Backend_write_m(argc, argv, BACKEND());
124
126
  }
125
127
 
128
+ // VALUE Polyphony_backend_close(VALUE self, VALUE io) {
129
+ // return Backend_close(BACKEND(), io);
130
+ // }
131
+
126
132
  void Init_Polyphony() {
127
133
  mPolyphony = rb_define_module("Polyphony");
128
134
 
@@ -147,6 +153,8 @@ void Init_Polyphony() {
147
153
  rb_define_singleton_method(mPolyphony, "backend_wait_io", Polyphony_backend_wait_io, 2);
148
154
  rb_define_singleton_method(mPolyphony, "backend_waitpid", Polyphony_backend_waitpid, 1);
149
155
  rb_define_singleton_method(mPolyphony, "backend_write", Polyphony_backend_write, -1);
156
+ // rb_define_singleton_method(mPolyphony, "backend_close", Polyphony_backend_close, 1);
157
+ rb_define_singleton_method(mPolyphony, "backend_verify_blocking_mode", Backend_verify_blocking_mode, 2);
150
158
 
151
159
  rb_define_global_function("snooze", Polyphony_snooze, 0);
152
160
  rb_define_global_function("suspend", Polyphony_suspend, 0);
@@ -166,8 +174,10 @@ void Init_Polyphony() {
166
174
  ID_ivar_running = rb_intern("@running");
167
175
  ID_ivar_thread = rb_intern("@thread");
168
176
  ID_new = rb_intern("new");
177
+ ID_raise = rb_intern("raise");
169
178
  ID_signal = rb_intern("signal");
170
179
  ID_size = rb_intern("size");
171
180
  ID_switch_fiber = rb_intern("switch_fiber");
181
+ ID_to_s = rb_intern("to_s");
172
182
  ID_transfer = rb_intern("transfer");
173
183
  }
@@ -10,7 +10,8 @@
10
10
  // debugging
11
11
  #define OBJ_ID(obj) (NUM2LONG(rb_funcall(obj, rb_intern("object_id"), 0)))
12
12
  #define INSPECT(str, obj) { printf(str); VALUE s = rb_funcall(obj, rb_intern("inspect"), 0); printf(": %s\n", StringValueCStr(s)); }
13
- #define TRACE_CALLER() { VALUE c = rb_funcall(rb_mKernel, rb_intern("caller"), 0); INSPECT("caller: ", c); }
13
+ #define CALLER() rb_funcall(rb_mKernel, rb_intern("caller"), 0)
14
+ #define TRACE_CALLER() INSPECT("caller: ", CALLER())
14
15
  #define TRACE_C_STACK() { \
15
16
  void *entries[10]; \
16
17
  size_t size = backtrace(entries, 10); \
@@ -57,6 +58,7 @@ extern ID ID_raise;
57
58
  extern ID ID_signal;
58
59
  extern ID ID_size;
59
60
  extern ID ID_switch_fiber;
61
+ extern ID ID_to_s;
60
62
  extern ID ID_transfer;
61
63
 
62
64
  extern VALUE SYM_fiber_create;
@@ -112,6 +114,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise);
112
114
  VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write);
113
115
  VALUE Backend_waitpid(VALUE self, VALUE pid);
114
116
  VALUE Backend_write_m(int argc, VALUE *argv, VALUE self);
117
+ // VALUE Backend_close(VALUE self, VALUE io);
115
118
 
116
119
  VALUE Backend_poll(VALUE self, VALUE blocking);
117
120
  VALUE Backend_wait_event(VALUE self, VALUE raise_on_exception);
@@ -121,24 +121,27 @@ VALUE Queue_unshift(VALUE self, VALUE value) {
121
121
 
122
122
  VALUE Queue_shift(VALUE self) {
123
123
  Queue_t *queue;
124
- GetQueue(self, queue);
125
-
126
124
  VALUE fiber = rb_fiber_current();
127
125
  VALUE thread = rb_thread_current();
128
126
  VALUE backend = rb_ivar_get(thread, ID_ivar_backend);
127
+ VALUE value;
128
+
129
+ GetQueue(self, queue);
129
130
 
130
131
  while (1) {
132
+ VALUE switchpoint_result;
133
+
131
134
  if (queue->values.count) Fiber_make_runnable(fiber, Qnil);
132
135
 
133
136
  ring_buffer_push(&queue->shift_queue, fiber);
134
- VALUE switchpoint_result = Backend_wait_event(backend, Qnil);
137
+ switchpoint_result = Backend_wait_event(backend, Qnil);
135
138
  ring_buffer_delete(&queue->shift_queue, fiber);
136
139
 
137
140
  RAISE_IF_EXCEPTION(switchpoint_result);
138
141
  RB_GC_GUARD(switchpoint_result);
139
142
  if (queue->values.count) break;
140
143
  }
141
- VALUE value = ring_buffer_shift(&queue->values);
144
+ value = ring_buffer_shift(&queue->values);
142
145
  if ((queue->capacity) && (queue->capacity > queue->values.count))
143
146
  queue_schedule_first_blocked_fiber(&queue->push_queue);
144
147
  RB_GC_GUARD(value);
@@ -206,9 +209,11 @@ VALUE Queue_shift_each(VALUE self) {
206
209
 
207
210
  VALUE Queue_shift_all(VALUE self) {
208
211
  Queue_t *queue;
212
+ VALUE result;
213
+
209
214
  GetQueue(self, queue);
210
215
 
211
- VALUE result = ring_buffer_shift_all(&queue->values);
216
+ result = ring_buffer_shift_all(&queue->values);
212
217
  if (queue->capacity) queue_schedule_blocked_fibers_to_capacity(queue);
213
218
  return result;
214
219
  }
@@ -24,9 +24,11 @@ inline void runqueue_ring_buffer_clear(runqueue_ring_buffer *buffer) {
24
24
  static runqueue_entry nil_runqueue_entry = {(Qnil), (Qnil)};
25
25
 
26
26
  inline runqueue_entry runqueue_ring_buffer_shift(runqueue_ring_buffer *buffer) {
27
+ runqueue_entry value;
28
+
27
29
  if (buffer->count == 0) return nil_runqueue_entry;
28
30
 
29
- runqueue_entry value = buffer->entries[buffer->head];
31
+ value = buffer->entries[buffer->head];
30
32
  buffer->head = (buffer->head + 1) % buffer->size;
31
33
  buffer->count--;
32
34
  return value;
@@ -17,10 +17,13 @@ VALUE Socket_double_chevron(VALUE self, VALUE msg) {
17
17
  }
18
18
 
19
19
  void Init_SocketExtensions() {
20
+ VALUE cSocket;
21
+ VALUE cTCPSocket;
22
+
20
23
  rb_require("socket");
21
24
 
22
- VALUE cSocket = rb_const_get(rb_cObject, rb_intern("Socket"));
23
- VALUE cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
25
+ cSocket = rb_const_get(rb_cObject, rb_intern("Socket"));
26
+ cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
24
27
 
25
28
  rb_define_method(cSocket, "send", Socket_send, 2);
26
29
  rb_define_method(cTCPSocket, "send", Socket_send, 2);
data/ext/test_eintr.c ADDED
@@ -0,0 +1,50 @@
1
+ #include <stdio.h>
2
+ #include <unistd.h>
3
+ #include <signal.h>
4
+ #include <poll.h>
5
+ #include "./liburing/liburing.h"
6
+
7
+ void sig_handler(int sig) {
8
+ printf("handle signal %d!\n", sig);
9
+ }
10
+
11
+ int main(int argc, char *argv[])
12
+ {
13
+ int pid = getpid();
14
+ int child_pid = fork();
15
+ if (!child_pid) {
16
+ sleep(1);
17
+ kill(pid, SIGINT);
18
+ sleep(1);
19
+ kill(pid, SIGINT);
20
+ }
21
+ else {
22
+ struct sigaction sa;
23
+
24
+ sa.sa_handler = sig_handler;
25
+ sa.sa_flags = SA_SIGINFO | SA_ONSTACK;//0;
26
+ sigemptyset(&sa.sa_mask);
27
+ sigaction(SIGINT, &sa, NULL);
28
+
29
+ printf("pid: %d\n", pid);
30
+
31
+ struct io_uring ring;
32
+ int ret = io_uring_queue_init(16, &ring, 0);
33
+ printf("io_uring_queue_init: %d\n", ret);
34
+
35
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
36
+ io_uring_prep_poll_add(sqe, STDIN_FILENO, POLLIN);
37
+ ret = io_uring_submit(&ring);
38
+ printf("io_uring_submit: %d\n", ret);
39
+
40
+ struct io_uring_cqe *cqe;
41
+
42
+ wait_cqe:
43
+ ret = io_uring_wait_cqe(&ring, &cqe);
44
+ printf("io_uring_wait_cqe: %d\n", ret);
45
+ if (ret == -EINTR) goto wait_cqe;
46
+
47
+ printf("done\n");
48
+ return 0;
49
+ }
50
+ }
@@ -5,17 +5,40 @@ require_relative '../core/exceptions'
5
5
  module Polyphony
6
6
  # Fiber control API
7
7
  module FiberControl
8
+ # Returns the fiber's monitoring mailbox queue, used for receiving fiber
9
+ # monitoring messages.
10
+ #
11
+ # @return [Polyphony::Queue] Monitoring mailbox queue
8
12
  def monitor_mailbox
9
13
  @monitor_mailbox ||= Polyphony::Queue.new
10
14
  end
11
15
 
16
+ # call-seq:
17
+ # fiber.stop(value = nil) -> fiber
18
+ # Fiber.interrupt(value = nil) -> fiber
19
+ #
20
+ # Stops the fiber by raising a Polyphony::MoveOn exception. The given value
21
+ # will become the fiber's return value.
22
+ #
23
+ # @param value [any] Fiber's eventual return value
24
+ # @return [Fiber] fiber
12
25
  def interrupt(value = nil)
13
26
  return if @running == false
14
27
 
15
28
  schedule Polyphony::MoveOn.new(value)
29
+ self
16
30
  end
17
31
  alias_method :stop, :interrupt
18
32
 
33
+ # call-seq:
34
+ # fiber.reset(value = nil) -> fiber
35
+ # fiber.restart(value = nil) -> fiber
36
+ #
37
+ # Restarts the fiber, with the given value serving as the first value passed
38
+ # to the fiber's block.
39
+ #
40
+ # @param value [any] value passed to fiber block
41
+ # @return [Fiber] restarted fiber
19
42
  def restart(value = nil)
20
43
  raise "Can't restart main fiber" if @main
21
44
 
@@ -31,32 +54,58 @@ module Polyphony
31
54
  end
32
55
  alias_method :reset, :restart
33
56
 
57
+ # Stops a fiber by raising a Polyphony::Cancel exception.
58
+ #
59
+ # @return [Fiber] fiber
34
60
  def cancel
35
61
  return if @running == false
36
62
 
37
63
  schedule Polyphony::Cancel.new
64
+ self
38
65
  end
39
66
 
67
+ # Sets the graceful shutdown flag for the fiber.
68
+ #
69
+ # @param graceful [bool] Whether or not to perform a graceful shutdown
40
70
  def graceful_shutdown=(graceful)
41
71
  @graceful_shutdown = graceful
42
72
  end
43
73
 
74
+ # Returns the graceful shutdown flag for the fiber.
75
+ #
76
+ # @return [bool]
44
77
  def graceful_shutdown?
45
78
  @graceful_shutdown
46
79
  end
47
80
 
81
+ # Terminates the fiber, optionally setting the graceful shutdown flag.
82
+ #
83
+ # @param graceful [bool] Whether to perform a graceful shutdown
84
+ # @return [Fiber]
48
85
  def terminate(graceful = false)
49
86
  return if @running == false
50
87
 
51
88
  @graceful_shutdown = graceful
52
89
  schedule Polyphony::Terminate.new
90
+ self
53
91
  end
54
92
 
93
+ # call-seq:
94
+ # fiber.raise(message) -> fiber
95
+ # fiber.raise(exception_class) -> fiber
96
+ # fiber.raise(exception_class, exception_message) -> fiber
97
+ # fiber.raise(exception) -> fiber
98
+ #
99
+ # Raises an exception in the context of the fiber.
100
+ #
101
+ # @return [Fiber]
55
102
  def raise(*args)
56
103
  error = error_from_raise_args(args)
57
104
  schedule(error)
105
+ self
58
106
  end
59
107
 
108
+ # :no-doc:
60
109
  def error_from_raise_args(args)
61
110
  case (arg = args.shift)
62
111
  when String then RuntimeError.new(arg)
@@ -125,6 +174,9 @@ module Polyphony
125
174
  # return values for all terminated fibers. If any of the awaited fibers
126
175
  # terminates with an uncaught exception, `Fiber.await` will await all the
127
176
  # other fibers to terminate, then reraise the exception.
177
+ #
178
+ # @param *fibers [Array<Fiber>] fibers to wait for
179
+ # @return [Array<any>] return values of given fibers
128
180
  def await(*fibers)
129
181
  return [] if fibers.empty?
130
182
 
@@ -158,6 +210,12 @@ module Polyphony
158
210
  end
159
211
  alias_method :join, :await
160
212
 
213
+ # Waits for at least one of the given fibers to terminate, returning an
214
+ # array containing the first terminated fiber and its return value. If an
215
+ # exception occurs in one of the given fibers, it will be reraised.
216
+ #
217
+ # @param *fibers [Array<Fiber>] Fibers to wait for
218
+ # @return [Array] Array containing the first terminated fiber and its return value
161
219
  def select(*fibers)
162
220
  return nil if fibers.empty?
163
221
 
@@ -190,13 +248,27 @@ module Polyphony
190
248
  # also be scheduled with priority. This method is mainly used trapping
191
249
  # signals (see also the patched `Kernel#trap`)
192
250
  def schedule_priority_oob_fiber(&block)
193
- f = Fiber.new do
251
+ oob_fiber = Fiber.new do
194
252
  Fiber.current.setup_raw
195
- block.call
253
+ result = block.call
196
254
  rescue Exception => e
197
255
  Thread.current.schedule_and_wakeup(Thread.main.main_fiber, e)
256
+ result = e
257
+ ensure
258
+ Thread.backend.trace(:fiber_terminate, Fiber.current, result)
259
+ suspend
198
260
  end
199
- Thread.current.schedule_and_wakeup(f, nil)
261
+ prepare_oob_fiber(oob_fiber, block)
262
+ Thread.backend.trace(:fiber_create, oob_fiber)
263
+ oob_fiber.schedule_with_priority(nil)
264
+ end
265
+
266
+ def prepare_oob_fiber(fiber, block)
267
+ fiber.oob = true
268
+ fiber.tag = :oob
269
+ fiber.thread = Thread.current
270
+ location = block.source_location
271
+ fiber.set_caller(["#{location.join(':')}"])
200
272
  end
201
273
  end
202
274
 
@@ -391,7 +463,7 @@ class ::Fiber
391
463
 
392
464
  extend Polyphony::FiberControlClassMethods
393
465
 
394
- attr_accessor :tag, :thread, :parent
466
+ attr_accessor :tag, :thread, :parent, :oob
395
467
  attr_reader :result
396
468
 
397
469
  def running?
@@ -408,7 +480,11 @@ class ::Fiber
408
480
  alias_method :to_s, :inspect
409
481
 
410
482
  def location
411
- @caller ? @caller[0] : '(root)'
483
+ if @oob
484
+ "#{@caller[0]} (oob)"
485
+ else
486
+ @caller ? @caller[0] : '(root)'
487
+ end
412
488
  end
413
489
 
414
490
  def caller
@@ -420,6 +496,10 @@ class ::Fiber
420
496
  end
421
497
  end
422
498
 
499
+ def set_caller(o)
500
+ @caller = o
501
+ end
502
+
423
503
  def main?
424
504
  @main
425
505
  end
@@ -38,8 +38,10 @@ class ::OpenSSL::SSL::SSLSocket
38
38
 
39
39
  alias_method :orig_sysread, :sysread
40
40
  def sysread(maxlen, buf = +'')
41
+ # ensure socket is non blocking
42
+ Polyphony.backend_verify_blocking_mode(io, false)
41
43
  while true
42
- case (result = read_nonblock(maxlen, buf, exception: false))
44
+ case (result = sysread_nonblock(maxlen, buf, exception: false))
43
45
  when :wait_readable then Polyphony.backend_wait_io(io, false)
44
46
  when :wait_writable then Polyphony.backend_wait_io(io, true)
45
47
  else return result
@@ -49,6 +51,8 @@ class ::OpenSSL::SSL::SSLSocket
49
51
 
50
52
  alias_method :orig_syswrite, :syswrite
51
53
  def syswrite(buf)
54
+ # ensure socket is non blocking
55
+ Polyphony.backend_verify_blocking_mode(io, false)
52
56
  while true
53
57
  case (result = write_nonblock(buf, exception: false))
54
58
  when :wait_readable then Polyphony.backend_wait_io(io, false)
@@ -31,7 +31,7 @@ class ::Socket
31
31
  alias_method :orig_read, :read
32
32
  def read(maxlen = nil, buf = nil, buf_pos = 0)
33
33
  return Polyphony.backend_recv(self, buf, maxlen, buf_pos) if buf
34
- return Polyphony.backend_recv(self, buf || +'', maxlen, 0) if maxlen
34
+ return Polyphony.backend_recv(self, +'', maxlen, 0) if maxlen
35
35
 
36
36
  buf = +''
37
37
  len = buf.bytesize
@@ -120,8 +120,13 @@ class ::TCPSocket
120
120
 
121
121
  attr_reader :io
122
122
 
123
+ def self.open(*args)
124
+ new(*args)
125
+ end
126
+
123
127
  def initialize(remote_host, remote_port, local_host = nil, local_port = nil)
124
- @io = Socket.new Socket::AF_INET, Socket::SOCK_STREAM
128
+ remote_addr = Addrinfo.tcp(remote_host, remote_port)
129
+ @io = Socket.new remote_addr.afamily, Socket::SOCK_STREAM
125
130
  if local_host && local_port
126
131
  addr = Addrinfo.tcp(local_host, local_port)
127
132
  @io.bind(addr)
@@ -167,7 +172,7 @@ class ::TCPSocket
167
172
  alias_method :orig_read, :read
168
173
  def read(maxlen = nil, buf = nil, buf_pos = 0)
169
174
  return Polyphony.backend_recv(self, buf, maxlen, buf_pos) if buf
170
- return Polyphony.backend_recv(self, buf || +'', maxlen, 0) if maxlen
175
+ return Polyphony.backend_recv(self, +'', maxlen, 0) if maxlen
171
176
 
172
177
  buf = +''
173
178
  len = buf.bytesize
@@ -224,8 +229,9 @@ end
224
229
  # Override stock TCPServer code by encapsulating a Socket instance.
225
230
  class ::TCPServer
226
231
  def initialize(hostname = nil, port = 0)
227
- @io = Socket.new Socket::AF_INET, Socket::SOCK_STREAM
228
- @io.bind(Addrinfo.tcp(hostname, port))
232
+ addr = Addrinfo.tcp(hostname, port)
233
+ @io = Socket.new addr.afamily, Socket::SOCK_STREAM
234
+ @io.bind(addr)
229
235
  @io.listen(0)
230
236
  end
231
237
 
@@ -259,7 +265,7 @@ class ::UNIXSocket
259
265
  alias_method :orig_read, :read
260
266
  def read(maxlen = nil, buf = nil, buf_pos = 0)
261
267
  return Polyphony.backend_recv(self, buf, maxlen, buf_pos) if buf
262
- return Polyphony.backend_recv(self, buf || +'', maxlen, 0) if maxlen
268
+ return Polyphony.backend_recv(self, +'', maxlen, 0) if maxlen
263
269
 
264
270
  buf = +''
265
271
  len = buf.bytesize
@@ -18,14 +18,20 @@ class ::Thread
18
18
  def execute
19
19
  # backend must be created in the context of the new thread, therefore it
20
20
  # cannot be created in Thread#initialize
21
- @backend = Polyphony::Backend.new
21
+ raise_error = false
22
+ begin
23
+ @backend = Polyphony::Backend.new
24
+ rescue Exception => e
25
+ raise_error = true
26
+ raise e
27
+ end
22
28
  setup
23
29
  @ready = true
24
30
  result = @block.(*@args)
25
31
  rescue Polyphony::MoveOn, Polyphony::Terminate => e
26
32
  result = e.value
27
33
  rescue Exception => e
28
- result = e
34
+ raise_error ? (raise e) : (result = e)
29
35
  ensure
30
36
  @ready = true
31
37
  finalize(result)
@@ -48,7 +54,7 @@ class ::Thread
48
54
  @result = result
49
55
  signal_waiters(result)
50
56
  end
51
- @backend.finalize
57
+ @backend&.finalize
52
58
  end
53
59
 
54
60
  def signal_waiters(result)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.73.1'
4
+ VERSION = '0.77'
5
5
  end
data/lib/polyphony.rb CHANGED
@@ -2,11 +2,12 @@
2
2
 
3
3
  require 'fiber'
4
4
  require_relative './polyphony_ext'
5
- require_relative './polyphony/extensions'
5
+ require_relative './polyphony/extensions/thread'
6
6
 
7
7
  Thread.current.setup_fiber_scheduling
8
8
  Thread.current.backend = Polyphony::Backend.new
9
9
 
10
+ require_relative './polyphony/extensions'
10
11
  require_relative './polyphony/core/exceptions'
11
12
  require_relative './polyphony/core/global_api'
12
13
  require_relative './polyphony/core/resource_pool'
@@ -40,6 +41,8 @@ module Polyphony
40
41
  run_forked_block(&block)
41
42
  rescue SystemExit
42
43
  # fall through to ensure
44
+ rescue Polyphony::MoveOn
45
+ exit!
43
46
  rescue Exception => e
44
47
  STDERR << e.full_message
45
48
  exit!
data/test/helper.rb CHANGED
@@ -21,10 +21,6 @@ IS_LINUX = RUBY_PLATFORM =~ /linux/
21
21
  # Minitest::Reporters::SpecReporter.new
22
22
  # ]
23
23
 
24
- class ::Fiber
25
- attr_writer :auto_watcher
26
- end
27
-
28
24
  module ::Kernel
29
25
  def trace(*args)
30
26
  STDOUT.orig_write(format_trace(args))
@@ -50,16 +46,16 @@ class MiniTest::Test
50
46
  Thread.current.backend.finalize
51
47
  Thread.current.backend = Polyphony::Backend.new
52
48
  sleep 0.001
49
+ @__stamp = Time.now
53
50
  end
54
51
 
55
52
  def teardown
56
- # trace "* teardown #{self.name}"
53
+ # trace "* teardown #{self.name} (#{Time.now - @__stamp}s)"
57
54
  Fiber.current.shutdown_all_children
58
55
  if Fiber.current.children.size > 0
59
56
  puts "Children left after #{self.name}: #{Fiber.current.children.inspect}"
60
57
  exit!
61
58
  end
62
- Fiber.current.instance_variable_set(:@auto_watcher, nil)
63
59
  rescue => e
64
60
  puts e
65
61
  puts e.backtrace.join("\n")
data/test/stress.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  count = ARGV[0] ? ARGV[0].to_i : 100
4
4
  test_name = ARGV[1]
5
5
 
6
- $test_cmd = +'ruby test/run.rb --name test_receive_cross_thread_exception'
6
+ $test_cmd = +'ruby test/run.rb --name test_cross_thread_send_receive'
7
7
  if test_name
8
8
  $test_cmd << " --name #{test_name}"
9
9
  end