polyphony 0.43.5 → 0.43.11

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +21 -4
  5. data/TODO.md +0 -7
  6. data/bin/stress.rb +28 -0
  7. data/docs/_includes/head.html +40 -0
  8. data/docs/_includes/title.html +1 -0
  9. data/docs/_user-guide/web-server.md +11 -11
  10. data/docs/getting-started/overview.md +2 -2
  11. data/docs/index.md +3 -1
  12. data/docs/polyphony-logo.png +0 -0
  13. data/examples/core/xx-channels.rb +4 -2
  14. data/examples/core/xx-using-a-mutex.rb +2 -1
  15. data/examples/io/xx-happy-eyeballs.rb +21 -22
  16. data/examples/io/xx-zip.rb +19 -0
  17. data/examples/performance/fiber_transfer.rb +47 -0
  18. data/examples/xx-spin.rb +32 -0
  19. data/ext/polyphony/agent.h +41 -0
  20. data/ext/polyphony/event.c +86 -0
  21. data/ext/polyphony/fiber.c +0 -5
  22. data/ext/polyphony/libev_agent.c +277 -135
  23. data/ext/polyphony/polyphony.c +2 -2
  24. data/ext/polyphony/polyphony.h +14 -21
  25. data/ext/polyphony/polyphony_ext.c +4 -2
  26. data/ext/polyphony/queue.c +208 -0
  27. data/ext/polyphony/ring_buffer.c +0 -24
  28. data/ext/polyphony/thread.c +42 -31
  29. data/lib/polyphony.rb +6 -7
  30. data/lib/polyphony/core/channel.rb +3 -34
  31. data/lib/polyphony/core/resource_pool.rb +13 -75
  32. data/lib/polyphony/core/sync.rb +12 -9
  33. data/lib/polyphony/extensions/fiber.rb +8 -8
  34. data/lib/polyphony/extensions/openssl.rb +8 -0
  35. data/lib/polyphony/extensions/socket.rb +11 -9
  36. data/lib/polyphony/extensions/thread.rb +1 -1
  37. data/lib/polyphony/net.rb +2 -1
  38. data/lib/polyphony/version.rb +1 -1
  39. data/test/helper.rb +2 -2
  40. data/test/test_agent.rb +2 -2
  41. data/test/test_event.rb +12 -0
  42. data/test/test_fiber.rb +1 -1
  43. data/test/test_io.rb +14 -0
  44. data/test/test_queue.rb +33 -0
  45. data/test/test_resource_pool.rb +24 -58
  46. data/test/test_trace.rb +18 -17
  47. metadata +12 -5
  48. data/ext/polyphony/libev_queue.c +0 -288
  49. data/lib/polyphony/event.rb +0 -27
@@ -96,4 +96,37 @@ class QueueTest < MiniTest::Test
96
96
  assert_nil f2.await
97
97
  assert_equal :bar, f3.await
98
98
  end
99
+
100
+ def test_fiber_removal_from_queue_simple
101
+ f1 = spin { @queue.shift }
102
+
103
+ # let fibers run
104
+ snooze
105
+
106
+ f1.stop
107
+ snooze
108
+
109
+ @queue << :foo
110
+ assert_nil f1.await
111
+ end
112
+
113
+ def test_queue_size
114
+ assert_equal 0, @queue.size
115
+
116
+ @queue.push 1
117
+
118
+ assert_equal 1, @queue.size
119
+
120
+ @queue.push 2
121
+
122
+ assert_equal 2, @queue.size
123
+
124
+ @queue.shift
125
+
126
+ assert_equal 1, @queue.size
127
+
128
+ @queue.shift
129
+
130
+ assert_equal 0, @queue.size
131
+ end
99
132
  end
@@ -12,72 +12,19 @@ class ResourcePoolTest < MiniTest::Test
12
12
  assert_equal 0, pool.size
13
13
 
14
14
  results = []
