polyphony 0.57.0 → 0.58

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: e350937221c476548465a2881e64689042137eab59571a0940f63236a249563f
4
- data.tar.gz: 14870d9e38e7aa9f300d09cdfa0693929edb59756e0d17e7b3e7ae2d4a9dab0d
3
+ metadata.gz: e8002b8c0e03afa8e915b5ed67d64c1fc288a5fa220dfb2c32d3966a78046eee
4
+ data.tar.gz: c3a46eeae1f048f4adee9d3130f5dd3593936e843c1874d928f742d1fa83053f
5
5
  SHA512:
6
- metadata.gz: 8087b84c7a583c8f3905c20d06aa4ad119fd2901be2594e66f56b577c1fa141f9e203971aedd27ad7c57b9c184adad1b97d0fbe4024702bfe3ef6bdaa861ac92
7
- data.tar.gz: 6943231ef2b29dac3e33cfee865dbc6b3b35549c62e04dd1ed08d40fbd90a5c179417baf3b27b965e80c3ee7e137cbe167be9e8187dff46d89892978beb8a40d
6
+ metadata.gz: e6681155761daee35b73c9674e69aa505786e576814db0c909028cb576d8609f99bccc79f80b7d06d90c7658747bc2589c9837d3bdb3419782147e573d39f278
7
+ data.tar.gz: 90197a8db54405ca37492a54a20adf00c85f142af606cad26ad823161825ccf6bd9a414adcbbf3e27eaf93eea1d94a7cbc50a2fb5982ee4ce48969d4378400cd
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 0.58 2021-06-25
2
+
3
+ - Implement `Thread#idle_gc_period`, `#on_idle` (#56)
4
+ - Implement `Backend#idle_block=` (#56)
5
+
1
6
  ## 0.57.0 2021-06-23
2
7
 
3
8
  - Implement `Backend#splice_chunks` method for both libev and io_uring backends
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.57.0)
4
+ polyphony (0.58)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -5,6 +5,14 @@
5
5
  #include "polyphony.h"
6
6
  #include "backend_common.h"
7
7
 
8
+ inline void initialize_backend_base(struct Backend_base *base) {
9
+ base->currently_polling = 0;
10
+ base->pending_count = 0;
11
+ base->idle_gc_period = 0;
12
+ base->idle_gc_last_time = 0;
13
+ base->idle_block = Qnil;
14
+ }
15
+
8
16
  #ifdef POLYPHONY_USE_PIDFD_OPEN
9
17
  #ifndef __NR_pidfd_open
10
18
  #define __NR_pidfd_open 434 /* System call # on most architectures */
@@ -174,6 +182,9 @@ inline void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking) {
174
182
  }
175
183
 
