polyphony 0.47.5.1 → 0.48.0

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: d4b0d95763e1c95248c83f2b649bdafa609e5a3877ea381afa1ce7253b340904
4
- data.tar.gz: c25589fd2a4433d21778281a631b364b5505ce27c9fe3f349d2a357a3dd30f97
3
+ metadata.gz: 3a699d313072b1beba17c900491dd2a4f454bc8faea69eb157634e98cba7ee90
4
+ data.tar.gz: 3d8579b2c927c34876560ce878e960b9bc48a857ce43890dc069b4f0fa5ad75f
5
5
  SHA512:
6
- metadata.gz: 2637f2f6aa6531e6b8bc62035a028caffd027d65668b1a1f6b957c9f89045d148b790003856ece2213ccf882e1eab12068578cce8ba0c8882213f3e8f9a125f3
7
- data.tar.gz: '0009c9eae3b3fc52239d26bc836d7a1b4d2bd24d922c02f9a986fbed5d26f76b618777422efe9909aaff27164cc53de9d7083f9b784bc5534b387e6735dc7be6'
6
+ metadata.gz: d6efca5f42f21fe25ab5e6064675c2806dcad9d86e2e80c908987fcce855dd520ab51c4913e5a9001d3e4a84880a6606d124fa46dedff7c2e437a1ef6e156a82
7
+ data.tar.gz: 73ad6c59d4f7e9f8f0d317f480be1ba33c1a70dcf130a717693c2bb5a9ef3e226bfff3bdb635461b505bd2e9bbec7a2174c6a206bee2a94709ba9739ab087956
@@ -1,3 +1,9 @@
1
+ ## 0.48.0
2
+
3
+ - Implement graceful shutdown
4
+ - Add support for `break` / `StopIteration` in `spin_loop`
5
+ - Fix `IO#gets`, `IO#readpartial`
6
+
1
7
  ## 0.47.5.1
2
8
 
3
9
  - Add missing `Socket#accept_loop` method
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.47.5.1)
4
+ polyphony (0.48.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/TODO.md CHANGED
@@ -1,29 +1,43 @@
1
- - Graceful shutdown again:
1
+ - Override stock `SizedQueue` impl with Queue with capacity
2
+
3
+ - Add support for `break` and `StopIteration` in all loops (with tests)
2
4
 
3
- - Fiber API:
4
- - `Fiber#terminate(graceul)` - with a graceful flag
5
- - `Fiber#terminate_all_children(graceful)` - with a graceful flag
6
- - `Fiber#shutdown_all_children(graceful)` - with a graceful flag
5
+ - Change `IO#gets` to use `String#split` to cut into lines, much faster (see
6
+ examples/performance/line_splitting.rb)
7
+
8
+ - More tight loops
9
+ - `IO#gets_loop`, `Socket#gets_loop`, `OpenSSL::Socket#gets_loop` (medium effort)
10
+ - `Fiber#receive_loop` (very little effort, should be implemented in C)
7
11
 
8
- - Set graceful termination in `@graceful_shutdown`
9
- - Add `Fiber#graceful_shutdown?` method
10
- - Returns `@graceful_shutdown`
11
12
 
12
- And then we have:
13
+ - Add `Backend#splice`, `Backend#splice_loop` for implementing stuff like proxying:
13
14
 
14
15
  ```ruby
15
- spin do
16
- loop { do_some_stuff }
17
- ensure
18
- shutdown_gracefully if Fiber.current.graceful_shutdown?
16
+ def two_way_proxy(socket1, socket2)
17
+ backend = Thread.current.backend
18
+ f1 = spin { backend.splice_loop(socket1, socket2) }
19
+ f2 = spin { backend.splice_loop(socket2, socket1) }
20
+ Fiber.await(f1, f2)
19
21
  end
20
22
  ```
21
23
 
24
+ - Graceful shutdown again:
25
+ - What happens to children when doing a graceful shutdown?
26
+ - What are the implications of passing graceful shutdown flag to children?
27
+ - What about errors while doing a graceful shutdown?
28
+ - What about graceful restarts?
29
+ - Some interesting discussions:
30
+ - https://trio.discourse.group/search?q=graceful%20shutdown
31
+ - https://github.com/python-trio/trio/issues/147
32
+ - https://github.com/python-trio/trio/issues/143
33
+ - https://trio.discourse.group/t/graceful-shutdown/93/2
34
+ - https://250bpm.com/blog:146/
35
+ - https://www.rodrigoaraujo.me/posts/golang-pattern-graceful-shutdown-of-concurrent-events/
36
+ - https://github.com/tj/go-gracefully
22
37
  - `Fiber#finalize_children` should pass graceful shutdown flag to children
23
-
24
- - More tight loops
25
- - IO#gets_loop, Socket#gets_loop, OpenSSL::Socket#gets_loop (medium effort)
26
- - Fiber#receive_loop (very little effort, should be implemented in C)
38
+ - A good use case is an HTTP server that on graceful shutdown:
39
+ - stops listening
40
+ - waits for all ongoing requests to finish, optionally with a timeout
27
41
 
28
42
  ## Roadmap for Polyphony 1.0
29
43
 
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ server1 = TCPServer.open('127.0.0.1', 1234)
7
+ server2 = TCPServer.open('127.0.0.1', 1235)
8
+
9
+ puts "Pid: #{Process.pid}"
10
+ puts 'Proxying port 1234 => port 1235'
11
+
12
+ client1 = client2 = nil
13
+
14
+ f1 = spin {
15
+ client1 = server1.accept
16
+ loop do
17
+ if client2
18
+ Thread.current.backend.splice_loop(client1, client2)
19
+ end
20
+ end
21
+ }
22
+
23
+ f2 = spin {
24
+ client2 = server2.accept
25
+ loop do
26
+ if client1
27
+ Thread.current.backend.splice_loop(client2, client1)
28
+ end
29
+ end
30
+ }
31
+
32
+ Fiber.await(f1, f2)
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "benchmark/ips"
4
+
5
+ def slice
6
+ str = ('*' * 40) + "\n" + ('*' * 40) + "\n" + ('*' * 40) + "\n" + ('*' * 40) + "\n" + ('*' * 40)
7
+ lines = []
8
+ while true
9
+ idx = str.index("\n")
10
+ break unless idx
11
+
12
+ lines << str.slice!(0, idx + 1)
13
+ end
14
+ raise unless lines.size == 4
15
+ raise unless str == ('*' * 40)
16
+ end
17
+
18
+ def split
19
+ str = ('*' * 40) + "\n" + ('*' * 40) + "\n" + ('*' * 40) + "\n" + ('*' * 40) + "\n" + ('*' * 40)
20
+ lines = str.split("\n")
21
+ if str[-1] == "\n"
22
+ str = ''
23
+ else
24
+ str = lines.pop
25
+ end
26
+ raise unless lines.size == 4
27
+ raise unless str == ('*' * 40)
28
+ end
29
+
30
+ Benchmark.ips do |x|
31
+ x.report("slice") { slice }
32
+ x.report("split") { split }
33
+ x.compare!
34
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'benchmark'
4
+
5
+ LIMIT = 1_000_0
6
+
7
+ def do_while
8
+ i = 0
9
+ while true
10
+ i += 1
11
+ break if i == LIMIT
12
+ end
13
+ end
14
+
15
+ def do_loop
16
+ i = 0
17
+ loop do
18
+ i += 1
19
+ break if i == LIMIT
20
+ end
21
+ end
22
+
23
+ GC.disable
24
+ Benchmark.bm do |x|
25
+ x.report('while') do
26
+ LIMIT.times { do_while }
27
+ end
28
+ x.report('loop') do
29
+ LIMIT.times { do_loop }
30
+ end
31
+ end
32
+
@@ -29,6 +29,12 @@ end
29
29
  server = TCPServer.open('0.0.0.0', 1234)
30
30
  puts "pid #{Process.pid} Polyphony (#{Thread.current.backend.kind}) listening on port 1234"
31
31
 
32
+ spin_loop(interval: 10) do
33
+ p Thread.current.fiber_scheduling_stats
34
+ end
35
+
36
+ GC.disable
37
+
32
38
  server.accept_loop do |c|
33
39
  spin { handle_client(c) }
34
40
  end
@@ -70,9 +70,9 @@ inline VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
70
70
 
71
71
  inline VALUE backend_await(Backend_t *backend) {
72
72
  VALUE ret;
73
- backend->ref_count++;
73
+ backend->pending_count++;
74
74
  ret = Thread_switch_fiber(rb_thread_current());
75
- backend->ref_count--;
75
+ backend->pending_count--;
76
76
  RB_GC_GUARD(ret);
77
77
  return ret;
78
78
  }
@@ -30,11 +30,14 @@ static int pidfd_open(pid_t pid, unsigned int flags) {
30
30
  VALUE SYM_io_uring;
31
31
 
32
32
  typedef struct Backend_t {
33
+ // common fields
34
+ unsigned int currently_polling;
35
+ unsigned int pending_count;
36
+ unsigned int poll_no_wait_count;
37
+
38
+ // implementation-specific fields
33
39
  struct io_uring ring;
34
40
  op_context_store_t store;
35
- int waiting_for_cqe;
36
- unsigned int ref_count;
37
- unsigned int run_no_wait_count;
38
41
  unsigned int pending_sqes;
39
42
  unsigned int prepared_limit;
40
43
  int event_fd;
@@ -65,11 +68,11 @@ static VALUE Backend_initialize(VALUE self) {
65
68
  Backend_t *backend;
66
69
  GetBackend(self, backend);
67
70
 
68
- backend->waiting_for_cqe = 0;
69
- backend->ref_count = 0;
70
- backend->run_no_wait_count = 0;
71
+ backend->currently_polling = 0;
72
+ backend->pending_count = 0;
73
+ backend->poll_no_wait_count = 0;
71
74
  backend->pending_sqes = 0;
72
- backend->prepared_limit = 1024;
75
+ backend->prepared_limit = 256;
73
76
 
74
77
  context_store_initialize(&backend->store);
75
78
  io_uring_queue_init(backend->prepared_limit, &backend->ring, 0);
@@ -95,46 +98,19 @@ VALUE Backend_post_fork(VALUE self) {
95
98
  io_uring_queue_exit(&backend->ring);
96
99
  io_uring_queue_init(backend->prepared_limit, &backend->ring, 0);
97
100
  context_store_free(&backend->store);
98
- backend->waiting_for_cqe = 0;
99
- backend->ref_count = 0;
100
- backend->run_no_wait_count = 0;
101
+ backend->currently_polling = 0;
102
+ backend->pending_count = 0;
103
+ backend->poll_no_wait_count = 0;
101
104
  backend->pending_sqes = 0;
102
105
 
103
106
  return self;
104
107
  }
105
108
 
106
- VALUE Backend_ref(VALUE self) {
107
- Backend_t *backend;
108
- GetBackend(self, backend);
109
-
110
- backend->ref_count++;
111
- return self;
112
- }
113
-
114
- VALUE Backend_unref(VALUE self) {
109
+ unsigned int Backend_pending_count(VALUE self) {
115
110
  Backend_t *backend;
116
111
  GetBackend(self, backend);
117
112
 
118
- backend->ref_count--;
119
- return self;
120
- }
121
-
122
- int Backend_ref_count(VALUE self) {
123
- Backend_t *backend;
124
- GetBackend(self, backend);
125
-
126
- return backend->ref_count;
127
- }
128
-
129
- void Backend_reset_ref_count(VALUE self) {
130
- Backend_t *backend;
131
- GetBackend(self, backend);
132
-
133
- backend->ref_count = 0;
134
- }
135
-
136
- VALUE Backend_pending_count(VALUE self) {
137
- return INT2NUM(0);
113
+ return backend->pending_count;
138
114
  }
139
115
 
140
116
  typedef struct poll_context {
@@ -158,7 +134,7 @@ static inline bool cq_ring_needs_flush(struct io_uring *ring) {
158
134
 
159
135
  void io_uring_backend_handle_completion(struct io_uring_cqe *cqe, Backend_t *backend) {
160
136
  op_context_t *ctx = io_uring_cqe_get_data(cqe);
161
- if (ctx == 0) return;
137
+ if (!ctx) return;
162
138
 
163
139
  ctx->result = cqe->res;
164
140
 
@@ -211,9 +187,9 @@ void io_uring_backend_poll(Backend_t *backend) {
211
187
  io_uring_submit(&backend->ring);
212
188
  }
213
189
 
214
- backend->waiting_for_cqe = 1;
190
+ backend->currently_polling = 1;
215
191
  rb_thread_call_without_gvl(io_uring_backend_poll_without_gvl, (void *)&poll_ctx, RUBY_UBF_IO, 0);
216
- backend->waiting_for_cqe = 0;
192
+ backend->currently_polling = 0;
217
193
  if (poll_ctx.result < 0) return;
218
194
 
219
195
  io_uring_backend_handle_completion(poll_ctx.cqe, backend);
@@ -226,14 +202,14 @@ VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue
226
202
  GetBackend(self, backend);
227
203
 
228
204
  if (is_nowait) {
229
- backend->run_no_wait_count++;
230
- if (backend->run_no_wait_count < 10) return self;
205
+ backend->poll_no_wait_count++;
206
+ if (backend->poll_no_wait_count < 10) return self;
231
207
 
232
208
  long runnable_count = Runqueue_len(runqueue);
233
- if (backend->run_no_wait_count < runnable_count) return self;
209
+ if (backend->poll_no_wait_count < runnable_count) return self;
234
210
  }
235
211
 
236
- backend->run_no_wait_count = 0;
212
+ backend->poll_no_wait_count = 0;
237
213
 
238
214
  if (is_nowait && backend->pending_sqes) {
239
215
  backend->pending_sqes = 0;
@@ -252,7 +228,7 @@ VALUE Backend_wakeup(VALUE self) {
252
228
  Backend_t *backend;
253
229
  GetBackend(self, backend);
254
230
 
255
- if (backend->waiting_for_cqe) {
231
+ if (backend->currently_polling) {
256
232
  // Since we're currently blocking while waiting for a completion, we add a
257
233
  // NOP which would cause the io_uring_enter syscall to return
258
234
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
@@ -284,12 +260,12 @@ int io_uring_backend_defer_submit_and_await(
284
260
  VALUE switchpoint_result = Qnil;
285
261
 
286
262
  io_uring_sqe_set_data(sqe, ctx);
287
- io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
263
+ // io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
288
264
  io_uring_backend_defer_submit(backend);
289
265
 
290
- backend->ref_count++;
266
+ backend->pending_count++;
291
267
  switchpoint_result = backend_await(backend);
292
- backend->ref_count--;
268
+ backend->pending_count--;
293
269
 
294
270
  if (!ctx->completed) {
295
271
  ctx->result = -ECANCELED;
@@ -351,7 +327,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
351
327
 
352
328
  if (result < 0)
353
329
  rb_syserr_fail(-result, strerror(-result));
354
- else if (result == 0)
330
+ else if (!result)
355
331
  break; // EOF
356
332
  else {
357
333
  total += result;
@@ -373,7 +349,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
373
349
  io_set_read_length(str, total, shrinkable);
374
350
  io_enc_str(str, fptr);
375
351
 
376
- if (total == 0) return Qnil;
352
+ if (!total) return Qnil;
377
353
 
378
354
  return str;
379
355
  }
@@ -410,7 +386,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io) {
410
386
 
411
387
  if (result < 0)
412
388
  rb_syserr_fail(-result, strerror(-result));
413
- else if (result == 0)
389
+ else if (!result)
414
390
  break; // EOF
415
391
  else {
416
392
  total = result;
@@ -581,7 +557,7 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length) {
581
557
  io_set_read_length(str, total, shrinkable);
582
558
  io_enc_str(str, fptr);
583
559
 
584
- if (total == 0) return Qnil;
560
+ if (!total) return Qnil;
585
561
 
586
562
  return str;
587
563
  }
@@ -618,7 +594,7 @@ VALUE Backend_recv_loop(VALUE self, VALUE io) {
618
594
 
619
595
  if (result < 0)
620
596
  rb_syserr_fail(-result, strerror(-result));
621
- else if (result == 0)
597
+ else if (!result)
622
598
  break; // EOF
623
599
  else {
624
600
  total = result;
@@ -950,10 +926,6 @@ void Init_Backend() {
950
926
  rb_define_method(cBackend, "initialize", Backend_initialize, 0);
951
927
  rb_define_method(cBackend, "finalize", Backend_finalize, 0);
952
928
  rb_define_method(cBackend, "post_fork", Backend_post_fork, 0);
953
- rb_define_method(cBackend, "pending_count", Backend_pending_count, 0);
954
-
955
- rb_define_method(cBackend, "ref", Backend_ref, 0);
956
- rb_define_method(cBackend, "unref", Backend_unref, 0);
957
929
 
958
930
  rb_define_method(cBackend, "poll", Backend_poll, 3);
959
931
  rb_define_method(cBackend, "break", Backend_wakeup, 0);
@@ -977,15 +949,6 @@ void Init_Backend() {
977
949
  rb_define_method(cBackend, "kind", Backend_kind, 0);
978
950
 
979
951
  SYM_io_uring = ID2SYM(rb_intern("io_uring"));
980
-
981
- __BACKEND__.pending_count = Backend_pending_count;
982
- __BACKEND__.poll = Backend_poll;
983
- __BACKEND__.ref = Backend_ref;
984
- __BACKEND__.ref_count = Backend_ref_count;
985
- __BACKEND__.reset_ref_count = Backend_reset_ref_count;
986
- __BACKEND__.unref = Backend_unref;
987
- __BACKEND__.wait_event = Backend_wait_event;
988
- __BACKEND__.wakeup = Backend_wakeup;
989
952
  }
990
953
 
991
954
  #endif // POLYPHONY_BACKEND_LIBURING
@@ -40,11 +40,14 @@ inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
40
40
  }
41
41
 
42
42
  typedef struct Backend_t {
43
+ // common fields
44
+ unsigned int currently_polling;
45
+ unsigned int pending_count;
46
+ unsigned int poll_no_wait_count;
47
+
48
+ // implementation-specific fields
43
49
  struct ev_loop *ev_loop;
44
50
  struct ev_async break_async;
45
- int running;
46
- int ref_count;
47
- int run_no_wait_count;
48
51
  } Backend_t;
49
52
 
50
53
  static size_t Backend_size(const void *ptr) {
@@ -83,9 +86,9 @@ static VALUE Backend_initialize(VALUE self) {
83
86
  ev_async_start(backend->ev_loop, &backend->break_async);
84
87
  ev_unref(backend->ev_loop); // don't count the break_async watcher
85
88
 
86
- backend->running = 0;
87
- backend->ref_count = 0;
88
- backend->run_no_wait_count = 0;
89
+ backend->currently_polling = 0;
90
+ backend->pending_count = 0;
91
+ backend->poll_no_wait_count = 0;
89
92
 
90
93
  return Qnil;
91
94
  }
@@ -116,42 +119,11 @@ VALUE Backend_post_fork(VALUE self) {
116
119
  return self;
117
120
  }
118
121
 
119
- VALUE Backend_ref(VALUE self) {
120
- Backend_t *backend;
121
- GetBackend(self, backend);
122
-
123
- backend->ref_count++;
124
- return self;
125
- }
126
-
127
- VALUE Backend_unref(VALUE self) {
128
- Backend_t *backend;
129
- GetBackend(self, backend);
130
-
131
- backend->ref_count--;
132
- return self;
133
- }
134
-
135
- int Backend_ref_count(VALUE self) {
136
- Backend_t *backend;
137
- GetBackend(self, backend);
138
-
139
- return backend->ref_count;
140
- }
141
-
142
- void Backend_reset_ref_count(VALUE self) {
122
+ unsigned int Backend_pending_count(VALUE self) {
143
123
  Backend_t *backend;
144
124
  GetBackend(self, backend);
145
125
 
146
- backend->ref_count = 0;
147
- }
148
-
149
- VALUE Backend_pending_count(VALUE self) {
150
- int count;
151
- Backend_t *backend;
152
- GetBackend(self, backend);
153
- count = ev_pending_count(backend->ev_loop);
154
- return INT2NUM(count);
126
+ return backend->pending_count;
155
127
  }
156
128
 
157
129
  VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue) {
@@ -160,19 +132,19 @@ VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue
160
132
  GetBackend(self, backend);
161
133
 
162
134
  if (is_nowait) {
163
- backend->run_no_wait_count++;
164
- if (backend->run_no_wait_count < 10) return self;
135
+ backend->poll_no_wait_count++;
136
+ if (backend->poll_no_wait_count < 10) return self;
165
137
 
166
138
  long runnable_count = Runqueue_len(runqueue);
167
- if (backend->run_no_wait_count < runnable_count) return self;
139
+ if (backend->poll_no_wait_count < runnable_count) return self;
168
140
  }
169
141
 
170
- backend->run_no_wait_count = 0;
142
+ backend->poll_no_wait_count = 0;
171
143
 
172
144
  COND_TRACE(2, SYM_fiber_event_poll_enter, current_fiber);
173
- backend->running = 1;
145
+ backend->currently_polling = 1;
174
146
  ev_run(backend->ev_loop, is_nowait ? EVRUN_NOWAIT : EVRUN_ONCE);
175
- backend->running = 0;
147
+ backend->currently_polling = 0;
176
148
  COND_TRACE(2, SYM_fiber_event_poll_leave, current_fiber);
177
149
 
178
150
  return self;
@@ -182,7 +154,7 @@ VALUE Backend_wakeup(VALUE self) {
182
154
  Backend_t *backend;
183
155
  GetBackend(self, backend);
184
156
 
185
- if (backend->running) {
157
+ if (backend->currently_polling) {
186
158
  // Since the loop will run until at least one event has occurred, we signal
187
159
  // the selector's associated async watcher, which will cause the ev loop to
188
160
  // return. In contrast to using `ev_break` to break out of the loop, which
@@ -854,10 +826,6 @@ void Init_Backend() {
854
826
  rb_define_method(cBackend, "initialize", Backend_initialize, 0);
855
827
  rb_define_method(cBackend, "finalize", Backend_finalize, 0);
856
828
  rb_define_method(cBackend, "post_fork", Backend_post_fork, 0);
857
- rb_define_method(cBackend, "pending_count", Backend_pending_count, 0);
858
-
859
- rb_define_method(cBackend, "ref", Backend_ref, 0);
860
- rb_define_method(cBackend, "unref", Backend_unref, 0);
861
829
 
862
830
  rb_define_method(cBackend, "poll", Backend_poll, 3);
863
831
  rb_define_method(cBackend, "break", Backend_wakeup, 0);
@@ -882,15 +850,6 @@ void Init_Backend() {
882
850
 
883
851
  ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
884
852
  SYM_libev = ID2SYM(rb_intern("libev"));
885
-
886
- __BACKEND__.pending_count = Backend_pending_count;
887
- __BACKEND__.poll = Backend_poll;
888
- __BACKEND__.ref = Backend_ref;
889
- __BACKEND__.ref_count = Backend_ref_count;
890
- __BACKEND__.reset_ref_count = Backend_reset_ref_count;
891
- __BACKEND__.unref = Backend_unref;
892
- __BACKEND__.wait_event = Backend_wait_event;
893
- __BACKEND__.wakeup = Backend_wakeup;
894
853
  }
895
854
 
896
855
  #endif // POLYPHONY_BACKEND_LIBEV
@@ -66,7 +66,7 @@ VALUE Event_await(VALUE self) {
66
66
 
67
67
  VALUE backend = rb_ivar_get(rb_thread_current(), ID_ivar_backend);
68
68
  event->waiting_fiber = rb_fiber_current();
69
- VALUE switchpoint_result = __BACKEND__.wait_event(backend, Qnil);
69
+ VALUE switchpoint_result = Backend_wait_event(backend, Qnil);
70
70
  event->waiting_fiber = Qnil;
71
71
 
72
72
  RAISE_IF_EXCEPTION(switchpoint_result);
@@ -22,8 +22,6 @@ ID ID_R;
22
22
  ID ID_W;
23
23
  ID ID_RW;
24
24
 
25
- backend_interface_t backend_interface;
26
-
27
25
  VALUE Polyphony_snooze(VALUE self) {
28
26
  VALUE ret;
29
27
  VALUE fiber = rb_fiber_current();
@@ -4,7 +4,6 @@
4
4
  #include <execinfo.h>
5
5
 
6
6
  #include "ruby.h"
7
- #include "backend.h"
8
7
  #include "runqueue_ring_buffer.h"
9
8
 
10
9
  // debugging
@@ -32,9 +31,6 @@
32
31
  // Fiber#transfer
33
32
  #define FIBER_TRANSFER(fiber, value) rb_funcall(fiber, ID_transfer, 1, value)
34
33
 
35
- extern backend_interface_t backend_interface;
36
- #define __BACKEND__ (backend_interface)
37
-
38
34
  extern VALUE mPolyphony;
39
35
  extern VALUE cQueue;
40
36
  extern VALUE cEvent;
@@ -92,6 +88,11 @@ void Runqueue_clear(VALUE self);
92
88
  long Runqueue_len(VALUE self);
93
89
  int Runqueue_empty_p(VALUE self);
94
90
 
91
+ unsigned int Backend_pending_count(VALUE self);
92
+ VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue);
93
+ VALUE Backend_wait_event(VALUE self, VALUE raise_on_exception);
94
+ VALUE Backend_wakeup(VALUE self);
95
+
95
96
  VALUE Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
96
97
  VALUE Thread_schedule_fiber_with_priority(VALUE thread, VALUE fiber, VALUE value);
97
98
  VALUE Thread_switch_fiber(VALUE thread);
@@ -86,7 +86,7 @@ inline void capped_queue_block_push(Queue_t *queue) {
86
86
  if (queue->capacity > queue->values.count) Fiber_make_runnable(fiber, Qnil);
87
87
 
88
88
  ring_buffer_push(&queue->push_queue, fiber);
89
- switchpoint_result = __BACKEND__.wait_event(backend, Qnil);
89
+ switchpoint_result = Backend_wait_event(backend, Qnil);
90
90
  ring_buffer_delete(&queue->push_queue, fiber);
91
91
 
92
92
  RAISE_IF_EXCEPTION(switchpoint_result);
@@ -131,7 +131,7 @@ VALUE Queue_shift(VALUE self) {
131
131
  if (queue->values.count) Fiber_make_runnable(fiber, Qnil);
132
132
 
133
133
  ring_buffer_push(&queue->shift_queue, fiber);
134
- VALUE switchpoint_result = __BACKEND__.wait_event(backend, Qnil);
134
+ VALUE switchpoint_result = Backend_wait_event(backend, Qnil);
135
135
  ring_buffer_delete(&queue->shift_queue, fiber);
136
136
 
137
137
  RAISE_IF_EXCEPTION(switchpoint_result);
@@ -17,16 +17,6 @@ static VALUE Thread_setup_fiber_scheduling(VALUE self) {
17
17
  return self;
18
18
  }
19
19
 
20
- int Thread_fiber_ref_count(VALUE self) {
21
- VALUE backend = rb_ivar_get(self, ID_ivar_backend);
22
- return NUM2INT(__BACKEND__.ref_count(backend));
23
- }
24
-
25
- inline void Thread_fiber_reset_ref_count(VALUE self) {
26
- VALUE backend = rb_ivar_get(self, ID_ivar_backend);
27
- __BACKEND__.reset_ref_count(backend);
28
- }
29
-
30
20
  static VALUE SYM_scheduled_fibers;
31
21
  static VALUE SYM_pending_watchers;
32
22
 
@@ -39,7 +29,7 @@ static VALUE Thread_fiber_scheduling_stats(VALUE self) {
39
29
  long scheduled_count = Runqueue_len(runqueue);
40
30
  rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(scheduled_count));
41
31
 
42
- pending_count = __BACKEND__.pending_count(backend);
32
+ pending_count = Backend_pending_count(backend);
43
33
  rb_hash_aset(stats, SYM_pending_watchers, INT2NUM(pending_count));
44
34
 
45
35
  return stats;
@@ -64,7 +54,7 @@ void schedule_fiber(VALUE self, VALUE fiber, VALUE value, int prioritize) {
64
54
  // happen, not knowing that it there's already a fiber ready to run in its
65
55
  // run queue.
66
56
  VALUE backend = rb_ivar_get(self,ID_ivar_backend);
67
- __BACKEND__.wakeup(backend);
57
+ Backend_wakeup(backend);
68
58
  }
69
59
  }
70
60
  }
@@ -84,25 +74,24 @@ VALUE Thread_switch_fiber(VALUE self) {
84
74
  VALUE runqueue = rb_ivar_get(self, ID_ivar_runqueue);
85
75
  runqueue_entry next;
86
76
  VALUE backend = rb_ivar_get(self, ID_ivar_backend);
87
- int ref_count;
88
- int backend_was_polled = 0;
77
+ unsigned int pending_count = Backend_pending_count(backend);
78
+ unsigned int backend_was_polled = 0;
89
79
 
90
80
  if (__tracing_enabled__ && (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse))
91
81
  TRACE(2, SYM_fiber_switchpoint, current_fiber);
92
82
 
93
- ref_count = __BACKEND__.ref_count(backend);
94
83
  while (1) {
95
84
  next = Runqueue_shift(runqueue);
96
85
  if (next.fiber != Qnil) {
97
- if (backend_was_polled == 0 && ref_count > 0) {
86
+ if (!backend_was_polled && pending_count) {
98
87
  // this prevents event starvation in case the run queue never empties
99
- __BACKEND__.poll(backend, Qtrue, current_fiber, runqueue);
88
+ Backend_poll(backend, Qtrue, current_fiber, runqueue);
100
89
  }
101
90
  break;
102
91
  }
103
- if (ref_count == 0) break;
92
+ if (pending_count == 0) break;
104
93
 
105
- __BACKEND__.poll(backend, Qnil, current_fiber, runqueue);
94
+ Backend_poll(backend, Qnil, current_fiber, runqueue);
106
95
  backend_was_polled = 1;
107
96
  }
108
97
 
@@ -118,20 +107,13 @@ VALUE Thread_switch_fiber(VALUE self) {
118
107
  next.value : FIBER_TRANSFER(next.fiber, next.value);
119
108
  }
120
109
 
121
- VALUE Thread_reset_fiber_scheduling(VALUE self) {
122
- VALUE queue = rb_ivar_get(self, ID_ivar_runqueue);
123
- Runqueue_clear(queue);
124
- Thread_fiber_reset_ref_count(self);
125
- return self;
126
- }
127
-
128
110
  VALUE Thread_fiber_schedule_and_wakeup(VALUE self, VALUE fiber, VALUE resume_obj) {
129
111
  VALUE backend = rb_ivar_get(self, ID_ivar_backend);
130
112
  if (fiber != Qnil) {
131
113
  Thread_schedule_fiber_with_priority(self, fiber, resume_obj);
132
114
  }
133
115
 
134
- if (__BACKEND__.wakeup(backend) == Qnil) {
116
+ if (Backend_wakeup(backend) == Qnil) {
135
117
  // we're not inside the ev_loop, so we just do a switchpoint
136
118
  Thread_switch_fiber(self);
137
119
  }
@@ -146,7 +128,6 @@ VALUE Thread_debug(VALUE self) {
146
128
 
147
129
  void Init_Thread() {
148
130
  rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
149
- rb_define_method(rb_cThread, "reset_fiber_scheduling", Thread_reset_fiber_scheduling, 0);
150
131
  rb_define_method(rb_cThread, "fiber_scheduling_stats", Thread_fiber_scheduling_stats, 0);
151
132
  rb_define_method(rb_cThread, "schedule_and_wakeup", Thread_fiber_schedule_and_wakeup, 2);
152
133
 
@@ -11,7 +11,7 @@ module ::PG
11
11
 
12
12
  def self.connect_async(conn)
13
13
  socket_io = conn.socket_io
14
- loop do
14
+ while true
15
15
  res = conn.connect_poll
16
16
  case res
17
17
  when PGRES_POLLING_FAILED then raise Error, conn.error_message
@@ -23,7 +23,7 @@ module ::PG
23
23
  end
24
24
 
25
25
  def self.connect_sync(conn)
26
- loop do
26
+ while true
27
27
  res = conn.connect_poll
28
28
  case res
29
29
  when PGRES_POLLING_FAILED
@@ -96,7 +96,7 @@ class ::PG::Connection
96
96
  def wait_for_notify(timeout = nil, &block)
97
97
  return move_on_after(timeout) { wait_for_notify(&block) } if timeout
98
98
 
99
- loop do
99
+ while true
100
100
  Thread.current.backend.wait_io(socket_io, false)
101
101
  consume_input
102
102
  notice = notifies
@@ -59,7 +59,15 @@ module Polyphony
59
59
  throttled_loop(rate: rate, interval: interval, &block)
60
60
  end
61
61
  else
62
- Fiber.current.spin(tag, caller) { loop(&block) }
62
+ spin_looped_block(tag, caller, block)
63
+ end
64
+ end
65
+
66
+ def spin_looped_block(tag, caller, block)
67
+ Fiber.current.spin(tag, caller) do
68
+ block.call while true
69
+ rescue LocalJumpError, StopIteration
70
+ # break called or StopIteration raised
63
71
  end
64
72
  end
65
73
 
@@ -133,8 +141,12 @@ module Polyphony
133
141
  if opts[:count]
134
142
  opts[:count].times { |_i| throttler.(&block) }
135
143
  else
136
- loop { throttler.(&block) }
144
+ while true
145
+ throttler.(&block)
146
+ end
137
147
  end
148
+ rescue LocalJumpError, StopIteration
149
+ # break called or StopIteration raised
138
150
  ensure
139
151
  throttler&.stop
140
152
  end
@@ -45,7 +45,9 @@ module Polyphony
45
45
  end
46
46
 
47
47
  def thread_loop
48
- loop { run_queued_task }
48
+ while true
49
+ run_queued_task
50
+ end
49
51
  end
50
52
 
51
53
  def run_queued_task
@@ -15,7 +15,7 @@ module Polyphony
15
15
  Thread.current.backend.sleep(delta) if delta > 0
16
16
  yield self
17
17
 
18
- loop do
18
+ while true
19
19
  @next_time += @min_dt
20
20
  break if @next_time > now
21
21
  end
@@ -34,9 +34,18 @@ module Polyphony
34
34
  schedule Polyphony::Cancel.new
35
35
  end
36
36
 
37
- def terminate
37
+ def graceful_shutdown=(graceful)
38
+ @graceful_shutdown = graceful
39
+ end
40
+
41
+ def graceful_shutdown?
42
+ @graceful_shutdown
43
+ end
44
+
45
+ def terminate(graceful = false)
38
46
  return if @running == false
39
47
 
48
+ @graceful_shutdown = graceful
40
49
  schedule Polyphony::Terminate.new
41
50
  end
42
51
 
@@ -66,7 +75,9 @@ module Polyphony
66
75
  @on_child_done = proc do |fiber, result|
67
76
  self << fiber unless result.is_a?(Exception)
68
77
  end
69
- loop { supervise_perform(opts) }
78
+ while true
79
+ supervise_perform(opts)
80
+ end
70
81
  rescue Polyphony::MoveOn
71
82
  # generated in #supervise_perform to stop supervisor
72
83
  ensure
@@ -210,11 +221,14 @@ module Polyphony
210
221
  @on_child_done&.(child_fiber, result)
211
222
  end
212
223
 
213
- def terminate_all_children
224
+ def terminate_all_children(graceful = false)
214
225
  return unless @children
215
226
 
216
227
  e = Polyphony::Terminate.new
217
- @children.each_key { |c| c.raise e }
228
+ @children.each_key do |c|
229
+ c.graceful_shutdown = true if graceful
230
+ c.raise e
231
+ end
218
232
  end
219
233
 
220
234
  def await_all_children
@@ -230,8 +244,8 @@ module Polyphony
230
244
  results.values
231
245
  end
232
246
 
233
- def shutdown_all_children
234
- terminate_all_children
247
+ def shutdown_all_children(graceful = false)
248
+ terminate_all_children(graceful)
235
249
  await_all_children
236
250
  end
237
251
  end
@@ -119,18 +119,11 @@ class ::IO
119
119
  end
120
120
 
121
121
  alias_method :orig_readpartial, :read
122
- def readpartial(len, str = nil)
123
- @read_buffer ||= +''
124
- result = Thread.current.backend.read(self, @read_buffer, len, false)
122
+ def readpartial(len, str = +'')
123
+ result = Thread.current.backend.read(self, str, len, false)
125
124
  raise EOFError unless result
126
125
 
127
- if str
128
- str << @read_buffer
129
- else
130
- str = @read_buffer
131
- end
132
- @read_buffer = +''
133
- str
126
+ result
134
127
  end
135
128
 
136
129
  alias_method :orig_write, :write
@@ -154,15 +147,16 @@ class ::IO
154
147
 
155
148
  @read_buffer ||= +''
156
149
 
157
- loop do
150
+ while true
158
151
  idx = @read_buffer.index(sep)
159
152
  return @read_buffer.slice!(0, idx + sep_size) if idx
160
153
 
161
- data = readpartial(8192)
154
+ data = readpartial(8192, +'')
155
+ return nil unless data
162
156
  @read_buffer << data
163
- rescue EOFError
164
- return nil
165
157
  end
158
+ rescue EOFError
159
+ return nil
166
160
  end
167
161
 
168
162
  # def print(*args)
@@ -25,7 +25,7 @@ class ::OpenSSL::SSL::SSLSocket
25
25
 
26
26
  alias_method :orig_accept, :accept
27
27
  def accept
28
- loop do
28
+ while true
29
29
  result = accept_nonblock(exception: false)
30
30
  case result
31
31
  when :wait_readable then Thread.current.backend.wait_io(io, false)
@@ -37,14 +37,14 @@ class ::OpenSSL::SSL::SSLSocket
37
37
  end
38
38
 
39
39
  def accept_loop
40
- loop do
40
+ while true
41
41
  yield accept
42
42
  end
43
43
  end
44
44
 
45
45
  alias_method :orig_sysread, :sysread
46
46
  def sysread(maxlen, buf = +'')
47
- loop do
47
+ while true
48
48
  case (result = read_nonblock(maxlen, buf, exception: false))
49
49
  when :wait_readable then Thread.current.backend.wait_io(io, false)
50
50
  when :wait_writable then Thread.current.backend.wait_io(io, true)
@@ -55,7 +55,7 @@ class ::OpenSSL::SSL::SSLSocket
55
55
 
56
56
  alias_method :orig_syswrite, :syswrite
57
57
  def syswrite(buf)
58
- loop do
58
+ while true
59
59
  case (result = write_nonblock(buf, exception: false))
60
60
  when :wait_readable then Thread.current.backend.wait_io(io, false)
61
61
  when :wait_writable then Thread.current.backend.wait_io(io, true)
@@ -33,7 +33,7 @@ class ::Socket
33
33
 
34
34
  def recvfrom(maxlen, flags = 0)
35
35
  @read_buffer ||= +''
36
- loop do
36
+ while true
37
37
  result = recvfrom_nonblock(maxlen, flags, @read_buffer, **NO_EXCEPTION)
38
38
  case result
39
39
  when nil then raise IOError
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.47.5.1'
4
+ VERSION = '0.48.0'
5
5
  end
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
6
6
  s.licenses = ['MIT']
7
7
  s.summary = 'Fine grained concurrency for Ruby'
8
8
  s.author = 'Sharon Rosner'
9
- s.email = 'ciconia@gmail.com'
9
+ s.email = 'sharon@noteflakes.com'
10
10
  s.files = `git ls-files`.split
11
11
  s.homepage = 'https://digital-fabric.github.io/polyphony'
12
12
  s.metadata = {
@@ -4,6 +4,7 @@ require 'bundler/setup'
4
4
 
5
5
  require_relative './coverage' if ENV['COVERAGE']
6
6
 
7
+ require 'httparty'
7
8
  require 'polyphony'
8
9
 
9
10
  require 'fileutils'
@@ -105,7 +105,7 @@ class BackendTest < MiniTest::Test
105
105
 
106
106
  clients = []
107
107
  server_fiber = spin do
108
- @backend.accept_loop(server) { |c| clients << c }
108
+ @backend.accept_loop(server, TCPSocket) { |c| clients << c }
109
109
  end
110
110
 
111
111
  c1 = TCPSocket.new('127.0.0.1', 1234)
@@ -1038,3 +1038,67 @@ class RestartTest < MiniTest::Test
1038
1038
  assert_equal [f, 'foo', 'bar', :done, f2, 'baz', 42, :done], buffer
1039
1039
  end
1040
1040
  end
1041
+
1042
+ class GracefulTerminationTest < MiniTest::Test
1043
+ def test_graceful_termination
1044
+ buffer = []
1045
+ f = spin do
1046
+ buffer << 1
1047
+ snooze
1048
+ buffer << 2
1049
+ sleep 3
1050
+ buffer << 3
1051
+ ensure
1052
+ buffer << 4 if Fiber.current.graceful_shutdown?
1053
+ end
1054
+
1055
+ 3.times { snooze }
1056
+ f.terminate(false)
1057
+ f.await
1058
+ assert_equal [1, 2], buffer
1059
+
1060
+ buffer = []
1061
+ f = spin do
1062
+ buffer << 1
1063
+ snooze
1064
+ buffer << 2
1065
+ sleep 3
1066
+ buffer << 3
1067
+ ensure
1068
+ buffer << 4 if Fiber.current.graceful_shutdown?
1069
+ end
1070
+
1071
+ 3.times { snooze }
1072
+ f.terminate(true)
1073
+ f.await
1074
+ assert_equal [1, 2, 4], buffer
1075
+ end
1076
+
1077
+ def test_graceful_child_shutdown
1078
+ buffer = []
1079
+ f0 = spin do
1080
+ f1 = spin do
1081
+ sleep
1082
+ ensure
1083
+ buffer << 1 if Fiber.current.graceful_shutdown?
1084
+ end
1085
+
1086
+ f2 = spin do
1087
+ sleep
1088
+ ensure
1089
+ buffer << 2 if Fiber.current.graceful_shutdown?
1090
+ end
1091
+
1092
+ sleep
1093
+ ensure
1094
+ Fiber.current.terminate_all_children(true) if Fiber.current.graceful_shutdown?
1095
+ Fiber.current.await_all_children
1096
+ end
1097
+
1098
+ 3.times { snooze }
1099
+ f0.terminate(true)
1100
+ f0.await
1101
+
1102
+ assert_equal [1, 2], buffer
1103
+ end
1104
+ end
@@ -307,6 +307,36 @@ class SpinLoopTest < MiniTest::Test
307
307
  f.stop
308
308
  assert_in_range 1..3, counter
309
309
  end
310
+
311
+ def test_spin_loop_break
312
+ i = 0
313
+ f = spin_loop do
314
+ i += 1
315
+ snooze
316
+ break if i >= 5
317
+ end
318
+ f.await
319
+ assert_equal 5, i
320
+
321
+ i = 0
322
+ f = spin_loop do
323
+ i += 1
324
+ snooze
325
+ raise StopIteration if i >= 5
326
+ end
327
+ f.await
328
+ assert_equal 5, i
329
+ end
330
+
331
+ def test_throttled_spin_loop_break
332
+ i = 0
333
+ f = spin_loop(rate: 100) do
334
+ i += 1
335
+ break if i >= 5
336
+ end
337
+ f.await
338
+ assert_equal 5, i
339
+ end
310
340
  end
311
341
 
312
342
  class SpinScopeTest < MiniTest::Test
@@ -92,6 +92,32 @@ class IOTest < MiniTest::Test
92
92
  assert_raises(EOFError) { i.readpartial(1) }
93
93
  end
94
94
 
95
+ def test_gets
96
+ i, o = IO.pipe
97
+
98
+ buf = []
99
+ f = spin do
100
+ while (l = i.gets)
101
+ buf << l
102
+ end
103
+ end
104
+
105
+ snooze
106
+ assert_equal [], buf
107
+
108
+ o << 'fab'
109
+ snooze
110
+ assert_equal [], buf
111
+
112
+ o << "ulous\n"
113
+ 10.times { snooze }
114
+ assert_equal ["fabulous\n"], buf
115
+
116
+ o.close
117
+ f.await
118
+ assert_equal ["fabulous\n"], buf
119
+ end
120
+
95
121
  def test_getc
96
122
  i, o = IO.pipe
97
123
 
@@ -60,7 +60,6 @@ class SocketTest < MiniTest::Test
60
60
  end
61
61
 
62
62
  class HTTPClientTest < MiniTest::Test
63
- require 'httparty'
64
63
  require 'json'
65
64
 
66
65
  def test_http
@@ -20,7 +20,7 @@ class SuperviseTest < MiniTest::Test
20
20
 
21
21
  f2 << 'bar'
22
22
  f2.await
23
- assert_equal :waiting, p.state
23
+ assert_equal :runnable, p.state
24
24
  snooze
25
25
 
26
26
  assert_equal :dead, p.state
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.47.5.1
4
+ version: 0.48.0
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-11-20 00:00:00.000000000 Z
11
+ date: 2021-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -269,7 +269,7 @@ dependencies:
269
269
  - !ruby/object:Gem::Version
270
270
  version: 0.3.0
271
271
  description:
272
- email: ciconia@gmail.com
272
+ email: sharon@noteflakes.com
273
273
  executables: []
274
274
  extensions:
275
275
  - ext/polyphony/extconf.rb
@@ -383,6 +383,7 @@ files:
383
383
  - examples/io/raw.rb
384
384
  - examples/io/reline.rb
385
385
  - examples/io/system.rb
386
+ - examples/io/tcp_proxy.rb
386
387
  - examples/io/tcpserver.rb
387
388
  - examples/io/tcpsocket.rb
388
389
  - examples/io/tunnel.rb
@@ -391,6 +392,8 @@ files:
391
392
  - examples/performance/fiber_resume.rb
392
393
  - examples/performance/fiber_transfer.rb
393
394
  - examples/performance/fs_read.rb
395
+ - examples/performance/line_splitting.rb
396
+ - examples/performance/loop.rb
394
397
  - examples/performance/mem-usage.rb
395
398
  - examples/performance/messaging.rb
396
399
  - examples/performance/multi_snooze.rb
@@ -433,7 +436,6 @@ files:
433
436
  - ext/liburing/setup.c
434
437
  - ext/liburing/syscall.c
435
438
  - ext/liburing/syscall.h
436
- - ext/polyphony/backend.h
437
439
  - ext/polyphony/backend_common.h
438
440
  - ext/polyphony/backend_io_uring.c
439
441
  - ext/polyphony/backend_io_uring_context.c
@@ -1,26 +0,0 @@
1
- #ifndef BACKEND_H
2
- #define BACKEND_H
3
-
4
- #include "ruby.h"
5
-
6
- typedef VALUE (* backend_pending_count_t)(VALUE self);
7
- typedef VALUE (*backend_poll_t)(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue);
8
- typedef VALUE (* backend_ref_t)(VALUE self);
9
- typedef int (* backend_ref_count_t)(VALUE self);
10
- typedef void (* backend_reset_ref_count_t)(VALUE self);
11
- typedef VALUE (* backend_unref_t)(VALUE self);
12
- typedef VALUE (* backend_wait_event_t)(VALUE self, VALUE raise_on_exception);
13
- typedef VALUE (* backend_wakeup_t)(VALUE self);
14
-
15
- typedef struct backend_interface {
16
- backend_pending_count_t pending_count;
17
- backend_poll_t poll;
18
- backend_ref_t ref;
19
- backend_ref_count_t ref_count;
20
- backend_reset_ref_count_t reset_ref_count;
21
- backend_unref_t unref;
22
- backend_wait_event_t wait_event;
23
- backend_wakeup_t wakeup;
24
- } backend_interface_t;
25
-
26
- #endif /* BACKEND_H */