15
- 4.times {
16
- spin {
17
- snooze
15
+ 4.times { |i|
16
+ spin(:"foo#{i}") {
18
17
  pool.acquire { |resource|
19
18
  results << resource
20
19
  snooze
21
20
  }
22
21
  }
23
22
  }
24
- 2.times { snooze }
25
- assert_equal 2, pool.limit
26
- assert_equal 0, pool.available
27
- assert_equal 2, pool.size
28
-
29
- 2.times { snooze }
30
-
31
- assert_equal ['a', 'b', 'a', 'b'], results
32
-
33
- 2.times { snooze }
34
-
23
+ Fiber.current.await_all_children
35
24
  assert_equal 2, pool.limit
36
25
  assert_equal 2, pool.available
37
26
  assert_equal 2, pool.size
38
- end
39
-
40
- def test_discard
41
- resources = [+'a', +'b']
42
- pool = Polyphony::ResourcePool.new(limit: 2) { resources.shift }
43
-
44
- results = []
45
- 4.times {
46
- spin {
47
- snooze
48
- pool.acquire { |resource|
49
- results << resource
50
- resource.__discard__ if resource == 'b'
51
- snooze
52
- }
53
- }
54
- }
55
- 6.times { snooze }
56
-
57
- assert_equal ['a', 'b', 'a', 'a'], results
58
- assert_equal 1, pool.size
59
- end
60
-
61
- def test_add
62
- resources = [+'a', +'b']
63
- pool = Polyphony::ResourcePool.new(limit: 2) { resources.shift }
64
-
65
- pool << +'c'
66
-
67
- results = []
68
- 4.times {
69
- spin {
70
- snooze
71
- pool.acquire { |resource|
72
- results << resource
73
- resource.__discard__ if resource == 'b'
74
- snooze
75
- }
76
- }
77
- }
78
- 6.times { snooze }
79
-
80
- assert_equal ['c', 'a', 'c', 'a'], results
27
+ assert_equal ['a', 'b', 'a', 'b'], results
81
28
  end
82
29
 
83
30
  def test_single_resource_limit
@@ -94,7 +41,7 @@ class ResourcePoolTest < MiniTest::Test
94
41
  }
95
42
  }
96
43
  }
97
- 20.times { snooze }
44
+ 21.times { snooze }
98
45
 
99
46
  assert_equal ['a'] * 10, results
100
47
  end
@@ -135,4 +82,23 @@ class ResourcePoolTest < MiniTest::Test
135
82
  end
136
83
  end
137
84
  end
85
+
86
+ def test_overloaded_resource_pool
87
+ pool = Polyphony::ResourcePool.new(limit: 1) { 1 }
88
+
89
+ buf = []
90
+ fibers = 2.times.map do |i|
91
+ spin(:"foo#{i}") do
92
+ 2.times do
93
+ pool.acquire do |r|
94
+ buf << r
95
+ snooze
96
+ end
97
+ end
98
+ end
99
+ end
100
+ Fiber.current.await_all_children
101
+
102
+ assert_equal [1, 1, 1, 1], buf
103
+ end
138
104
  end
@@ -34,7 +34,8 @@ class TraceTest < MiniTest::Test
34
34
 
35
35
  def test_2_fiber_trace
36
36
  records = []
37
- t = Polyphony::Trace.new(:fiber_all) { |r| records << r if r[:event] =~ /^fiber_/ }
37
+ thread = Thread.current
38
+ t = Polyphony::Trace.new(:fiber_all) { |r| records << r if Thread.current == thread && r[:event] =~ /^fiber_/ }
38
39
  t.enable
39
40
  Polyphony.trace(true)
40
41
 
@@ -42,23 +43,23 @@ class TraceTest < MiniTest::Test
42
43
  suspend
43
44
  sleep 0
44
45
 