176
184
  inline void backend_run_idle_tasks(struct Backend_base *base) {
185
+ if (base->idle_block != Qnil)
186
+ rb_funcall(base->idle_block, ID_call, 0);
187
+
177
188
  if (base->idle_gc_period == 0) return;
178
189
 
179
190
  double now = current_time();
@@ -9,8 +9,11 @@ struct Backend_base {
9
9
  unsigned int pending_count;
10
10
  double idle_gc_period;
11
11
  double idle_gc_last_time;
12
+ VALUE idle_block;
12
13
  };
13
14
 
15
+ void initialize_backend_base(struct Backend_base *base);
16
+
14
17
  #ifdef POLYPHONY_USE_PIDFD_OPEN
15
18
  int pidfd_open(pid_t pid, unsigned int flags);
16
19
  #endif
@@ -42,13 +42,19 @@ typedef struct Backend_t {
42
42
  int event_fd;
43
43
  } Backend_t;
44
44
 
45
+ static void Backend_mark(void *ptr) {
46
+ Backend_t *backend = ptr;
47
+ if (backend->base.idle_block != Qnil)
48
+ rb_gc_mark(backend->base.idle_block);
49
+ }
50
+
45
51
  static size_t Backend_size(const void *ptr) {
46
52
  return sizeof(Backend_t);
47
53
  }
48
54
 
49
55
  static const rb_data_type_t Backend_type = {
50
56
  "IOUringBackend",
51
- {0, 0, Backend_size,},
57
+ {Backend_mark, 0, Backend_size,},
52
58
  0, 0, RUBY_TYPED_FREE_IMMEDIATELY
53
59
  };
54
60
 
@@ -65,11 +71,7 @@ static VALUE Backend_initialize(VALUE self) {
65
71
  Backend_t *backend;
66
72
  GetBackend(self, backend);
67
73
 
68
- backend->base.currently_polling = 0;
69
- backend->base.pending_count = 0;
70
- backend->base.idle_gc_period = 0;
71
- backend->base.idle_gc_last_time = 0;
72
-
74
+ initialize_backend_base(&backend->base);
73
75
  backend->pending_sqes = 0;
74
76
  backend->prepared_limit = 2048;
75
77
 
@@ -134,6 +136,7 @@ static inline void io_uring_backend_handle_completion(struct io_uring_cqe *cqe,
134
136
  op_context_t *ctx = io_uring_cqe_get_data(cqe);
135
137
  if (!ctx) return;
136
138
 
139
+ // printf("cqe ctx %p id: %d result: %d (%s, ref_count: %d)\n", ctx, ctx->id, cqe->res, op_type_to_str(ctx->type), ctx->ref_count);
137
140
  ctx->result = cqe->res;
138
141
  if (ctx->ref_count == 2 && ctx->result != -ECANCELED && ctx->fiber)
139
142
  Fiber_make_runnable(ctx->fiber, ctx->resume_value);
@@ -908,7 +911,6 @@ int io_uring_backend_submit_timeout_and_await(Backend_t *backend, double duratio
908
911
 
909
912
  op_context_t *ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
910
913
  io_uring_prep_timeout(sqe, &ts, 0, 0);
911
-
912
914
  io_uring_backend_defer_submit_and_await(backend, sqe, ctx, resume_value);
913
915
  return context_store_release(&backend->store, ctx);
914
916
  }
@@ -1185,6 +1187,13 @@ VALUE Backend_idle_gc_period_set(VALUE self, VALUE period) {
1185
1187
  return self;
1186
1188
  }
1187
1189
 
1190
+ VALUE Backend_idle_block_set(VALUE self, VALUE block) {
1191
+ Backend_t *backend;
1192
+ GetBackend(self, backend);
1193
+ backend->base.idle_block = block;
1194
+ return self;
1195
+ }
1196
+
1188
1197
  inline VALUE Backend_run_idle_tasks(VALUE self) {
1189
1198
  Backend_t *backend;
1190
1199
  GetBackend(self, backend);
@@ -1359,6 +1368,7 @@ void Init_Backend() {
1359
1368
  rb_define_method(cBackend, "kind", Backend_kind, 0);
1360
1369
  rb_define_method(cBackend, "chain", Backend_chain, -1);
1361
1370
  rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
1371
+ rb_define_method(cBackend, "idle_block=", Backend_idle_block_set, 1);
1362
1372
  rb_define_method(cBackend, "splice_chunks", Backend_splice_chunks, 7);
1363
1373
 
1364
1374
  rb_define_method(cBackend, "accept", Backend_accept, 2);
@@ -37,8 +37,7 @@ inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_ty
37
37
  ctx = malloc(sizeof(op_context_t));
38
38
  }
39
39
  ctx->id = (++store->last_id);
40
- // printf("acquire %d (%s)\n", ctx->id, op_type_to_str(type));
41
-
40
+ // printf("acquire %p %d (%s)\n", ctx, ctx->id, op_type_to_str(type));
42
41
  ctx->prev = NULL;
43
42
  ctx->next = store->taken;
44
43
  if (store->taken) store->taken->prev = ctx;
@@ -55,7 +54,7 @@ inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_ty
55
54
 
56
55
  // returns true if ctx was released
57
56
  inline int context_store_release(op_context_store_t *store, op_context_t *ctx) {
58
- // printf("release %d (%s, ref_count: %d)\n", ctx->id, op_type_to_str(ctx->type), ctx->ref_count);
57
+ // printf("release %p %d (%s, ref_count: %d)\n", ctx, ctx->id, op_type_to_str(ctx->type), ctx->ref_count);
59
58
 
60
59
  assert(ctx->ref_count);
61
60
 
@@ -73,13 +73,19 @@ typedef struct Backend_t {
73
73
  struct ev_async break_async;
74
74
  } Backend_t;
75
75
 
76
+ static void Backend_mark(void *ptr) {
77
+ Backend_t *backend = ptr;
78
+ if (backend->base.idle_block != Qnil)
79
+ rb_gc_mark(backend->base.idle_block);
80
+ }
81
+
76
82
  static size_t Backend_size(const void *ptr) {
77
83
  return sizeof(Backend_t);
78
84
  }
79
85
 
80
86
  static const rb_data_type_t Backend_type = {
81
87
  "LibevBackend",
82
- {0, 0, Backend_size,},
88
+ {Backend_mark, 0, Backend_size,},
83
89
  0, 0, RUBY_TYPED_FREE_IMMEDIATELY
84
90
  };
85
91
 
@@ -110,6 +116,8 @@ static VALUE Backend_initialize(VALUE self) {
110
116
  Backend_t *backend;
111
117
 
112
118
  GetBackend(self, backend);
119
+
120
+ initialize_backend_base(&backend->base);
113
121
  backend->ev_loop = libev_new_loop();
114
122
 
115
123
  // start async watcher used for breaking a poll op (from another thread)
@@ -119,11 +127,6 @@ static VALUE Backend_initialize(VALUE self) {
119
127
  // block when no other watcher is active
120
128
  ev_unref(backend->ev_loop);
121
129
 
122
- backend->base.currently_polling = 0;
123
- backend->base.pending_count = 0;
124
- backend->base.idle_gc_period = 0;
125
- backend->base.idle_gc_last_time = 0;
126
-
127
130
  return Qnil;
128
131
  }
129
132
 
@@ -1290,6 +1293,13 @@ VALUE Backend_idle_gc_period_set(VALUE self, VALUE period) {
1290
1293
  return self;
1291
1294
  }
1292
1295
 
1296
+ VALUE Backend_idle_block_set(VALUE self, VALUE block) {
1297
+ Backend_t *backend;
1298
+ GetBackend(self, backend);
1299
+ backend->base.idle_block = block;
1300
+ return self;
1301
+ }
1302
+
1293
1303
  inline VALUE Backend_run_idle_tasks(VALUE self) {
1294
1304
  Backend_t *backend;
1295
1305
  GetBackend(self, backend);
@@ -1448,6 +1458,7 @@ void Init_Backend() {
1448
1458
  rb_define_method(cBackend, "kind", Backend_kind, 0);
1449
1459
  rb_define_method(cBackend, "chain", Backend_chain, -1);
1450
1460
  rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
1461
+ rb_define_method(cBackend, "idle_block=", Backend_idle_block_set, 1);
1451
1462
  rb_define_method(cBackend, "splice_chunks", Backend_splice_chunks, 7);
1452
1463
 
1453
1464
  rb_define_method(cBackend, "accept", Backend_accept, 2);
@@ -105,4 +105,12 @@ class ::Thread
105
105
  main_fiber << value
106
106
  end
107
107
  alias_method :send, :<<
108
+
109
+ def idle_gc_period=(period)
110
+ backend.idle_gc_period = period
111
+ end
112
+
113
+ def on_idle(&block)
114
+ backend.idle_block = block
115
+ end
108
116
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.57.0'
4
+ VERSION = '0.58'
5
5
  end
data/test/test_backend.rb CHANGED
@@ -340,9 +340,38 @@ class BackendTest < MiniTest::Test
340
340
  # GC period.
341
341
  sleep 0.05
342
342
  assert_equal count + 1, GC.count
343
+
344
+ @backend.idle_gc_period = 0
345
+ count = GC.count
346
+ sleep 0.001
347
+ sleep 0.002
348
+ sleep 0.003
349
+ assert_equal count, GC.count
343
350
  ensure
344
351
  GC.enable
345
352
  end
353
+
354
+ def test_idle_block
355
+ counter = 0
356
+
357
+ @backend.idle_block = proc { counter += 1 }
358
+
359
+ 3.times { snooze }
360
+ assert_equal 0, counter
361
+
362
+ sleep 0.01
363
+ assert_equal 1, counter
364
+ sleep 0.01
365
+ assert_equal 2, counter
366
+
367
+ assert_equal 2, counter
368
+ 3.times { snooze }
369
+ assert_equal 2, counter
370
+
371
+ @backend.idle_block = nil
372
+ sleep 0.01
373
+ assert_equal 2, counter
374
+ end
346
375
  end
347
376
 
348
377
  class BackendChainTest < MiniTest::Test
data/test/test_thread.rb CHANGED
@@ -171,4 +171,56 @@ class ThreadTest < MiniTest::Test
171
171
  t&.kill
172
172
  t&.join
173
173
  end
174
+
175
+ def test_idle_gc
176
+ GC.disable
177
+
178
+ count = GC.count
179
+ snooze
180
+ assert_equal count, GC.count
181
+ sleep 0.01
182
+ assert_equal count, GC.count
183
+
184
+ Thread.current.idle_gc_period = 0.1
185
+ snooze
186
+ assert_equal count, GC.count
187
+ sleep 0.05
188
+ assert_equal count, GC.count
189
+ # The idle tasks are ran at most once per fiber switch, before the backend
190
+ # is polled. Therefore, the second sleep will not have triggered a GC, since
191
+ # only 0.05s have passed since the gc period was set.
192
+ sleep 0.07
193
+ assert_equal count, GC.count
194
+ # Upon the third sleep the GC should be triggered, at 0.12s post setting the
195
+ # GC period.
196
+ sleep 0.05
197
+ assert_equal count + 1, GC.count
198
+
199
+ Thread.current.idle_gc_period = 0
200
+ count = GC.count
201
+ sleep 0.001
202
+ sleep 0.002
203
+ sleep 0.003
204
+ assert_equal count, GC.count
205
+ ensure
206
+ GC.enable
207
+ end
208
+
209
+ def test_on_idle
210
+ counter = 0
211
+
212
+ Thread.current.on_idle { counter += 1 }
213
+
214
+ 3.times { snooze }
215
+ assert_equal 0, counter
216
+
217
+ sleep 0.01
218
+ assert_equal 1, counter
219
+ sleep 0.01
220
+ assert_equal 2, counter
221
+
222
+ assert_equal 2, counter
223
+ 3.times { snooze }
224
+ assert_equal 2, counter
225
+ end
174
226
  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.57.0
4
+ version: '0.58'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-23 00:00:00.000000000 Z
11
+ date: 2021-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler