polyphony 0.49.1 → 0.52.0
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 +35 -0
- data/Gemfile.lock +7 -68
- data/TODO.md +6 -0
- data/examples/core/forking.rb +2 -2
- data/examples/core/nested.rb +21 -0
- data/examples/core/suspend.rb +13 -0
- data/examples/core/terminate_main_fiber.rb +12 -0
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +2 -4
- data/ext/polyphony/backend_common.h +58 -8
- data/ext/polyphony/backend_io_uring.c +158 -35
- data/ext/polyphony/backend_libev.c +192 -25
- data/ext/polyphony/event.c +1 -1
- data/ext/polyphony/extconf.rb +7 -2
- data/ext/polyphony/fiber.c +2 -1
- data/ext/polyphony/polyphony.c +94 -0
- data/ext/polyphony/polyphony.h +29 -2
- data/ext/polyphony/queue.c +1 -1
- data/ext/polyphony/runqueue.c +7 -1
- data/ext/polyphony/runqueue_ring_buffer.c +9 -0
- data/ext/polyphony/runqueue_ring_buffer.h +1 -0
- data/ext/polyphony/thread.c +14 -0
- data/lib/polyphony/adapters/irb.rb +1 -1
- data/lib/polyphony/adapters/mysql2.rb +1 -1
- data/lib/polyphony/adapters/postgres.rb +5 -5
- data/lib/polyphony/adapters/process.rb +4 -4
- data/lib/polyphony/core/exceptions.rb +1 -0
- data/lib/polyphony/core/global_api.rb +6 -6
- data/lib/polyphony/core/sync.rb +1 -1
- data/lib/polyphony/core/throttler.rb +1 -1
- data/lib/polyphony/core/timer.rb +63 -20
- data/lib/polyphony/extensions/core.rb +5 -5
- data/lib/polyphony/extensions/fiber.rb +11 -8
- data/lib/polyphony/extensions/io.rb +13 -22
- data/lib/polyphony/extensions/openssl.rb +6 -6
- data/lib/polyphony/extensions/socket.rb +41 -41
- data/lib/polyphony/extensions/thread.rb +1 -2
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +6 -5
- data/test/helper.rb +2 -3
- data/test/stress.rb +2 -0
- data/test/test_backend.rb +58 -5
- data/test/test_fiber.rb +31 -0
- data/test/test_global_api.rb +2 -2
- data/test/test_io.rb +84 -1
- data/test/test_kernel.rb +1 -1
- data/test/test_signal.rb +2 -3
- data/test/test_socket.rb +61 -0
- data/test/test_timer.rb +41 -8
- metadata +21 -60
data/ext/polyphony/polyphony.h
CHANGED
@@ -23,9 +23,9 @@
|
|
23
23
|
#define COND_TRACE(...) if (__tracing_enabled__) { TRACE(__VA_ARGS__); }
|
24
24
|
|
25
25
|
// exceptions
|
26
|
-
#define TEST_EXCEPTION(ret) (
|
26
|
+
#define TEST_EXCEPTION(ret) (rb_obj_is_kind_of(ret, rb_eException) == Qtrue)
|
27
27
|
#define RAISE_EXCEPTION(e) rb_funcall(e, ID_invoke, 0);
|
28
|
-
#define RAISE_IF_EXCEPTION(ret) if (
|
28
|
+
#define RAISE_IF_EXCEPTION(ret) if (rb_obj_is_kind_of(ret, rb_eException) == Qtrue) { RAISE_EXCEPTION(ret); }
|
29
29
|
#define RAISE_IF_NOT_NIL(ret) if (ret != Qnil) { RAISE_EXCEPTION(ret); }
|
30
30
|
|
31
31
|
// Fiber#transfer
|
@@ -84,10 +84,37 @@ void Runqueue_push(VALUE self, VALUE fiber, VALUE value, int reschedule);
|
|
84
84
|
void Runqueue_unshift(VALUE self, VALUE fiber, VALUE value, int reschedule);
|
85
85
|
runqueue_entry Runqueue_shift(VALUE self);
|
86
86
|
void Runqueue_delete(VALUE self, VALUE fiber);
|
87
|
+
int Runqueue_index_of(VALUE self, VALUE fiber);
|
87
88
|
void Runqueue_clear(VALUE self);
|
88
89
|
long Runqueue_len(VALUE self);
|
89
90
|
int Runqueue_empty_p(VALUE self);
|
90
91
|
|
92
|
+
#ifdef POLYPHONY_BACKEND_LIBEV
|
93
|
+
#define Backend_recv_loop Backend_read_loop
|
94
|
+
#define Backend_recv_feed_loop Backend_feed_loop
|
95
|
+
#endif
|
96
|
+
|
97
|
+
// Backend public interface
|
98
|
+
|
99
|
+
VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class);
|
100
|
+
VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class);
|
101
|
+
VALUE Backend_connect(VALUE self, VALUE io, VALUE addr, VALUE port);
|
102
|
+
VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method);
|
103
|
+
VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof);
|
104
|
+
VALUE Backend_read_loop(VALUE self, VALUE io);
|
105
|
+
VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length);
|
106
|
+
VALUE Backend_recv_loop(VALUE self, VALUE io);
|
107
|
+
VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method);
|
108
|
+
VALUE Backend_send(VALUE self, VALUE io, VALUE msg, VALUE flags);
|
109
|
+
VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
|
110
|
+
VALUE Backend_sleep(VALUE self, VALUE duration);
|
111
|
+
VALUE Backend_timeout(int argc,VALUE *argv, VALUE self);
|
112
|
+
VALUE Backend_timer_loop(VALUE self, VALUE interval);
|
113
|
+
VALUE Backend_wait_event(VALUE self, VALUE raise);
|
114
|
+
VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write);
|
115
|
+
VALUE Backend_waitpid(VALUE self, VALUE pid);
|
116
|
+
VALUE Backend_write_m(int argc, VALUE *argv, VALUE self);
|
117
|
+
|
91
118
|
unsigned int Backend_pending_count(VALUE self);
|
92
119
|
VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue);
|
93
120
|
VALUE Backend_wait_event(VALUE self, VALUE raise_on_exception);
|
data/ext/polyphony/queue.c
CHANGED
@@ -247,7 +247,7 @@ VALUE Queue_size_m(VALUE self) {
|
|
247
247
|
}
|
248
248
|
|
249
249
|
void Init_Queue() {
|
250
|
-
cQueue = rb_define_class_under(mPolyphony, "Queue",
|
250
|
+
cQueue = rb_define_class_under(mPolyphony, "Queue", rb_cObject);
|
251
251
|
rb_define_alloc_func(cQueue, Queue_allocate);
|
252
252
|
|
253
253
|
rb_define_method(cQueue, "initialize", Queue_initialize, -1);
|
data/ext/polyphony/runqueue.c
CHANGED
@@ -74,6 +74,12 @@ void Runqueue_delete(VALUE self, VALUE fiber) {
|
|
74
74
|
runqueue_ring_buffer_delete(&runqueue->entries, fiber);
|
75
75
|
}
|
76
76
|
|
77
|
+
int Runqueue_index_of(VALUE self, VALUE fiber) {
|
78
|
+
Runqueue_t *runqueue;
|
79
|
+
GetRunqueue(self, runqueue);
|
80
|
+
return runqueue_ring_buffer_index_of(&runqueue->entries, fiber);
|
81
|
+
}
|
82
|
+
|
77
83
|
void Runqueue_clear(VALUE self) {
|
78
84
|
Runqueue_t *runqueue;
|
79
85
|
GetRunqueue(self, runqueue);
|
@@ -95,7 +101,7 @@ int Runqueue_empty_p(VALUE self) {
|
|
95
101
|
}
|
96
102
|
|
97
103
|
void Init_Runqueue() {
|
98
|
-
cRunqueue = rb_define_class_under(mPolyphony, "Runqueue",
|
104
|
+
cRunqueue = rb_define_class_under(mPolyphony, "Runqueue", rb_cObject);
|
99
105
|
rb_define_alloc_func(cRunqueue, Runqueue_allocate);
|
100
106
|
|
101
107
|
rb_define_method(cRunqueue, "initialize", Runqueue_initialize, 0);
|
@@ -80,6 +80,15 @@ void runqueue_ring_buffer_delete(runqueue_ring_buffer *buffer, VALUE fiber) {
|
|
80
80
|
}
|
81
81
|
}
|
82
82
|
|
83
|
+
int runqueue_ring_buffer_index_of(runqueue_ring_buffer *buffer, VALUE fiber) {
|
84
|
+
for (unsigned int i = 0; i < buffer->count; i++) {
|
85
|
+
unsigned int idx = (buffer->head + i) % buffer->size;
|
86
|
+
if (buffer->entries[idx].fiber == fiber)
|
87
|
+
return i;
|
88
|
+
}
|
89
|
+
return -1;
|
90
|
+
}
|
91
|
+
|
83
92
|
void runqueue_ring_buffer_clear(runqueue_ring_buffer *buffer) {
|
84
93
|
buffer->count = buffer->head = buffer->tail = 0;
|
85
94
|
}
|
@@ -27,5 +27,6 @@ void runqueue_ring_buffer_unshift(runqueue_ring_buffer *buffer, VALUE fiber, VAL
|
|
27
27
|
void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value);
|
28
28
|
|
29
29
|
void runqueue_ring_buffer_delete(runqueue_ring_buffer *buffer, VALUE fiber);
|
30
|
+
int runqueue_ring_buffer_index_of(runqueue_ring_buffer *buffer, VALUE fiber);
|
30
31
|
|
31
32
|
#endif /* RUNQUEUE_RING_BUFFER_H */
|
data/ext/polyphony/thread.c
CHANGED
@@ -59,6 +59,18 @@ void schedule_fiber(VALUE self, VALUE fiber, VALUE value, int prioritize) {
|
|
59
59
|
}
|
60
60
|
}
|
61
61
|
|
62
|
+
VALUE Thread_fiber_scheduling_index(VALUE self, VALUE fiber) {
|
63
|
+
VALUE runqueue = rb_ivar_get(self, ID_ivar_runqueue);
|
64
|
+
|
65
|
+
return INT2NUM(Runqueue_index_of(runqueue, fiber));
|
66
|
+
}
|
67
|
+
|
68
|
+
VALUE Thread_fiber_unschedule(VALUE self, VALUE fiber) {
|
69
|
+
VALUE runqueue = rb_ivar_get(self, ID_ivar_runqueue);
|
70
|
+
Runqueue_delete(runqueue, fiber);
|
71
|
+
return self;
|
72
|
+
}
|
73
|
+
|
62
74
|
VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
|
63
75
|
schedule_fiber(self, fiber, value, 0);
|
64
76
|
return self;
|
@@ -135,6 +147,8 @@ void Init_Thread() {
|
|
135
147
|
rb_define_method(rb_cThread, "schedule_fiber_with_priority",
|
136
148
|
Thread_schedule_fiber_with_priority, 2);
|
137
149
|
rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
|
150
|
+
rb_define_method(rb_cThread, "fiber_scheduling_index", Thread_fiber_scheduling_index, 1);
|
151
|
+
rb_define_method(rb_cThread, "fiber_unschedule", Thread_fiber_unschedule, 1);
|
138
152
|
|
139
153
|
rb_define_method(rb_cThread, "debug!", Thread_debug, 0);
|
140
154
|
|
@@ -15,8 +15,8 @@ module ::PG
|
|
15
15
|
res = conn.connect_poll
|
16
16
|
case res
|
17
17
|
when PGRES_POLLING_FAILED then raise Error, conn.error_message
|
18
|
-
when PGRES_POLLING_READING then
|
19
|
-
when PGRES_POLLING_WRITING then
|
18
|
+
when PGRES_POLLING_READING then Polyphony.backend_wait_io(socket_io, false)
|
19
|
+
when PGRES_POLLING_WRITING then Polyphony.backend_wait_io(socket_io, true)
|
20
20
|
when PGRES_POLLING_OK then return conn.setnonblocking(true)
|
21
21
|
end
|
22
22
|
end
|
@@ -42,7 +42,7 @@ class ::PG::Connection
|
|
42
42
|
|
43
43
|
def get_result(&block)
|
44
44
|
while is_busy
|
45
|
-
|
45
|
+
Polyphony.backend_wait_io(socket_io, false)
|
46
46
|
consume_input
|
47
47
|
end
|
48
48
|
orig_get_result(&block)
|
@@ -59,7 +59,7 @@ class ::PG::Connection
|
|
59
59
|
|
60
60
|
def block(_timeout = 0)
|
61
61
|
while is_busy
|
62
|
-
|
62
|
+
Polyphony.backend_wait_io(socket_io, false)
|
63
63
|
consume_input
|
64
64
|
end
|
65
65
|
end
|
@@ -97,7 +97,7 @@ class ::PG::Connection
|
|
97
97
|
return move_on_after(timeout) { wait_for_notify(&block) } if timeout
|
98
98
|
|
99
99
|
while true
|
100
|
-
|
100
|
+
Polyphony.backend_wait_io(socket_io, false)
|
101
101
|
consume_input
|
102
102
|
notice = notifies
|
103
103
|
next unless notice
|
@@ -7,7 +7,7 @@ module Polyphony
|
|
7
7
|
def watch(cmd = nil, &block)
|
8
8
|
terminated = nil
|
9
9
|
pid = cmd ? Kernel.spawn(cmd) : Polyphony.fork(&block)
|
10
|
-
|
10
|
+
Polyphony.backend_waitpid(pid)
|
11
11
|
terminated = true
|
12
12
|
ensure
|
13
13
|
kill_process(pid) unless terminated || pid.nil?
|
@@ -23,9 +23,9 @@ module Polyphony
|
|
23
23
|
|
24
24
|
def kill_and_await(sig, pid)
|
25
25
|
::Process.kill(sig, pid)
|
26
|
-
|
27
|
-
rescue Errno::
|
28
|
-
#
|
26
|
+
Polyphony.backend_waitpid(pid)
|
27
|
+
rescue Errno::ESRCH
|
28
|
+
# process doesn't exist
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -21,7 +21,7 @@ module Polyphony
|
|
21
21
|
elsif block.arity > 0
|
22
22
|
cancel_after_with_block(Fiber.current, interval, with_exception, &block)
|
23
23
|
else
|
24
|
-
|
24
|
+
Polyphony.backend_timeout(interval, with_exception, &block)
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -29,7 +29,7 @@ module Polyphony
|
|
29
29
|
spin do
|
30
30
|
sleep interval
|
31
31
|
exception = cancel_exception(with_exception)
|
32
|
-
exception.
|
32
|
+
exception.raising_fiber = nil
|
33
33
|
fiber.schedule exception
|
34
34
|
end
|
35
35
|
end
|
@@ -82,7 +82,7 @@ module Polyphony
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def every(interval, &block)
|
85
|
-
|
85
|
+
Polyphony.backend_timer_loop(interval, &block)
|
86
86
|
end
|
87
87
|
|
88
88
|
def move_on_after(interval, with_value: nil, &block)
|
@@ -91,7 +91,7 @@ module Polyphony
|
|
91
91
|
elsif block.arity > 0
|
92
92
|
move_on_after_with_block(Fiber.current, interval, with_value, &block)
|
93
93
|
else
|
94
|
-
|
94
|
+
Polyphony.backend_timeout(interval, nil, with_value, &block)
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
@@ -129,11 +129,11 @@ module Polyphony
|
|
129
129
|
def sleep(duration = nil)
|
130
130
|
return sleep_forever unless duration
|
131
131
|
|
132
|
-
|
132
|
+
Polyphony.backend_sleep duration
|
133
133
|
end
|
134
134
|
|
135
135
|
def sleep_forever
|
136
|
-
|
136
|
+
Polyphony.backend_wait_event(true)
|
137
137
|
end
|
138
138
|
|
139
139
|
def throttled_loop(rate = nil, **opts, &block)
|
data/lib/polyphony/core/sync.rb
CHANGED
data/lib/polyphony/core/timer.rb
CHANGED
@@ -11,12 +11,45 @@ module Polyphony
|
|
11
11
|
def stop
|
12
12
|
@fiber.stop
|
13
13
|
end
|
14
|
+
|
15
|
+
def sleep(duration)
|
16
|
+
fiber = Fiber.current
|
17
|
+
@timeouts[fiber] = {
|
18
|
+
interval: duration,
|
19
|
+
target_stamp: now + duration
|
20
|
+
}
|
21
|
+
Polyphony.backend_wait_event(true)
|
22
|
+
ensure
|
23
|
+
@timeouts.delete(fiber)
|
24
|
+
end
|
25
|
+
|
26
|
+
def after(interval, &block)
|
27
|
+
spin do
|
28
|
+
self.sleep interval
|
29
|
+
block.()
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def every(interval)
|
34
|
+
fiber = Fiber.current
|
35
|
+
@timeouts[fiber] = {
|
36
|
+
interval: interval,
|
37
|
+
target_stamp: now + interval,
|
38
|
+
recurring: true
|
39
|
+
}
|
40
|
+
while true
|
41
|
+
Polyphony.backend_wait_event(true)
|
42
|
+
yield
|
43
|
+
end
|
44
|
+
ensure
|
45
|
+
@timeouts.delete(fiber)
|
46
|
+
end
|
14
47
|
|
15
|
-
def cancel_after(
|
48
|
+
def cancel_after(interval, with_exception: Polyphony::Cancel)
|
16
49
|
fiber = Fiber.current
|
17
50
|
@timeouts[fiber] = {
|
18
|
-
|
19
|
-
target_stamp:
|
51
|
+
interval: interval,
|
52
|
+
target_stamp: now + interval,
|
20
53
|
exception: with_exception
|
21
54
|
}
|
22
55
|
yield
|
@@ -24,12 +57,12 @@ module Polyphony
|
|
24
57
|
@timeouts.delete(fiber)
|
25
58
|
end
|
26
59
|
|
27
|
-
def move_on_after(
|
60
|
+
def move_on_after(interval, with_value: nil)
|
28
61
|
fiber = Fiber.current
|
29
62
|
@timeouts[fiber] = {
|
30
|
-
|
31
|
-
target_stamp:
|
32
|
-
|
63
|
+
interval: interval,
|
64
|
+
target_stamp: now + interval,
|
65
|
+
exception: [Polyphony::MoveOn, with_value]
|
33
66
|
}
|
34
67
|
yield
|
35
68
|
rescue Polyphony::MoveOn => e
|
@@ -37,36 +70,46 @@ module Polyphony
|
|
37
70
|
ensure
|
38
71
|
@timeouts.delete(fiber)
|
39
72
|
end
|
40
|
-
|
73
|
+
|
41
74
|
def reset
|
42
75
|
record = @timeouts[Fiber.current]
|
43
76
|
return unless record
|
44
77
|
|
45
|
-
record[:target_stamp] =
|
78
|
+
record[:target_stamp] = now + record[:interval]
|
46
79
|
end
|
47
|
-
|
80
|
+
|
48
81
|
private
|
49
82
|
|
83
|
+
def now
|
84
|
+
::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
85
|
+
end
|
86
|
+
|
50
87
|
def timeout_exception(record)
|
51
88
|
case (exception = record[:exception])
|
52
|
-
when
|
53
|
-
|
54
|
-
when
|
55
|
-
|
89
|
+
when Array
|
90
|
+
exception[0].new(exception[1])
|
91
|
+
when Class
|
92
|
+
exception.new
|
93
|
+
else
|
94
|
+
RuntimeError.new(exception)
|
56
95
|
end
|
57
96
|
end
|
58
97
|
|
59
98
|
def update
|
60
|
-
|
61
|
-
|
99
|
+
return if @timeouts.empty?
|
100
|
+
|
62
101
|
@timeouts.each do |fiber, record|
|
63
102
|
next if record[:target_stamp] > now
|
64
103
|
|
65
|
-
|
66
|
-
|
67
|
-
|
104
|
+
value = record[:exception] ? timeout_exception(record) : record[:value]
|
105
|
+
fiber.schedule value
|
106
|
+
|
107
|
+
next unless record[:recurring]
|
108
|
+
|
109
|
+
while record[:target_stamp] <= now
|
110
|
+
record[:target_stamp] += record[:interval]
|
111
|
+
end
|
68
112
|
end
|
69
|
-
# elapsed&.each { |f| @timeouts.delete(f) }
|
70
113
|
end
|
71
114
|
end
|
72
115
|
end
|
@@ -12,11 +12,11 @@ class ::Exception
|
|
12
12
|
attr_accessor :__disable_sanitized_backtrace__
|
13
13
|
end
|
14
14
|
|
15
|
-
attr_accessor :source_fiber, :
|
15
|
+
attr_accessor :source_fiber, :raising_fiber
|
16
16
|
|
17
17
|
alias_method :orig_initialize, :initialize
|
18
18
|
def initialize(*args)
|
19
|
-
@
|
19
|
+
@raising_fiber = Fiber.current
|
20
20
|
orig_initialize(*args)
|
21
21
|
end
|
22
22
|
|
@@ -31,10 +31,10 @@ class ::Exception
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def sanitized_backtrace
|
34
|
-
return sanitize(orig_backtrace) unless @
|
34
|
+
return sanitize(orig_backtrace) unless @raising_fiber
|
35
35
|
|
36
36
|
backtrace = orig_backtrace || []
|
37
|
-
sanitize(backtrace + @
|
37
|
+
sanitize(backtrace + @raising_fiber.caller)
|
38
38
|
end
|
39
39
|
|
40
40
|
POLYPHONY_DIR = File.expand_path(File.join(__dir__, '..'))
|
@@ -55,7 +55,7 @@ module ::Process
|
|
55
55
|
class << self
|
56
56
|
alias_method :orig_detach, :detach
|
57
57
|
def detach(pid)
|
58
|
-
fiber = spin {
|
58
|
+
fiber = spin { Polyphony.backend_waitpid(pid) }
|
59
59
|
fiber.define_singleton_method(:pid) { pid }
|
60
60
|
fiber
|
61
61
|
end
|