45
- events = records.map { |r| [r[:fiber], r[:event]] }
46
+ events = records.map { |r| [r[:fiber] == f ? :f : :current, r[:event]] }
46
47
  assert_equal [
47
- [f, :fiber_create],
48
- [f, :fiber_schedule],
49
- [Fiber.current, :fiber_switchpoint],
50
- [f, :fiber_run],
51
- [f, :fiber_switchpoint],
52
- [f, :fiber_ev_loop_enter],
53
- [f, :fiber_schedule],
54
- [f, :fiber_ev_loop_leave],
55
- [f, :fiber_run],
56
- [f, :fiber_terminate],
57
- [Fiber.current, :fiber_switchpoint],
58
- [Fiber.current, :fiber_ev_loop_enter],
59
- [Fiber.current, :fiber_schedule],
60
- [Fiber.current, :fiber_ev_loop_leave],
61
- [Fiber.current, :fiber_run]
48
+ [:f, :fiber_create],
49
+ [:f, :fiber_schedule],
50
+ [:current, :fiber_switchpoint],
51
+ [:f, :fiber_run],
52
+ [:f, :fiber_switchpoint],
53
+ [:f, :fiber_ev_loop_enter],
54
+ [:f, :fiber_schedule],
55
+ [:f, :fiber_ev_loop_leave],
56
+ [:f, :fiber_run],
57
+ [:f, :fiber_terminate],
58
+ [:current, :fiber_switchpoint],
59
+ [:current, :fiber_ev_loop_enter],
60
+ [:current, :fiber_schedule],
61
+ [:current, :fiber_ev_loop_leave],
62
+ [:current, :fiber_run]
62
63
  ], events
63
64
  ensure
64
65
  t&.disable
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.43.5
4
+ version: 0.43.11
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-07-12 00:00:00.000000000 Z
11
+ date: 2020-07-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -241,7 +241,10 @@ files:
241
241
  - Rakefile
242
242
  - TODO.md
243
243
  - bin/polyphony-debug
244
+ - bin/stress.rb
244
245
  - docs/_config.yml
246
+ - docs/_includes/head.html
247
+ - docs/_includes/title.html
245
248
  - docs/_sass/custom/custom.scss
246
249
  - docs/_sass/overrides.scss
247
250
  - docs/_user-guide/all-about-timers.md
@@ -351,6 +354,8 @@ files:
351
354
  - examples/io/xx-system.rb
352
355
  - examples/io/xx-tcpserver.rb
353
356
  - examples/io/xx-tcpsocket.rb
357
+ - examples/io/xx-zip.rb
358
+ - examples/performance/fiber_transfer.rb
354
359
  - examples/performance/fs_read.rb
355
360
  - examples/performance/mem-usage.rb
356
361
  - examples/performance/messaging.rb
@@ -367,6 +372,7 @@ files:
367
372
  - examples/performance/xx-array.rb
368
373
  - examples/performance/xx-fiber-switch.rb
369
374
  - examples/performance/xx-snooze.rb
375
+ - examples/xx-spin.rb
370
376
  - ext/libev/Changes
371
377
  - ext/libev/LICENSE
372
378
  - ext/libev/README
@@ -383,15 +389,17 @@ files:
383
389
  - ext/libev/ev_win32.c
384
390
  - ext/libev/ev_wrap.h
385
391
  - ext/libev/test_libev_win32.c
392
+ - ext/polyphony/agent.h
393
+ - ext/polyphony/event.c
386
394
  - ext/polyphony/extconf.rb
387
395
  - ext/polyphony/fiber.c
388
396
  - ext/polyphony/libev.c
389
397
  - ext/polyphony/libev.h
390
398
  - ext/polyphony/libev_agent.c
391
- - ext/polyphony/libev_queue.c
392
399
  - ext/polyphony/polyphony.c
393
400
  - ext/polyphony/polyphony.h
394
401
  - ext/polyphony/polyphony_ext.c
402
+ - ext/polyphony/queue.c
395
403
  - ext/polyphony/ring_buffer.c
396
404
  - ext/polyphony/ring_buffer.h
397
405
  - ext/polyphony/thread.c
@@ -410,7 +418,6 @@ files:
410
418
  - lib/polyphony/core/sync.rb
411
419
  - lib/polyphony/core/thread_pool.rb
412
420
  - lib/polyphony/core/throttler.rb
