polyphony 0.43.4 → 0.43.10
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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +1 -1
- data/CHANGELOG.md +45 -0
- data/Gemfile.lock +1 -1
- data/README.md +21 -4
- data/TODO.md +1 -6
- data/bin/stress.rb +28 -0
- data/docs/_includes/head.html +40 -0
- data/docs/_includes/title.html +1 -0
- data/docs/_user-guide/web-server.md +11 -11
- data/docs/getting-started/overview.md +2 -2
- data/docs/index.md +4 -3
- data/docs/main-concepts/design-principles.md +23 -34
- data/docs/main-concepts/fiber-scheduling.md +1 -1
- data/docs/polyphony-logo.png +0 -0
- data/examples/core/xx-channels.rb +4 -2
- data/examples/core/xx-using-a-mutex.rb +2 -1
- data/examples/io/xx-happy-eyeballs.rb +21 -22
- data/examples/io/xx-zip.rb +19 -0
- data/examples/performance/fiber_transfer.rb +47 -0
- data/examples/performance/messaging.rb +29 -0
- data/examples/performance/multi_snooze.rb +11 -9
- data/examples/xx-spin.rb +32 -0
- data/ext/polyphony/agent.h +39 -0
- data/ext/polyphony/event.c +86 -0
- data/ext/polyphony/fiber.c +0 -5
- data/ext/polyphony/libev_agent.c +231 -79
- data/ext/polyphony/polyphony.c +2 -2
- data/ext/polyphony/polyphony.h +19 -16
- data/ext/polyphony/polyphony_ext.c +4 -2
- data/ext/polyphony/queue.c +194 -0
- data/ext/polyphony/ring_buffer.c +96 -0
- data/ext/polyphony/ring_buffer.h +28 -0
- data/ext/polyphony/thread.c +48 -31
- data/lib/polyphony.rb +5 -6
- data/lib/polyphony/core/channel.rb +3 -34
- data/lib/polyphony/core/resource_pool.rb +13 -75
- data/lib/polyphony/core/sync.rb +12 -9
- data/lib/polyphony/core/thread_pool.rb +1 -1
- data/lib/polyphony/extensions/core.rb +9 -0
- data/lib/polyphony/extensions/fiber.rb +9 -2
- data/lib/polyphony/extensions/io.rb +16 -15
- data/lib/polyphony/extensions/openssl.rb +8 -0
- data/lib/polyphony/extensions/socket.rb +13 -9
- data/lib/polyphony/extensions/thread.rb +1 -1
- data/lib/polyphony/version.rb +1 -1
- data/test/helper.rb +2 -2
- data/test/q.rb +24 -0
- data/test/test_agent.rb +2 -2
- data/test/test_event.rb +12 -0
- data/test/test_global_api.rb +2 -2
- data/test/test_io.rb +24 -2
- data/test/test_queue.rb +59 -1
- data/test/test_resource_pool.rb +0 -43
- data/test/test_trace.rb +18 -17
- metadata +16 -5
- data/ext/polyphony/libev_queue.c +0 -217
- data/lib/polyphony/event.rb +0 -27
data/test/test_agent.rb
CHANGED
@@ -6,7 +6,7 @@ class AgentTest < MiniTest::Test
|
|
6
6
|
def setup
|
7
7
|
super
|
8
8
|
@prev_agent = Thread.current.agent
|
9
|
-
@agent = Polyphony::
|
9
|
+
@agent = Polyphony::Agent.new
|
10
10
|
Thread.current.agent = @agent
|
11
11
|
end
|
12
12
|
|
@@ -97,7 +97,7 @@ class AgentTest < MiniTest::Test
|
|
97
97
|
o.close
|
98
98
|
|
99
99
|
# read_loop will snooze after every read
|
100
|
-
|
100
|
+
6.times { snooze }
|
101
101
|
|
102
102
|
assert_equal [:ready, 'foo', 'bar', :done], buf
|
103
103
|
end
|
data/test/test_event.rb
CHANGED
@@ -34,6 +34,7 @@ class EventTest < MiniTest::Test
|
|
34
34
|
}
|
35
35
|
}
|
36
36
|
snooze
|
37
|
+
|
37
38
|
t = Thread.new do
|
38
39
|
orig_sleep 0.001
|
39
40
|
3.times { a.signal }
|
@@ -45,4 +46,15 @@ class EventTest < MiniTest::Test
|
|
45
46
|
t&.kill
|
46
47
|
t&.join
|
47
48
|
end
|
49
|
+
|
50
|
+
def test_exception_while_waiting_for_event
|
51
|
+
e = Polyphony::Event.new
|
52
|
+
|
53
|
+
f = spin { e.await }
|
54
|
+
g = spin { f.raise 'foo' }
|
55
|
+
|
56
|
+
assert_raises(RuntimeError) do
|
57
|
+
f.await
|
58
|
+
end
|
59
|
+
end
|
48
60
|
end
|
data/test/test_global_api.rb
CHANGED
@@ -211,13 +211,13 @@ class SpinLoopTest < MiniTest::Test
|
|
211
211
|
|
212
212
|
def test_spin_loop_location
|
213
213
|
location = /^#{__FILE__}:#{__LINE__ + 1}/
|
214
|
-
f = spin_loop {}
|
214
|
+
f = spin_loop { snooze }
|
215
215
|
|
216
216
|
assert_match location, f.location
|
217
217
|
end
|
218
218
|
|
219
219
|
def test_spin_loop_tag
|
220
|
-
f = spin_loop(:my_loop) {}
|
220
|
+
f = spin_loop(:my_loop) { snooze }
|
221
221
|
|
222
222
|
assert_equal :my_loop, f.tag
|
223
223
|
end
|
data/test/test_io.rb
CHANGED
@@ -30,6 +30,14 @@ class IOTest < MiniTest::Test
|
|
30
30
|
assert_equal 'hello', msg
|
31
31
|
end
|
32
32
|
|
33
|
+
def test_write_multiple_arguments
|
34
|
+
i, o = IO.pipe
|
35
|
+
count = o.write('a', 'b', "\n", 'c')
|
36
|
+
assert_equal 4, count
|
37
|
+
o.close
|
38
|
+
assert_equal "ab\nc", i.read
|
39
|
+
end
|
40
|
+
|
33
41
|
def test_that_double_chevron_method_returns_io
|
34
42
|
assert_equal @o, @o << 'foo'
|
35
43
|
|
@@ -83,6 +91,20 @@ class IOTest < MiniTest::Test
|
|
83
91
|
|
84
92
|
assert_raises(EOFError) { i.readpartial(1) }
|
85
93
|
end
|
94
|
+
|
95
|
+
# see https://github.com/digital-fabric/polyphony/issues/30
|
96
|
+
def test_reopened_tempfile
|
97
|
+
file = Tempfile.new
|
98
|
+
file << 'hello: world'
|
99
|
+
file.close
|
100
|
+
|
101
|
+
buf = nil
|
102
|
+
File.open(file, 'r:bom|utf-8') do |f|
|
103
|
+
buf = f.read(16384)
|
104
|
+
end
|
105
|
+
|
106
|
+
assert_equal 'hello: world', buf
|
107
|
+
end
|
86
108
|
end
|
87
109
|
|
88
110
|
class IOClassMethodsTest < MiniTest::Test
|
@@ -121,7 +143,7 @@ class IOClassMethodsTest < MiniTest::Test
|
|
121
143
|
assert_equal "end\n", lines[-1]
|
122
144
|
end
|
123
145
|
|
124
|
-
def
|
146
|
+
def test_read_class_method
|
125
147
|
s = IO.read(__FILE__)
|
126
148
|
assert_kind_of String, s
|
127
149
|
assert(!s.empty?)
|
@@ -144,7 +166,7 @@ class IOClassMethodsTest < MiniTest::Test
|
|
144
166
|
|
145
167
|
WRITE_DATA = "foo\nbar קוקו"
|
146
168
|
|
147
|
-
def
|
169
|
+
def test_write_class_method
|
148
170
|
fn = '/tmp/test_write'
|
149
171
|
FileUtils.rm(fn) rescue nil
|
150
172
|
|
data/test/test_queue.rb
CHANGED
@@ -8,7 +8,7 @@ class QueueTest < MiniTest::Test
|
|
8
8
|
@queue = Polyphony::Queue.new
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
11
|
+
def test_push_shift
|
12
12
|
spin {
|
13
13
|
@queue << 42
|
14
14
|
}
|
@@ -21,6 +21,18 @@ class QueueTest < MiniTest::Test
|
|
21
21
|
assert_equal [1, 2, 3, 4], buf
|
22
22
|
end
|
23
23
|
|
24
|
+
def test_unshift
|
25
|
+
@queue.push 1
|
26
|
+
@queue.push 2
|
27
|
+
@queue.push 3
|
28
|
+
@queue.unshift 4
|
29
|
+
|
30
|
+
buf = []
|
31
|
+
buf << @queue.shift while !@queue.empty?
|
32
|
+
|
33
|
+
assert_equal [4, 1, 2, 3], buf
|
34
|
+
end
|
35
|
+
|
24
36
|
def test_multiple_waiters
|
25
37
|
a = spin { @queue.shift }
|
26
38
|
b = spin { @queue.shift }
|
@@ -41,6 +53,19 @@ class QueueTest < MiniTest::Test
|
|
41
53
|
buf = []
|
42
54
|
@queue.shift_each { |i| buf << i }
|
43
55
|
assert_equal [1, 2, 3, 4], buf
|
56
|
+
|
57
|
+
buf = []
|
58
|
+
@queue.shift_each { |i| buf << i }
|
59
|
+
assert_equal [], buf
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_shift_all
|
63
|
+
(1..4).each { |i| @queue << i }
|
64
|
+
buf = @queue.shift_all
|
65
|
+
assert_equal [1, 2, 3, 4], buf
|
66
|
+
|
67
|
+
buf = @queue.shift_all
|
68
|
+
assert_equal [], buf
|
44
69
|
end
|
45
70
|
|
46
71
|
def test_empty?
|
@@ -71,4 +96,37 @@ class QueueTest < MiniTest::Test
|
|
71
96
|
assert_nil f2.await
|
72
97
|
assert_equal :bar, f3.await
|
73
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
|
74
132
|
end
|
data/test/test_resource_pool.rb
CHANGED
@@ -37,49 +37,6 @@ class ResourcePoolTest < MiniTest::Test
|
|
37
37
|
assert_equal 2, pool.size
|
38
38
|
end
|
39
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
|
81
|
-
end
|
82
|
-
|
83
40
|
def test_single_resource_limit
|
84
41
|
resources = [+'a', +'b']
|
85
42
|
pool = Polyphony::ResourcePool.new(limit: 1) { resources.shift }
|
data/test/test_trace.rb
CHANGED
@@ -34,7 +34,8 @@ class TraceTest < MiniTest::Test
|
|
34
34
|
|
35
35
|
def test_2_fiber_trace
|
36
36
|
records = []
|
37
|
-
|
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
|
-
[
|
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
|
-
[
|
58
|
-
[
|
59
|
-
[
|
60
|
-
[
|
61
|
-
[
|
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.
|
4
|
+
version: 0.43.10
|
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-
|
11
|
+
date: 2020-07-23 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,8 +354,11 @@ 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
|
361
|
+
- examples/performance/messaging.rb
|
356
362
|
- examples/performance/multi_snooze.rb
|
357
363
|
- examples/performance/snooze.rb
|
358
364
|
- examples/performance/snooze_raw.rb
|
@@ -366,6 +372,7 @@ files:
|
|
366
372
|
- examples/performance/xx-array.rb
|
367
373
|
- examples/performance/xx-fiber-switch.rb
|
368
374
|
- examples/performance/xx-snooze.rb
|
375
|
+
- examples/xx-spin.rb
|
369
376
|
- ext/libev/Changes
|
370
377
|
- ext/libev/LICENSE
|
371
378
|
- ext/libev/README
|
@@ -382,15 +389,19 @@ files:
|
|
382
389
|
- ext/libev/ev_win32.c
|
383
390
|
- ext/libev/ev_wrap.h
|
384
391
|
- ext/libev/test_libev_win32.c
|
392
|
+
- ext/polyphony/agent.h
|
393
|
+
- ext/polyphony/event.c
|
385
394
|
- ext/polyphony/extconf.rb
|
386
395
|
- ext/polyphony/fiber.c
|
387
396
|
- ext/polyphony/libev.c
|
388
397
|
- ext/polyphony/libev.h
|
389
398
|
- ext/polyphony/libev_agent.c
|
390
|
-
- ext/polyphony/libev_queue.c
|
391
399
|
- ext/polyphony/polyphony.c
|
392
400
|
- ext/polyphony/polyphony.h
|
393
401
|
- ext/polyphony/polyphony_ext.c
|
402
|
+
- ext/polyphony/queue.c
|
403
|
+
- ext/polyphony/ring_buffer.c
|
404
|
+
- ext/polyphony/ring_buffer.h
|
394
405
|
- ext/polyphony/thread.c
|
395
406
|
- ext/polyphony/tracing.c
|
396
407
|
- lib/polyphony.rb
|
@@ -407,7 +418,6 @@ files:
|
|
407
418
|
- lib/polyphony/core/sync.rb
|
408
419
|
- lib/polyphony/core/thread_pool.rb
|
409
420
|
- lib/polyphony/core/throttler.rb
|
410
|
-
- lib/polyphony/event.rb
|
411
421
|
- lib/polyphony/extensions/core.rb
|
412
422
|
- lib/polyphony/extensions/fiber.rb
|
413
423
|
- lib/polyphony/extensions/io.rb
|
@@ -420,6 +430,7 @@ files:
|
|
420
430
|
- test/coverage.rb
|
421
431
|
- test/eg.rb
|
422
432
|
- test/helper.rb
|
433
|
+
- test/q.rb
|
423
434
|
- test/run.rb
|
424
435
|
- test/stress.rb
|
425
436
|
- test/test_agent.rb
|
@@ -466,7 +477,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
466
477
|
- !ruby/object:Gem::Version
|
467
478
|
version: '0'
|
468
479
|
requirements: []
|
469
|
-
rubygems_version: 3.0.
|
480
|
+
rubygems_version: 3.0.8
|
470
481
|
signing_key:
|
471
482
|
specification_version: 4
|
472
483
|
summary: Fine grained concurrency for Ruby
|
data/ext/polyphony/libev_queue.c
DELETED
@@ -1,217 +0,0 @@
|
|
1
|
-
#include "polyphony.h"
|
2
|
-
|
3
|
-
struct async_watcher {
|
4
|
-
ev_async async;
|
5
|
-
struct ev_loop *ev_loop;
|
6
|
-
VALUE fiber;
|
7
|
-
};
|
8
|
-
|
9
|
-
struct async_queue {
|
10
|
-
struct async_watcher **queue;
|
11
|
-
unsigned int len;
|
12
|
-
unsigned int count;
|
13
|
-
unsigned int push_idx;
|
14
|
-
unsigned int pop_idx;
|
15
|
-
};
|
16
|
-
|
17
|
-
void async_queue_init(struct async_queue *queue) {
|
18
|
-
queue->len = 4;
|
19
|
-
queue->count = 0;
|
20
|
-
queue->queue = malloc(sizeof(struct async_watcher *) * queue->len);
|
21
|
-
queue->push_idx = 0;
|
22
|
-
queue->pop_idx = 0;
|
23
|
-
}
|
24
|
-
|
25
|
-
void async_queue_free(struct async_queue *queue) {
|
26
|
-
free(queue->queue);
|
27
|
-
}
|
28
|
-
|
29
|
-
void async_queue_push(struct async_queue *queue, struct async_watcher *watcher) {
|
30
|
-
if (queue->push_idx == queue->len) {
|
31
|
-
queue->len = queue->len * 2;
|
32
|
-
queue->queue = realloc(queue->queue, sizeof(struct async_watcher *) * queue->len);
|
33
|
-
}
|
34
|
-
if (queue->count == 0) {
|
35
|
-
queue->push_idx = 0;
|
36
|
-
queue->pop_idx = 0;
|
37
|
-
}
|
38
|
-
queue->count++;
|
39
|
-
queue->queue[queue->push_idx++] = watcher;
|
40
|
-
}
|
41
|
-
|
42
|
-
struct async_watcher *async_queue_pop(struct async_queue *queue) {
|
43
|
-
if (queue->count == 0) return 0;
|
44
|
-
|
45
|
-
queue->count--;
|
46
|
-
|
47
|
-
return queue->queue[queue->pop_idx++];
|
48
|
-
}
|
49
|
-
|
50
|
-
void async_queue_remove_at_idx(struct async_queue *queue, unsigned int remove_idx) {
|
51
|
-
queue->count--;
|
52
|
-
queue->push_idx--;
|
53
|
-
if (remove_idx < queue->push_idx)
|
54
|
-
memmove(
|
55
|
-
queue->queue + remove_idx,
|
56
|
-
queue->queue + remove_idx + 1,
|
57
|
-
(queue->push_idx - remove_idx) * sizeof(struct async_watcher *)
|
58
|
-
);
|
59
|
-
}
|
60
|
-
|
61
|
-
void async_queue_remove_by_fiber(struct async_queue *queue, VALUE fiber) {
|
62
|
-
if (queue->count == 0) return;
|
63
|
-
|
64
|
-
for (unsigned idx = queue->pop_idx; idx < queue->push_idx; idx++) {
|
65
|
-
if (queue->queue[idx]->fiber == fiber) {
|
66
|
-
async_queue_remove_at_idx(queue, idx);
|
67
|
-
return;
|
68
|
-
}
|
69
|
-
}
|
70
|
-
}
|
71
|
-
|
72
|
-
typedef struct queue {
|
73
|
-
VALUE items;
|
74
|
-
struct async_queue shift_queue;
|
75
|
-
} LibevQueue_t;
|
76
|
-
|
77
|
-
|
78
|
-
VALUE cLibevQueue = Qnil;
|
79
|
-
|
80
|
-
static void LibevQueue_mark(void *ptr) {
|
81
|
-
LibevQueue_t *queue = ptr;
|
82
|
-
rb_gc_mark(queue->items);
|
83
|
-
}
|
84
|
-
|
85
|
-
static void LibevQueue_free(void *ptr) {
|
86
|
-
LibevQueue_t *queue = ptr;
|
87
|
-
async_queue_free(&queue->shift_queue);
|
88
|
-
xfree(ptr);
|
89
|
-
}
|
90
|
-
|
91
|
-
static size_t LibevQueue_size(const void *ptr) {
|
92
|
-
return sizeof(LibevQueue_t);
|
93
|
-
}
|
94
|
-
|
95
|
-
static const rb_data_type_t LibevQueue_type = {
|
96
|
-
"Queue",
|
97
|
-
{LibevQueue_mark, LibevQueue_free, LibevQueue_size,},
|
98
|
-
0, 0, 0
|
99
|
-
};
|
100
|
-
|
101
|
-
static VALUE LibevQueue_allocate(VALUE klass) {
|
102
|
-
LibevQueue_t *queue;
|
103
|
-
|
104
|
-
queue = ALLOC(LibevQueue_t);
|
105
|
-
return TypedData_Wrap_Struct(klass, &LibevQueue_type, queue);
|
106
|
-
}
|
107
|
-
|
108
|
-
#define GetQueue(obj, queue) \
|
109
|
-
TypedData_Get_Struct((obj), LibevQueue_t, &LibevQueue_type, (queue))
|
110
|
-
|
111
|
-
static VALUE LibevQueue_initialize(VALUE self) {
|
112
|
-
LibevQueue_t *queue;
|
113
|
-
GetQueue(self, queue);
|
114
|
-
|
115
|
-
queue->items = rb_ary_new();
|
116
|
-
async_queue_init(&queue->shift_queue);
|
117
|
-
|
118
|
-
return self;
|
119
|
-
}
|
120
|
-
|
121
|
-
VALUE LibevQueue_push(VALUE self, VALUE value) {
|
122
|
-
LibevQueue_t *queue;
|
123
|
-
GetQueue(self, queue);
|
124
|
-
if (queue->shift_queue.count > 0) {
|
125
|
-
struct async_watcher *watcher = async_queue_pop(&queue->shift_queue);
|
126
|
-
if (watcher) {
|
127
|
-
ev_async_send(watcher->ev_loop, &watcher->async);
|
128
|
-
}
|
129
|
-
}
|
130
|
-
rb_ary_push(queue->items, value);
|
131
|
-
return self;
|
132
|
-
}
|
133
|
-
|
134
|
-
struct ev_loop *LibevAgent_ev_loop(VALUE self);
|
135
|
-
|
136
|
-
void async_queue_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
|
137
|
-
struct async_watcher *watcher = (struct async_watcher *)ev_async;
|
138
|
-
Fiber_make_runnable(watcher->fiber, Qnil);
|
139
|
-
}
|
140
|
-
|
141
|
-
VALUE libev_agent_await(VALUE self);
|
142
|
-
|
143
|
-
VALUE LibevQueue_shift(VALUE self) {
|
144
|
-
LibevQueue_t *queue;
|
145
|
-
GetQueue(self, queue);
|
146
|
-
|
147
|
-
if (RARRAY_LEN(queue->items) == 0) {
|
148
|
-
struct async_watcher watcher;
|
149
|
-
VALUE agent = rb_ivar_get(rb_thread_current(), ID_ivar_agent);
|
150
|
-
VALUE switchpoint_result = Qnil;
|
151
|
-
|
152
|
-
watcher.ev_loop = LibevAgent_ev_loop(agent);
|
153
|
-
watcher.fiber = rb_fiber_current();
|
154
|
-
async_queue_push(&queue->shift_queue, &watcher);
|
155
|
-
ev_async_init(&watcher.async, async_queue_callback);
|
156
|
-
ev_async_start(watcher.ev_loop, &watcher.async);
|
157
|
-
|
158
|
-
switchpoint_result = libev_agent_await(agent);
|
159
|
-
ev_async_stop(watcher.ev_loop, &watcher.async);
|
160
|
-
|
161
|
-
if (RTEST(rb_obj_is_kind_of(switchpoint_result, rb_eException))) {
|
162
|
-
async_queue_remove_by_fiber(&queue->shift_queue, watcher.fiber);
|
163
|
-
return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
164
|
-
}
|
165
|
-
RB_GC_GUARD(watcher.fiber);
|
166
|
-
RB_GC_GUARD(agent);
|
167
|
-
RB_GC_GUARD(switchpoint_result);
|
168
|
-
}
|
169
|
-
|
170
|
-
return rb_ary_shift(queue->items);
|
171
|
-
}
|
172
|
-
|
173
|
-
VALUE LibevQueue_shift_each(VALUE self) {
|
174
|
-
LibevQueue_t *queue;
|
175
|
-
VALUE old_queue;
|
176
|
-
GetQueue(self, queue);
|
177
|
-
old_queue = queue->items;
|
178
|
-
queue->items = rb_ary_new();
|
179
|
-
|
180
|
-
if (rb_block_given_p()) {
|
181
|
-
long len = RARRAY_LEN(old_queue);
|
182
|
-
long i;
|
183
|
-
for (i = 0; i < len; i++) {
|
184
|
-
rb_yield(RARRAY_AREF(old_queue, i));
|
185
|
-
}
|
186
|
-
RB_GC_GUARD(old_queue);
|
187
|
-
return self;
|
188
|
-
}
|
189
|
-
else {
|
190
|
-
RB_GC_GUARD(old_queue);
|
191
|
-
return old_queue;
|
192
|
-
}
|
193
|
-
}
|
194
|
-
|
195
|
-
VALUE LibevQueue_empty_p(VALUE self) {
|
196
|
-
LibevQueue_t *queue;
|
197
|
-
GetQueue(self, queue);
|
198
|
-
|
199
|
-
return (RARRAY_LEN(queue->items) == 0) ? Qtrue : Qfalse;
|
200
|
-
}
|
201
|
-
|
202
|
-
void Init_LibevQueue() {
|
203
|
-
cLibevQueue = rb_define_class_under(mPolyphony, "LibevQueue", rb_cData);
|
204
|
-
rb_define_alloc_func(cLibevQueue, LibevQueue_allocate);
|
205
|
-
|
206
|
-
rb_define_method(cLibevQueue, "initialize", LibevQueue_initialize, 0);
|
207
|
-
rb_define_method(cLibevQueue, "push", LibevQueue_push, 1);
|
208
|
-
rb_define_method(cLibevQueue, "<<", LibevQueue_push, 1);
|
209
|
-
|
210
|
-
rb_define_method(cLibevQueue, "pop", LibevQueue_shift, 0);
|
211
|
-
rb_define_method(cLibevQueue, "shift", LibevQueue_shift, 0);
|
212
|
-
|
213
|
-
rb_define_method(cLibevQueue, "shift_each", LibevQueue_shift_each, 0);
|
214
|
-
rb_define_method(cLibevQueue, "empty?", LibevQueue_empty_p, 0);
|
215
|
-
}
|
216
|
-
|
217
|
-
|