polyphony 0.43.5 → 0.43.11

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