413
- - lib/polyphony/event.rb
414
421
  - lib/polyphony/extensions/core.rb
415
422
  - lib/polyphony/extensions/fiber.rb
416
423
  - lib/polyphony/extensions/io.rb
@@ -470,7 +477,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
470
477
  - !ruby/object:Gem::Version
471
478
  version: '0'
472
479
  requirements: []
473
- rubygems_version: 3.0.6
480
+ rubygems_version: 3.1.2
474
481
  signing_key:
475
482
  specification_version: 4
476
483
  summary: Fine grained concurrency for Ruby
@@ -1,288 +0,0 @@
1
- #include "polyphony.h"
2
- #include "ring_buffer.h"
3
-
4
- struct async_watcher {
5
- ev_async async;
6
- struct ev_loop *ev_loop;
7
- VALUE fiber;
8
- };
9
-
10
- struct async_watcher_queue {
11
- struct async_watcher **queue;
12
- unsigned int length;
13
- unsigned int count;
14
- unsigned int push_idx;
15
- unsigned int shift_idx;
16
- };
17
-
18
- void async_watcher_queue_init(struct async_watcher_queue *queue) {
19
- queue->length = 1;
20
- queue->count = 0;
21
- queue->queue = malloc(sizeof(struct async_watcher *) * queue->length);
22
- queue->push_idx = 0;
23
- queue->shift_idx = 0;
24
- }
25
-
26
- void async_watcher_queue_free(struct async_watcher_queue *queue) {
27
- free(queue->queue);
28
- }
29
-
30
- void async_watcher_queue_realign(struct async_watcher_queue *queue) {
31
- memmove(
32
- queue->queue,
33
- queue->queue + queue->shift_idx,
34
- queue->count * sizeof(struct async_watcher *)
35
- );
36
- queue->push_idx = queue->push_idx - queue->shift_idx;
37
- queue->shift_idx = 0;
38
- }
39
-
40
- #define QUEUE_REALIGN_THRESHOLD 32
41
-
42
- void async_watcher_queue_push(struct async_watcher_queue *queue, struct async_watcher *watcher) {
43
- if (queue->count == 0) {
44
- queue->push_idx = 0;
45
- queue->shift_idx = 0;
46
- }
47
- if (queue->push_idx == queue->length) {
48
- // prevent shift idx moving too much away from zero
49
- if (queue->length >= QUEUE_REALIGN_THRESHOLD && queue->shift_idx >= (queue->length / 2))
50
- async_watcher_queue_realign(queue);
51
- else {
52
- queue->length = (queue->length == 1) ? 4 : queue->length * 2;
53
- queue->queue = realloc(queue->queue, sizeof(struct async_watcher *) * queue->length);
54
- }
55
- }
56
- queue->count++;
57
- queue->queue[queue->push_idx++] = watcher;
58
- }
59
-
60
- struct async_watcher *async_watcher_queue_shift(struct async_watcher_queue *queue) {
61
- if (queue->count == 0) return 0;
62
-
63
- queue->count--;
64
-
65
- return queue->queue[queue->shift_idx++];
66
- }
67
-
68
- void async_watcher_queue_remove_at_idx(struct async_watcher_queue *queue, unsigned int remove_idx) {
69
- queue->count--;
70
- queue->push_idx--;
71
- if (remove_idx < queue->push_idx)
72
- memmove(
73
- queue->queue + remove_idx,
74
- queue->queue + remove_idx + 1,
75
- (queue->push_idx - remove_idx) * sizeof(struct async_watcher *)
76
- );
77
- }
78
-
79
- void async_watcher_queue_remove_by_fiber(struct async_watcher_queue *queue, VALUE fiber) {
80
- if (queue->count == 0) return;
81
-
82
- for (unsigned idx = queue->shift_idx; idx < queue->push_idx; idx++) {
83
- if (queue->queue[idx]->fiber == fiber) {
84
- async_watcher_queue_remove_at_idx(queue, idx);
85
- return;
86
- }
87
- }
88
- }
89
-
90
- typedef struct queue {
91
- ring_buffer values;
92
- struct async_watcher_queue shift_queue;
93
- } LibevQueue_t;
94
-
95
- VALUE cLibevQueue = Qnil;
96
-
97
- static void LibevQueue_mark(void *ptr) {
98
- LibevQueue_t *queue = ptr;
99
- ring_buffer_mark(&queue->values);
100
- }
101
-
102
- static void LibevQueue_free(void *ptr) {
103
- LibevQueue_t *queue = ptr;
104
- ring_buffer_free(&queue->values);
105
- async_watcher_queue_free(&queue->shift_queue);
106
- xfree(ptr);
107
- }
108
-
109
- static size_t LibevQueue_size(const void *ptr) {
110
- return sizeof(LibevQueue_t);
111
- }
112
-
113
- static const rb_data_type_t LibevQueue_type = {
114
- "Queue",
115
- {LibevQueue_mark, LibevQueue_free, LibevQueue_size,},
116
- 0, 0, 0
117
- };
118
-
119
- static VALUE LibevQueue_allocate(VALUE klass) {
120
- LibevQueue_t *queue;
121
-
122
- queue = ALLOC(LibevQueue_t);
123
- return TypedData_Wrap_Struct(klass, &LibevQueue_type, queue);
124
- }
125
-
126
- #define GetQueue(obj, queue) \
127
- TypedData_Get_Struct((obj), LibevQueue_t, &LibevQueue_type, (queue))
128
-
129
- static VALUE LibevQueue_initialize(VALUE self) {
130
- LibevQueue_t *queue;
131
- GetQueue(self, queue);
132
-
133
- ring_buffer_init(&queue->values);
134
- async_watcher_queue_init(&queue->shift_queue);
135
-
136
- return self;
137
- }
138
-
139
- VALUE LibevQueue_push(VALUE self, VALUE value) {
140
- LibevQueue_t *queue;
141
- GetQueue(self, queue);
142
- if (queue->shift_queue.count > 0) {
143
- struct async_watcher *watcher = async_watcher_queue_shift(&queue->shift_queue);
144
- if (watcher) {
145
- ev_async_send(watcher->ev_loop, &watcher->async);
146
- }
147
- }
148
- ring_buffer_push(&queue->values, value);
149
- return self;
150
- }
151
-
152
- VALUE LibevQueue_unshift(VALUE self, VALUE value) {
153
- LibevQueue_t *queue;
154
- GetQueue(self, queue);
155
- if (queue->shift_queue.count > 0) {
156
- struct async_watcher *watcher = async_watcher_queue_shift(&queue->shift_queue);
157
- if (watcher) {
158
- ev_async_send(watcher->ev_loop, &watcher->async);
159
- }
160
- }
161
- ring_buffer_unshift(&queue->values, value);
162
- return self;
163
- }
164
-
165
- struct ev_loop *LibevAgent_ev_loop(VALUE self);
166
-
167
- void async_watcher_queue_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
168
- struct async_watcher *watcher = (struct async_watcher *)ev_async;
169
- Fiber_make_runnable(watcher->fiber, Qnil);
170
- }
171
-
172
- VALUE libev_agent_await(VALUE self);
173
-
174
- VALUE LibevQueue_shift(VALUE self) {
175
- LibevQueue_t *queue;
176
- GetQueue(self, queue);
177
-
178
- if (queue->values.count == 0) {
179
- struct async_watcher watcher;
180
- VALUE agent = rb_ivar_get(rb_thread_current(), ID_ivar_agent);
181
- VALUE switchpoint_result = Qnil;
182
-
183
- watcher.ev_loop = LibevAgent_ev_loop(agent);
184
- watcher.fiber = rb_fiber_current();
185
- async_watcher_queue_push(&queue->shift_queue, &watcher);
186
- ev_async_init(&watcher.async, async_watcher_queue_callback);
187
- ev_async_start(watcher.ev_loop, &watcher.async);
188
-
189
- switchpoint_result = libev_agent_await(agent);
190
- ev_async_stop(watcher.ev_loop, &watcher.async);
191
-
192
- if (RTEST(rb_obj_is_kind_of(switchpoint_result, rb_eException))) {
193
- async_watcher_queue_remove_by_fiber(&queue->shift_queue, watcher.fiber);
194
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
195
- }
196
- RB_GC_GUARD(watcher.fiber);
197
- RB_GC_GUARD(agent);
198
- RB_GC_GUARD(switchpoint_result);
199
- }
200
-
201
- return ring_buffer_shift(&queue->values);
202
- }
203
-
204
- VALUE LibevQueue_shift_no_wait(VALUE self) {
205
- LibevQueue_t *queue;
206
- GetQueue(self, queue);
207
-
208
- return ring_buffer_shift(&queue->values);
209
- }
210
-
211
- VALUE LibevQueue_delete(VALUE self, VALUE value) {
212
- LibevQueue_t *queue;
213
- GetQueue(self, queue);
214
-
215
- ring_buffer_delete(&queue->values, value);
216
- return self;
217
- }
218
-
219
- VALUE LibevQueue_clear(VALUE self) {
220
- LibevQueue_t *queue;
221
- GetQueue(self, queue);
222
-
223
- ring_buffer_clear(&queue->values);
224
- return self;
225
- }
226
-
227
- long LibevQueue_len(VALUE self) {
228
- LibevQueue_t *queue;
229
- GetQueue(self, queue);
230
-
231
- return queue->values.count;
232
- }
233
-
234
- VALUE LibevQueue_shift_each(VALUE self) {
235
- LibevQueue_t *queue;
236
- GetQueue(self, queue);
237
-
238
- ring_buffer_shift_each(&queue->values);
239
- return self;
240
- }
241
-
242
- VALUE LibevQueue_shift_all(VALUE self) {
243
- LibevQueue_t *queue;
244
- GetQueue(self, queue);
245
-
246
- return ring_buffer_shift_all(&queue->values);
247
- }
248
-
249
- VALUE LibevQueue_empty_p(VALUE self) {
250
- LibevQueue_t *queue;
251
- GetQueue(self, queue);
252
-
253
- return (queue->values.count == 0) ? Qtrue : Qfalse;
254
- }
255
-
256
- void LibevQueue_trace(VALUE self) {
257
- LibevQueue_t *queue;
258
- GetQueue(self, queue);
259
-
260
- printf(
261
- "queue size: %d count: %d head: %d tail: %d\n",
262
- queue->values.size,
263
- queue->values.count,
264
- queue->values.head,
265
- queue->values.tail
266
- );
267
- }
268
-
269
- void Init_LibevQueue() {
270
- cLibevQueue = rb_define_class_under(mPolyphony, "LibevQueue", rb_cData);
271
- rb_define_alloc_func(cLibevQueue, LibevQueue_allocate);
272
-
273
- rb_define_method(cLibevQueue, "initialize", LibevQueue_initialize, 0);
274
- rb_define_method(cLibevQueue, "push", LibevQueue_push, 1);
275
- rb_define_method(cLibevQueue, "<<", LibevQueue_push, 1);
276
- rb_define_method(cLibevQueue, "unshift", LibevQueue_unshift, 1);
277
-
278
- rb_define_method(cLibevQueue, "shift", LibevQueue_shift, 0);
279
- rb_define_method(cLibevQueue, "pop", LibevQueue_shift, 0);
280
- rb_define_method(cLibevQueue, "shift_no_wait", LibevQueue_shift_no_wait, 0);
281
- rb_define_method(cLibevQueue, "delete", LibevQueue_delete, 1);
282
-
283
- rb_define_method(cLibevQueue, "shift_each", LibevQueue_shift_each, 0);
284
- rb_define_method(cLibevQueue, "shift_all", LibevQueue_shift_all, 0);
285
- rb_define_method(cLibevQueue, "empty?", LibevQueue_empty_p, 0);
286
- }
287
-
288
-