polyphony 0.43.10 → 0.45.2
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/.rubocop.yml +8 -1
- data/CHANGELOG.md +37 -0
- data/Gemfile.lock +16 -6
- data/Rakefile +1 -1
- data/TODO.md +15 -10
- data/docs/_posts/2020-07-26-polyphony-0.44.md +77 -0
- data/docs/api-reference/thread.md +1 -1
- data/docs/getting-started/overview.md +14 -14
- data/docs/getting-started/tutorial.md +1 -1
- data/examples/adapters/redis_client.rb +3 -1
- data/examples/adapters/redis_pubsub_perf.rb +11 -8
- data/examples/adapters/sequel_mysql.rb +23 -0
- data/examples/adapters/sequel_mysql_pool.rb +33 -0
- data/examples/adapters/sequel_pg.rb +24 -0
- data/examples/core/{02-awaiting-fibers.rb → await.rb} +0 -0
- data/examples/core/{xx-channels.rb → channels.rb} +0 -0
- data/examples/core/deferring-an-operation.rb +16 -0
- data/examples/core/{xx-erlang-style-genserver.rb → erlang-style-genserver.rb} +16 -9
- data/examples/core/{xx-forking.rb → forking.rb} +1 -1
- data/examples/core/handling-signals.rb +11 -0
- data/examples/core/{03-interrupting.rb → interrupt.rb} +0 -0
- data/examples/core/{xx-pingpong.rb → pingpong.rb} +7 -5
- data/examples/core/{xx-recurrent-timer.rb → recurrent-timer.rb} +1 -1
- data/examples/core/{xx-resource_delegate.rb → resource_delegate.rb} +3 -4
- data/examples/core/{01-spinning-up-fibers.rb → spin.rb} +1 -1
- data/examples/core/{xx-spin_error_backtrace.rb → spin_error_backtrace.rb} +1 -1
- data/examples/core/{xx-supervise-process.rb → supervise-process.rb} +8 -5
- data/examples/core/supervisor.rb +20 -0
- data/examples/core/{xx-thread-sleep.rb → thread-sleep.rb} +0 -0
- data/examples/core/{xx-thread_pool.rb → thread_pool.rb} +0 -0
- data/examples/core/{xx-throttling.rb → throttling.rb} +0 -0
- data/examples/core/{xx-timeout.rb → timeout.rb} +0 -0
- data/examples/core/{xx-using-a-mutex.rb → using-a-mutex.rb} +0 -0
- data/examples/core/{xx-worker-thread.rb → worker-thread.rb} +2 -2
- data/examples/io/{xx-backticks.rb → backticks.rb} +0 -0
- data/examples/io/{xx-echo_client.rb → echo_client.rb} +1 -1
- data/examples/io/{xx-echo_client_from_stdin.rb → echo_client_from_stdin.rb} +2 -2
- data/examples/io/{xx-echo_pipe.rb → echo_pipe.rb} +1 -1
- data/examples/io/{xx-echo_server.rb → echo_server.rb} +0 -0
- data/examples/io/{xx-echo_server_with_timeout.rb → echo_server_with_timeout.rb} +1 -1
- data/examples/io/{xx-echo_stdin.rb → echo_stdin.rb} +0 -0
- data/examples/io/{xx-happy-eyeballs.rb → happy-eyeballs.rb} +0 -0
- data/examples/io/{xx-httparty.rb → httparty.rb} +4 -13
- data/examples/io/{xx-irb.rb → irb.rb} +0 -0
- data/examples/io/{xx-net-http.rb → net-http.rb} +0 -0
- data/examples/io/{xx-open.rb → open.rb} +0 -0
- data/examples/io/pry.rb +18 -0
- data/examples/io/rack_server.rb +71 -0
- data/examples/io/{xx-system.rb → system.rb} +1 -1
- data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
- data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
- data/examples/io/tunnel.rb +6 -1
- data/examples/io/{xx-zip.rb → zip.rb} +0 -0
- data/examples/performance/fiber_transfer.rb +2 -1
- data/examples/performance/fs_read.rb +5 -6
- data/examples/{io/xx-switch.rb → performance/switch.rb} +2 -1
- data/examples/performance/thread-vs-fiber/{xx-httparty_multi.rb → httparty_multi.rb} +3 -4
- data/examples/performance/thread-vs-fiber/{xx-httparty_threaded.rb → httparty_threaded.rb} +0 -0
- data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
- data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
- data/examples/performance/thread_pool_perf.rb +6 -7
- data/ext/polyphony/backend.h +40 -0
- data/ext/polyphony/event.c +3 -3
- data/ext/polyphony/extconf.rb +1 -1
- data/ext/polyphony/fiber.c +66 -6
- data/ext/polyphony/{libev_agent.c → libev_backend.c} +239 -235
- data/ext/polyphony/polyphony.c +3 -3
- data/ext/polyphony/polyphony.h +15 -23
- data/ext/polyphony/polyphony_ext.c +3 -4
- data/ext/polyphony/queue.c +25 -12
- data/ext/polyphony/ring_buffer.c +0 -1
- data/ext/polyphony/thread.c +36 -33
- data/lib/polyphony.rb +25 -38
- data/lib/polyphony/adapters/fs.rb +1 -1
- data/lib/polyphony/adapters/irb.rb +2 -17
- data/lib/polyphony/adapters/mysql2.rb +19 -0
- data/lib/polyphony/adapters/postgres.rb +5 -5
- data/lib/polyphony/adapters/process.rb +2 -2
- data/lib/polyphony/adapters/readline.rb +17 -0
- data/lib/polyphony/adapters/redis.rb +1 -1
- data/lib/polyphony/adapters/sequel.rb +45 -0
- data/lib/polyphony/core/exceptions.rb +11 -0
- data/lib/polyphony/core/global_api.rb +17 -12
- data/lib/polyphony/core/resource_pool.rb +20 -7
- data/lib/polyphony/core/sync.rb +46 -8
- data/lib/polyphony/core/throttler.rb +1 -1
- data/lib/polyphony/extensions/core.rb +38 -25
- data/lib/polyphony/extensions/fiber.rb +12 -45
- data/lib/polyphony/extensions/io.rb +45 -12
- data/lib/polyphony/extensions/openssl.rb +6 -6
- data/lib/polyphony/extensions/socket.rb +22 -15
- data/lib/polyphony/extensions/thread.rb +6 -5
- data/lib/polyphony/net.rb +2 -1
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +7 -3
- data/test/helper.rb +1 -1
- data/test/{test_agent.rb → test_backend.rb} +22 -22
- data/test/test_fiber.rb +28 -11
- data/test/test_io.rb +17 -1
- data/test/test_kernel.rb +5 -0
- data/test/test_resource_pool.rb +50 -16
- data/test/test_signal.rb +5 -29
- data/test/test_socket.rb +17 -0
- data/test/test_sync.rb +52 -0
- metadata +126 -98
- data/.gitbook.yaml +0 -4
- data/examples/adapters/concurrent-ruby.rb +0 -9
- data/examples/core/04-handling-signals.rb +0 -19
- data/examples/core/xx-agent.rb +0 -102
- data/examples/core/xx-at_exit.rb +0 -29
- data/examples/core/xx-caller.rb +0 -12
- data/examples/core/xx-daemon.rb +0 -14
- data/examples/core/xx-deadlock.rb +0 -8
- data/examples/core/xx-deferring-an-operation.rb +0 -14
- data/examples/core/xx-exception-backtrace.rb +0 -40
- data/examples/core/xx-fork-cleanup.rb +0 -22
- data/examples/core/xx-fork-spin.rb +0 -42
- data/examples/core/xx-fork-terminate.rb +0 -27
- data/examples/core/xx-move_on.rb +0 -23
- data/examples/core/xx-queue-async.rb +0 -120
- data/examples/core/xx-readpartial.rb +0 -18
- data/examples/core/xx-signals.rb +0 -16
- data/examples/core/xx-sleep-forever.rb +0 -9
- data/examples/core/xx-sleeping.rb +0 -25
- data/examples/core/xx-snooze-starve.rb +0 -16
- data/examples/core/xx-spin-fork.rb +0 -49
- data/examples/core/xx-state-machine.rb +0 -51
- data/examples/core/xx-stop.rb +0 -20
- data/examples/core/xx-supervisors.rb +0 -21
- data/examples/core/xx-thread-selector-sleep.rb +0 -51
- data/examples/core/xx-thread-selector-snooze.rb +0 -46
- data/examples/core/xx-thread-snooze.rb +0 -34
- data/examples/core/xx-timer-gc.rb +0 -17
- data/examples/core/xx-trace.rb +0 -79
- data/examples/performance/xx-array.rb +0 -11
- data/examples/performance/xx-fiber-switch.rb +0 -9
- data/examples/performance/xx-snooze.rb +0 -15
- data/examples/xx-spin.rb +0 -32
- data/ext/polyphony/agent.h +0 -39
|
File without changes
|
|
File without changes
|
data/examples/io/tunnel.rb
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
require 'bundler/setup'
|
|
4
4
|
require 'polyphony'
|
|
5
5
|
|
|
6
|
+
if ARGV.size < 2
|
|
7
|
+
puts "Usage: ruby examples/tunnel.rb <port1> <port2>"
|
|
8
|
+
exit
|
|
9
|
+
end
|
|
10
|
+
|
|
6
11
|
Ports = ARGV[0..1]
|
|
7
12
|
EndPoints = []
|
|
8
13
|
|
|
@@ -24,7 +29,7 @@ def endpoint_loop(idx, peer_idx)
|
|
|
24
29
|
conn.binmode
|
|
25
30
|
EndPoints[idx] = conn
|
|
26
31
|
log "Client connected on port #{port} (#{conn.remote_address.inspect})"
|
|
27
|
-
|
|
32
|
+
conn.read_loop do |data|
|
|
28
33
|
peer = EndPoints[peer_idx]
|
|
29
34
|
if peer
|
|
30
35
|
peer << data
|
|
File without changes
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'bundler/setup'
|
|
4
4
|
require 'polyphony'
|
|
5
|
-
require 'polyphony/fs'
|
|
5
|
+
require 'polyphony/adapters/fs'
|
|
6
6
|
|
|
7
7
|
def raw_read_file(x)
|
|
8
8
|
t0 = Time.now
|
|
@@ -14,7 +14,7 @@ def threaded_read_file(x, y)
|
|
|
14
14
|
t0 = Time.now
|
|
15
15
|
threads = []
|
|
16
16
|
y.times do
|
|
17
|
-
threads << Thread.new { x.times { IO.orig_read(
|
|
17
|
+
threads << Thread.new { x.times { IO.orig_read(__FILE__) } }
|
|
18
18
|
end
|
|
19
19
|
threads.each(&:join)
|
|
20
20
|
puts "threaded_read_file: #{Time.now - t0}"
|
|
@@ -22,11 +22,10 @@ end
|
|
|
22
22
|
|
|
23
23
|
def thread_pool_read_file(x, y)
|
|
24
24
|
t0 = Time.now
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
s.spin { x.times { IO.read(PATH) } }
|
|
28
|
-
end
|
|
25
|
+
y.times do
|
|
26
|
+
spin { x.times { IO.read(__FILE__) } }
|
|
29
27
|
end
|
|
28
|
+
Fiber.current.await_all_children
|
|
30
29
|
puts "thread_pool_read_file: #{Time.now - t0}"
|
|
31
30
|
end
|
|
32
31
|
|
|
File without changes
|
|
@@ -24,7 +24,7 @@ def handle_client(socket)
|
|
|
24
24
|
parser.on_message_complete = proc do |env|
|
|
25
25
|
reqs << Object.new # parser
|
|
26
26
|
end
|
|
27
|
-
|
|
27
|
+
socket.read_loop do |data|
|
|
28
28
|
parser << data
|
|
29
29
|
while (req = reqs.shift)
|
|
30
30
|
handle_request(socket, req)
|
|
@@ -11,11 +11,7 @@ def handle_client(client)
|
|
|
11
11
|
headers = "Content-Length: #{data.bytesize}\r\n"
|
|
12
12
|
client.write "HTTP/1.1 #{status_code}\r\n#{headers}\r\n#{data}"
|
|
13
13
|
end
|
|
14
|
-
|
|
15
|
-
while data = client.readpartial(8192) rescue nil
|
|
16
|
-
parser << data
|
|
17
|
-
end
|
|
18
|
-
end
|
|
14
|
+
client.read_loop { |data| parser << data }
|
|
19
15
|
client.close
|
|
20
16
|
end
|
|
21
17
|
end
|
|
@@ -10,7 +10,7 @@ def lengthy_op
|
|
|
10
10
|
# Digest::SHA256.digest(IO.read('doc/Promise.html'))
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
X =
|
|
13
|
+
X = 10000
|
|
14
14
|
|
|
15
15
|
def compare_performance
|
|
16
16
|
t0 = Time.now
|
|
@@ -35,20 +35,19 @@ def compare_performance
|
|
|
35
35
|
|
|
36
36
|
acc = 0
|
|
37
37
|
count = 0
|
|
38
|
-
|
|
38
|
+
1.times do |_i|
|
|
39
39
|
t0 = Time.now
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
s.spin { Polyphony::ThreadPool.process { lengthy_op } }
|
|
43
|
-
end
|
|
40
|
+
X.times do
|
|
41
|
+
spin { Polyphony::ThreadPool.process { lengthy_op } }
|
|
44
42
|
end
|
|
43
|
+
Fiber.current.await_all_children
|
|
45
44
|
thread_pool_perf = X / (Time.now - t0)
|
|
46
45
|
acc += thread_pool_perf
|
|
47
46
|
count += 1
|
|
48
47
|
end
|
|
49
48
|
avg_perf = acc / count
|
|
50
49
|
puts format(
|
|
51
|
-
'
|
|
50
|
+
'spin X thread pool performance: %g (X %0.2f)',
|
|
52
51
|
avg_perf,
|
|
53
52
|
avg_perf / native_perf
|
|
54
53
|
)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#ifndef BACKEND_H
|
|
2
|
+
#define BACKEND_H
|
|
3
|
+
|
|
4
|
+
#include "ruby.h"
|
|
5
|
+
|
|
6
|
+
// backend interface function signatures
|
|
7
|
+
|
|
8
|
+
// VALUE LibevBackend_accept(VALUE self, VALUE sock);
|
|
9
|
+
// VALUE LibevBackend_accept_loop(VALUE self, VALUE sock);
|
|
10
|
+
// VALUE LibevBackend_connect(VALUE self, VALUE sock, VALUE host, VALUE port);
|
|
11
|
+
// VALUE LibevBackend_finalize(VALUE self);
|
|
12
|
+
// VALUE LibevBackend_post_fork(VALUE self);
|
|
13
|
+
// VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof);
|
|
14
|
+
// VALUE LibevBackend_read_loop(VALUE self, VALUE io);
|
|
15
|
+
// VALUE LibevBackend_sleep(VALUE self, VALUE duration);
|
|
16
|
+
// VALUE LibevBackend_wait_io(VALUE self, VALUE io, VALUE write);
|
|
17
|
+
// VALUE LibevBackend_wait_pid(VALUE self, VALUE pid);
|
|
18
|
+
// VALUE LibevBackend_write(int argc, VALUE *argv, VALUE self);
|
|
19
|
+
|
|
20
|
+
typedef VALUE (* backend_pending_count_t)(VALUE self);
|
|
21
|
+
typedef VALUE (*backend_poll_t)(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue);
|
|
22
|
+
typedef VALUE (* backend_ref_t)(VALUE self);
|
|
23
|
+
typedef int (* backend_ref_count_t)(VALUE self);
|
|
24
|
+
typedef void (* backend_reset_ref_count_t)(VALUE self);
|
|
25
|
+
typedef VALUE (* backend_unref_t)(VALUE self);
|
|
26
|
+
typedef VALUE (* backend_wait_event_t)(VALUE self, VALUE raise_on_exception);
|
|
27
|
+
typedef VALUE (* backend_wakeup_t)(VALUE self);
|
|
28
|
+
|
|
29
|
+
typedef struct backend_interface {
|
|
30
|
+
backend_pending_count_t pending_count;
|
|
31
|
+
backend_poll_t poll;
|
|
32
|
+
backend_ref_t ref;
|
|
33
|
+
backend_ref_count_t ref_count;
|
|
34
|
+
backend_reset_ref_count_t reset_ref_count;
|
|
35
|
+
backend_unref_t unref;
|
|
36
|
+
backend_wait_event_t wait_event;
|
|
37
|
+
backend_wakeup_t wakeup;
|
|
38
|
+
} backend_interface_t;
|
|
39
|
+
|
|
40
|
+
#endif /* BACKEND_H */
|
data/ext/polyphony/event.c
CHANGED
|
@@ -64,13 +64,13 @@ VALUE Event_await(VALUE self) {
|
|
|
64
64
|
if (event->waiting_fiber != Qnil)
|
|
65
65
|
rb_raise(rb_eRuntimeError, "Event is already awaited by another fiber");
|
|
66
66
|
|
|
67
|
-
VALUE
|
|
67
|
+
VALUE backend = rb_ivar_get(rb_thread_current(), ID_ivar_backend);
|
|
68
68
|
event->waiting_fiber = rb_fiber_current();
|
|
69
|
-
VALUE switchpoint_result =
|
|
69
|
+
VALUE switchpoint_result = __BACKEND__.wait_event(backend, Qnil);
|
|
70
70
|
event->waiting_fiber = Qnil;
|
|
71
71
|
|
|
72
72
|
TEST_RESUME_EXCEPTION(switchpoint_result);
|
|
73
|
-
RB_GC_GUARD(
|
|
73
|
+
RB_GC_GUARD(backend);
|
|
74
74
|
RB_GC_GUARD(switchpoint_result);
|
|
75
75
|
|
|
76
76
|
return switchpoint_result;
|
data/ext/polyphony/extconf.rb
CHANGED
data/ext/polyphony/fiber.c
CHANGED
|
@@ -2,12 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
ID ID_fiber_trace;
|
|
4
4
|
ID ID_ivar_auto_watcher;
|
|
5
|
-
ID
|
|
6
|
-
ID
|
|
7
|
-
ID
|
|
8
|
-
ID ID_trace_runnable;
|
|
9
|
-
ID ID_trace_terminate;
|
|
10
|
-
ID ID_trace_wait;
|
|
5
|
+
ID ID_ivar_mailbox;
|
|
6
|
+
ID ID_ivar_result;
|
|
7
|
+
ID ID_ivar_waiting_fibers;
|
|
11
8
|
|
|
12
9
|
VALUE SYM_dead;
|
|
13
10
|
VALUE SYM_running;
|
|
@@ -67,6 +64,57 @@ void Fiber_make_runnable(VALUE fiber, VALUE value) {
|
|
|
67
64
|
}
|
|
68
65
|
}
|
|
69
66
|
|
|
67
|
+
VALUE Fiber_await(VALUE self) {
|
|
68
|
+
VALUE result;
|
|
69
|
+
|
|
70
|
+
// we compare with false, since a fiber that has not yet started will have
|
|
71
|
+
// @running set to nil
|
|
72
|
+
if (rb_ivar_get(self, ID_ivar_running) == Qfalse) {
|
|
73
|
+
result = rb_ivar_get(self, ID_ivar_result);
|
|
74
|
+
TEST_RESUME_EXCEPTION(result);
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
VALUE fiber = rb_fiber_current();
|
|
79
|
+
VALUE waiting_fibers = rb_ivar_get(self, ID_ivar_waiting_fibers);
|
|
80
|
+
if (waiting_fibers == Qnil) {
|
|
81
|
+
waiting_fibers = rb_hash_new();
|
|
82
|
+
rb_ivar_set(self, ID_ivar_waiting_fibers, waiting_fibers);
|
|
83
|
+
}
|
|
84
|
+
rb_hash_aset(waiting_fibers, fiber, Qtrue);
|
|
85
|
+
|
|
86
|
+
result = Thread_switch_fiber(rb_thread_current());
|
|
87
|
+
|
|
88
|
+
rb_hash_delete(waiting_fibers, fiber);
|
|
89
|
+
TEST_RESUME_EXCEPTION(result);
|
|
90
|
+
RB_GC_GUARD(result);
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
VALUE Fiber_send(VALUE self, VALUE value) {
|
|
95
|
+
VALUE mailbox = rb_ivar_get(self, ID_ivar_mailbox);
|
|
96
|
+
if (mailbox == Qnil) {
|
|
97
|
+
mailbox = rb_funcall(cQueue, ID_new, 0);
|
|
98
|
+
rb_ivar_set(self, ID_ivar_mailbox, mailbox);
|
|
99
|
+
}
|
|
100
|
+
Queue_push(mailbox, value);
|
|
101
|
+
return self;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
VALUE Fiber_receive(VALUE self) {
|
|
105
|
+
VALUE mailbox = rb_ivar_get(self, ID_ivar_mailbox);
|
|
106
|
+
if (mailbox == Qnil) {
|
|
107
|
+
mailbox = rb_funcall(cQueue, ID_new, 0);
|
|
108
|
+
rb_ivar_set(self, ID_ivar_mailbox, mailbox);
|
|
109
|
+
}
|
|
110
|
+
return Queue_shift(mailbox);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
VALUE Fiber_receive_all_pending(VALUE self) {
|
|
114
|
+
VALUE mailbox = rb_ivar_get(self, ID_ivar_mailbox);
|
|
115
|
+
return (mailbox == Qnil) ? rb_ary_new() : Queue_shift_all(mailbox);
|
|
116
|
+
}
|
|
117
|
+
|
|
70
118
|
void Init_Fiber() {
|
|
71
119
|
VALUE cFiber = rb_const_get(rb_cObject, rb_intern("Fiber"));
|
|
72
120
|
rb_define_method(cFiber, "safe_transfer", Fiber_safe_transfer, -1);
|
|
@@ -74,6 +122,15 @@ void Init_Fiber() {
|
|
|
74
122
|
rb_define_method(cFiber, "state", Fiber_state, 0);
|
|
75
123
|
rb_define_method(cFiber, "auto_watcher", Fiber_auto_watcher, 0);
|
|
76
124
|
|
|
125
|
+
rb_define_method(cFiber, "await", Fiber_await, 0);
|
|
126
|
+
rb_define_method(cFiber, "join", Fiber_await, 0);
|
|
127
|
+
|
|
128
|
+
rb_define_method(cFiber, "<<", Fiber_send, 1);
|
|
129
|
+
rb_define_method(cFiber, "send", Fiber_send, 1);
|
|
130
|
+
|
|
131
|
+
rb_define_method(cFiber, "receive", Fiber_receive, 0);
|
|
132
|
+
rb_define_method(cFiber, "receive_all_pending", Fiber_receive_all_pending, 0);
|
|
133
|
+
|
|
77
134
|
SYM_dead = ID2SYM(rb_intern("dead"));
|
|
78
135
|
SYM_running = ID2SYM(rb_intern("running"));
|
|
79
136
|
SYM_runnable = ID2SYM(rb_intern("runnable"));
|
|
@@ -85,6 +142,9 @@ void Init_Fiber() {
|
|
|
85
142
|
|
|
86
143
|
ID_fiber_trace = rb_intern("__fiber_trace__");
|
|
87
144
|
ID_ivar_auto_watcher = rb_intern("@auto_watcher");
|
|
145
|
+
ID_ivar_mailbox = rb_intern("@mailbox");
|
|
146
|
+
ID_ivar_result = rb_intern("@result");
|
|
147
|
+
ID_ivar_waiting_fibers = rb_intern("@waiting_fibers");
|
|
88
148
|
|
|
89
149
|
SYM_fiber_create = ID2SYM(rb_intern("fiber_create"));
|
|
90
150
|
SYM_fiber_ev_loop_enter = ID2SYM(rb_intern("fiber_ev_loop_enter"));
|
|
@@ -11,157 +11,155 @@
|
|
|
11
11
|
|
|
12
12
|
VALUE cTCPSocket;
|
|
13
13
|
|
|
14
|
-
struct
|
|
14
|
+
typedef struct LibevBackend_t {
|
|
15
15
|
struct ev_loop *ev_loop;
|
|
16
16
|
struct ev_async break_async;
|
|
17
17
|
int running;
|
|
18
18
|
int ref_count;
|
|
19
19
|
int run_no_wait_count;
|
|
20
|
-
};
|
|
20
|
+
} LibevBackend_t;
|
|
21
21
|
|
|
22
|
-
static size_t
|
|
23
|
-
return sizeof(
|
|
22
|
+
static size_t LibevBackend_size(const void *ptr) {
|
|
23
|
+
return sizeof(LibevBackend_t);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
static const rb_data_type_t
|
|
26
|
+
static const rb_data_type_t LibevBackend_type = {
|
|
27
27
|
"Libev",
|
|
28
|
-
{0, 0,
|
|
28
|
+
{0, 0, LibevBackend_size,},
|
|
29
29
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
static VALUE
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return TypedData_Wrap_Struct(klass, &
|
|
32
|
+
static VALUE LibevBackend_allocate(VALUE klass) {
|
|
33
|
+
LibevBackend_t *backend = ALLOC(LibevBackend_t);
|
|
34
|
+
|
|
35
|
+
return TypedData_Wrap_Struct(klass, &LibevBackend_type, backend);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
#define
|
|
39
|
-
TypedData_Get_Struct((obj),
|
|
38
|
+
#define GetLibevBackend(obj, backend) \
|
|
39
|
+
TypedData_Get_Struct((obj), LibevBackend_t, &LibevBackend_type, (backend))
|
|
40
40
|
|
|
41
41
|
void break_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
|
|
42
42
|
// This callback does nothing, the break async is used solely for breaking out
|
|
43
43
|
// of a *blocking* event loop (waking it up) in a thread-safe, signal-safe manner
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
static VALUE
|
|
47
|
-
|
|
46
|
+
static VALUE LibevBackend_initialize(VALUE self) {
|
|
47
|
+
LibevBackend_t *backend;
|
|
48
48
|
VALUE thread = rb_thread_current();
|
|
49
49
|
int is_main_thread = (thread == rb_thread_main());
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
GetLibevBackend(self, backend);
|
|
52
|
+
backend->ev_loop = is_main_thread ? EV_DEFAULT : ev_loop_new(EVFLAG_NOSIGMASK);
|
|
53
53
|
|
|
54
|
-
ev_async_init(&
|
|
55
|
-
ev_async_start(
|
|
56
|
-
ev_unref(
|
|
54
|
+
ev_async_init(&backend->break_async, break_async_callback);
|
|
55
|
+
ev_async_start(backend->ev_loop, &backend->break_async);
|
|
56
|
+
ev_unref(backend->ev_loop); // don't count the break_async watcher
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
backend->running = 0;
|
|
59
|
+
backend->ref_count = 0;
|
|
60
|
+
backend->run_no_wait_count = 0;
|
|
61
61
|
|
|
62
62
|
return Qnil;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
VALUE
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
VALUE LibevBackend_finalize(VALUE self) {
|
|
66
|
+
LibevBackend_t *backend;
|
|
67
|
+
GetLibevBackend(self, backend);
|
|
68
68
|
|
|
69
|
-
ev_async_stop(
|
|
69
|
+
ev_async_stop(backend->ev_loop, &backend->break_async);
|
|
70
70
|
|
|
71
|
-
if (!ev_is_default_loop(
|
|
71
|
+
if (!ev_is_default_loop(backend->ev_loop)) ev_loop_destroy(backend->ev_loop);
|
|
72
72
|
|
|
73
73
|
return self;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
VALUE
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
VALUE LibevBackend_post_fork(VALUE self) {
|
|
77
|
+
LibevBackend_t *backend;
|
|
78
|
+
GetLibevBackend(self, backend);
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
ev_loop_fork(agent->ev_loop);
|
|
80
|
+
// After fork there may be some watchers still active left over from the
|
|
81
|
+
// parent, so we destroy the loop, even if it's the default one, then use the
|
|
82
|
+
// default one, as post_fork is called only from the main thread of the forked
|
|
83
|
+
// process. That way we don't need to call ev_loop_fork, since the loop is
|
|
84
|
+
// always a fresh one.
|
|
85
|
+
ev_loop_destroy(backend->ev_loop);
|
|
86
|
+
backend->ev_loop = EV_DEFAULT;
|
|
89
87
|
|
|
90
88
|
return self;
|
|
91
89
|
}
|
|
92
90
|
|
|
93
|
-
VALUE
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
VALUE LibevBackend_ref(VALUE self) {
|
|
92
|
+
LibevBackend_t *backend;
|
|
93
|
+
GetLibevBackend(self, backend);
|
|
96
94
|
|
|
97
|
-
|
|
98
|
-
return self;
|
|
95
|
+
backend->ref_count++;
|
|
96
|
+
return self;
|
|
99
97
|
}
|
|
100
98
|
|
|
101
|
-
VALUE
|
|
102
|
-
|
|
103
|
-
|
|
99
|
+
VALUE LibevBackend_unref(VALUE self) {
|
|
100
|
+
LibevBackend_t *backend;
|
|
101
|
+
GetLibevBackend(self, backend);
|
|
104
102
|
|
|
105
|
-
|
|
106
|
-
return self;
|
|
103
|
+
backend->ref_count--;
|
|
104
|
+
return self;
|
|
107
105
|
}
|
|
108
106
|
|
|
109
|
-
int
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
int LibevBackend_ref_count(VALUE self) {
|
|
108
|
+
LibevBackend_t *backend;
|
|
109
|
+
GetLibevBackend(self, backend);
|
|
112
110
|
|
|
113
|
-
return
|
|
111
|
+
return backend->ref_count;
|
|
114
112
|
}
|
|
115
113
|
|
|
116
|
-
void
|
|
117
|
-
|
|
118
|
-
|
|
114
|
+
void LibevBackend_reset_ref_count(VALUE self) {
|
|
115
|
+
LibevBackend_t *backend;
|
|
116
|
+
GetLibevBackend(self, backend);
|
|
119
117
|
|
|
120
|
-
|
|
118
|
+
backend->ref_count = 0;
|
|
121
119
|
}
|
|
122
120
|
|
|
123
|
-
VALUE
|
|
121
|
+
VALUE LibevBackend_pending_count(VALUE self) {
|
|
124
122
|
int count;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
count = ev_pending_count(
|
|
123
|
+
LibevBackend_t *backend;
|
|
124
|
+
GetLibevBackend(self, backend);
|
|
125
|
+
count = ev_pending_count(backend->ev_loop);
|
|
128
126
|
return INT2NUM(count);
|
|
129
127
|
}
|
|
130
128
|
|
|
131
|
-
VALUE
|
|
129
|
+
VALUE LibevBackend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue) {
|
|
132
130
|
int is_nowait = nowait == Qtrue;
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
LibevBackend_t *backend;
|
|
132
|
+
GetLibevBackend(self, backend);
|
|
135
133
|
|
|
136
134
|
if (is_nowait) {
|
|
137
135
|
long runnable_count = Queue_len(queue);
|
|
138
|
-
|
|
139
|
-
if (
|
|
136
|
+
backend->run_no_wait_count++;
|
|
137
|
+
if (backend->run_no_wait_count < runnable_count || backend->run_no_wait_count < 10)
|
|
140
138
|
return self;
|
|
141
139
|
}
|
|
142
140
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
ev_run(
|
|
148
|
-
|
|
149
|
-
|
|
141
|
+
backend->run_no_wait_count = 0;
|
|
142
|
+
|
|
143
|
+
COND_TRACE(2, SYM_fiber_ev_loop_enter, current_fiber);
|
|
144
|
+
backend->running = 1;
|
|
145
|
+
ev_run(backend->ev_loop, is_nowait ? EVRUN_NOWAIT : EVRUN_ONCE);
|
|
146
|
+
backend->running = 0;
|
|
147
|
+
COND_TRACE(2, SYM_fiber_ev_loop_leave, current_fiber);
|
|
150
148
|
|
|
151
149
|
return self;
|
|
152
150
|
}
|
|
153
151
|
|
|
154
|
-
VALUE
|
|
155
|
-
|
|
156
|
-
|
|
152
|
+
VALUE LibevBackend_wakeup(VALUE self) {
|
|
153
|
+
LibevBackend_t *backend;
|
|
154
|
+
GetLibevBackend(self, backend);
|
|
157
155
|
|
|
158
|
-
if (
|
|
156
|
+
if (backend->running) {
|
|
159
157
|
// Since the loop will run until at least one event has occurred, we signal
|
|
160
158
|
// the selector's associated async watcher, which will cause the ev loop to
|
|
161
159
|
// return. In contrast to using `ev_break` to break out of the loop, which
|
|
162
160
|
// should be called from the same thread (from within the ev_loop), using an
|
|
163
161
|
// `ev_async` allows us to interrupt the event loop across threads.
|
|
164
|
-
ev_async_send(
|
|
162
|
+
ev_async_send(backend->ev_loop, &backend->break_async);
|
|
165
163
|
return Qtrue;
|
|
166
164
|
}
|
|
167
165
|
|
|
@@ -240,40 +238,47 @@ struct libev_io {
|
|
|
240
238
|
VALUE fiber;
|
|
241
239
|
};
|
|
242
240
|
|
|
243
|
-
void
|
|
241
|
+
void LibevBackend_io_callback(EV_P_ ev_io *w, int revents)
|
|
244
242
|
{
|
|
245
243
|
struct libev_io *watcher = (struct libev_io *)w;
|
|
246
244
|
Fiber_make_runnable(watcher->fiber, Qnil);
|
|
247
245
|
}
|
|
248
246
|
|
|
249
|
-
inline VALUE libev_await(
|
|
247
|
+
inline VALUE libev_await(LibevBackend_t *backend) {
|
|
250
248
|
VALUE ret;
|
|
251
|
-
|
|
249
|
+
backend->ref_count++;
|
|
252
250
|
ret = Thread_switch_fiber(rb_thread_current());
|
|
253
|
-
|
|
251
|
+
backend->ref_count--;
|
|
254
252
|
RB_GC_GUARD(ret);
|
|
255
253
|
return ret;
|
|
256
254
|
}
|
|
257
255
|
|
|
258
|
-
VALUE
|
|
259
|
-
struct LibevAgent_t *agent;
|
|
260
|
-
GetLibevAgent(self, agent);
|
|
261
|
-
return libev_await(agent);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
VALUE libev_io_wait(struct LibevAgent_t *agent, struct libev_io *watcher, rb_io_t *fptr, int flags) {
|
|
256
|
+
VALUE libev_wait_fd_with_watcher(LibevBackend_t *backend, int fd, struct libev_io *watcher, int events) {
|
|
265
257
|
VALUE switchpoint_result;
|
|
266
258
|
|
|
267
259
|
if (watcher->fiber == Qnil) {
|
|
268
260
|
watcher->fiber = rb_fiber_current();
|
|
269
|
-
ev_io_init(&watcher->io,
|
|
261
|
+
ev_io_init(&watcher->io, LibevBackend_io_callback, fd, events);
|
|
270
262
|
}
|
|
271
|
-
ev_io_start(
|
|
272
|
-
switchpoint_result = libev_await(agent);
|
|
273
|
-
ev_io_stop(agent->ev_loop, &watcher->io);
|
|
263
|
+
ev_io_start(backend->ev_loop, &watcher->io);
|
|
274
264
|
|
|
265
|
+
switchpoint_result = libev_await(backend);
|
|
266
|
+
|
|
267
|
+
ev_io_stop(backend->ev_loop, &watcher->io);
|
|
275
268
|
RB_GC_GUARD(switchpoint_result);
|
|
276
|
-
return switchpoint_result;
|
|
269
|
+
return switchpoint_result;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
VALUE libev_wait_fd(LibevBackend_t *backend, int fd, int events, int raise_exception) {
|
|
273
|
+
struct libev_io watcher;
|
|
274
|
+
VALUE switchpoint_result = Qnil;
|
|
275
|
+
watcher.fiber = Qnil;
|
|
276
|
+
|
|
277
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fd, &watcher, events);
|
|
278
|
+
|
|
279
|
+
if (raise_exception) TEST_RESUME_EXCEPTION(switchpoint_result);
|
|
280
|
+
RB_GC_GUARD(switchpoint_result);
|
|
281
|
+
return switchpoint_result;
|
|
277
282
|
}
|
|
278
283
|
|
|
279
284
|
VALUE libev_snooze() {
|
|
@@ -290,24 +295,23 @@ ID ID_ivar_is_nonblocking;
|
|
|
290
295
|
// benchmarks (with a "hello world" HTTP server) show throughput is improved
|
|
291
296
|
// by 10-13%.
|
|
292
297
|
inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
|
|
298
|
+
VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
|
|
299
|
+
if (is_nonblocking == Qtrue) return;
|
|
300
|
+
|
|
301
|
+
rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
|
|
302
|
+
|
|
293
303
|
#ifdef _WIN32
|
|
294
|
-
|
|
304
|
+
rb_w32_set_nonblock(fptr->fd);
|
|
295
305
|
#elif defined(F_GETFL)
|
|
296
|
-
|
|
297
|
-
if (
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if (oflags == -1) return;
|
|
301
|
-
if (oflags & O_NONBLOCK) return;
|
|
302
|
-
oflags |= O_NONBLOCK;
|
|
303
|
-
fcntl(fptr->fd, F_SETFL, oflags);
|
|
304
|
-
}
|
|
306
|
+
int oflags = fcntl(fptr->fd, F_GETFL);
|
|
307
|
+
if ((oflags == -1) && (oflags & O_NONBLOCK)) return;
|
|
308
|
+
oflags |= O_NONBLOCK;
|
|
309
|
+
fcntl(fptr->fd, F_SETFL, oflags);
|
|
305
310
|
#endif
|
|
306
|
-
return;
|
|
307
311
|
}
|
|
308
312
|
|
|
309
|
-
VALUE
|
|
310
|
-
|
|
313
|
+
VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
|
|
314
|
+
LibevBackend_t *backend;
|
|
311
315
|
struct libev_io watcher;
|
|
312
316
|
rb_io_t *fptr;
|
|
313
317
|
long dynamic_len = length == Qnil;
|
|
@@ -319,13 +323,13 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
|
319
323
|
int read_to_eof = RTEST(to_eof);
|
|
320
324
|
VALUE underlying_io = rb_iv_get(io, "@io");
|
|
321
325
|
|
|
322
|
-
|
|
326
|
+
GetLibevBackend(self, backend);
|
|
323
327
|
if (underlying_io != Qnil) io = underlying_io;
|
|
324
328
|
GetOpenFile(io, fptr);
|
|
325
329
|
rb_io_check_byte_readable(fptr);
|
|
326
330
|
io_set_nonblock(fptr, io);
|
|
327
331
|
watcher.fiber = Qnil;
|
|
328
|
-
|
|
332
|
+
|
|
329
333
|
OBJ_TAINT(str);
|
|
330
334
|
|
|
331
335
|
// Apparently after reopening a closed file, the file position is not reset,
|
|
@@ -342,12 +346,14 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
|
342
346
|
if (n < 0) {
|
|
343
347
|
int e = errno;
|
|
344
348
|
if (e != EWOULDBLOCK && e != EAGAIN) rb_syserr_fail(e, strerror(e));
|
|
345
|
-
|
|
346
|
-
switchpoint_result =
|
|
349
|
+
|
|
350
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
|
|
351
|
+
|
|
347
352
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
348
353
|
}
|
|
349
354
|
else {
|
|
350
355
|
switchpoint_result = libev_snooze();
|
|
356
|
+
|
|
351
357
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
352
358
|
|
|
353
359
|
if (n == 0) break; // EOF
|
|
@@ -356,7 +362,7 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
|
356
362
|
|
|
357
363
|
if (total == len) {
|
|
358
364
|
if (!dynamic_len) break;
|
|
359
|
-
|
|
365
|
+
|
|
360
366
|
rb_str_resize(str, total);
|
|
361
367
|
rb_str_modify_expand(str, len);
|
|
362
368
|
buf = RSTRING_PTR(str) + total;
|
|
@@ -366,20 +372,21 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
|
366
372
|
else buf += n;
|
|
367
373
|
}
|
|
368
374
|
}
|
|
369
|
-
if (total == 0) return Qnil;
|
|
370
375
|
|
|
371
376
|
io_set_read_length(str, total, shrinkable);
|
|
372
377
|
io_enc_str(str, fptr);
|
|
373
|
-
|
|
378
|
+
|
|
379
|
+
if (total == 0) return Qnil;
|
|
380
|
+
|
|
374
381
|
RB_GC_GUARD(watcher.fiber);
|
|
375
382
|
RB_GC_GUARD(switchpoint_result);
|
|
376
383
|
|
|
377
384
|
return str;
|
|
378
385
|
error:
|
|
379
|
-
return
|
|
386
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
380
387
|
}
|
|
381
388
|
|
|
382
|
-
VALUE
|
|
389
|
+
VALUE LibevBackend_read_loop(VALUE self, VALUE io) {
|
|
383
390
|
|
|
384
391
|
#define PREPARE_STR() { \
|
|
385
392
|
str = Qnil; \
|
|
@@ -396,7 +403,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
|
396
403
|
PREPARE_STR(); \
|
|
397
404
|
}
|
|
398
405
|
|
|
399
|
-
|
|
406
|
+
LibevBackend_t *backend;
|
|
400
407
|
struct libev_io watcher;
|
|
401
408
|
rb_io_t *fptr;
|
|
402
409
|
VALUE str;
|
|
@@ -409,7 +416,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
|
409
416
|
|
|
410
417
|
PREPARE_STR();
|
|
411
418
|
|
|
412
|
-
|
|
419
|
+
GetLibevBackend(self, backend);
|
|
413
420
|
if (underlying_io != Qnil) io = underlying_io;
|
|
414
421
|
GetOpenFile(io, fptr);
|
|
415
422
|
rb_io_check_byte_readable(fptr);
|
|
@@ -431,22 +438,17 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
|
431
438
|
int e = errno;
|
|
432
439
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
433
440
|
|
|
434
|
-
switchpoint_result =
|
|
441
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
|
|
435
442
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
436
443
|
}
|
|
437
444
|
else {
|
|
438
445
|
switchpoint_result = libev_snooze();
|
|
439
|
-
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
440
446
|
|
|
441
|
-
if (
|
|
447
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
442
448
|
|
|
449
|
+
if (n == 0) break; // EOF
|
|
443
450
|
total = n;
|
|
444
451
|
YIELD_STR();
|
|
445
|
-
Fiber_make_runnable(rb_fiber_current(), Qnil);
|
|
446
|
-
switchpoint_result = Thread_switch_fiber(rb_thread_current());
|
|
447
|
-
if (TEST_EXCEPTION(switchpoint_result)) {
|
|
448
|
-
goto error;
|
|
449
|
-
}
|
|
450
452
|
}
|
|
451
453
|
}
|
|
452
454
|
|
|
@@ -456,11 +458,11 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
|
456
458
|
|
|
457
459
|
return io;
|
|
458
460
|
error:
|
|
459
|
-
return
|
|
461
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
460
462
|
}
|
|
461
463
|
|
|
462
|
-
VALUE
|
|
463
|
-
|
|
464
|
+
VALUE LibevBackend_write(VALUE self, VALUE io, VALUE str) {
|
|
465
|
+
LibevBackend_t *backend;
|
|
464
466
|
struct libev_io watcher;
|
|
465
467
|
rb_io_t *fptr;
|
|
466
468
|
VALUE switchpoint_result = Qnil;
|
|
@@ -471,7 +473,7 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
|
|
471
473
|
|
|
472
474
|
underlying_io = rb_iv_get(io, "@io");
|
|
473
475
|
if (underlying_io != Qnil) io = underlying_io;
|
|
474
|
-
|
|
476
|
+
GetLibevBackend(self, backend);
|
|
475
477
|
io = rb_io_get_write_io(io);
|
|
476
478
|
GetOpenFile(io, fptr);
|
|
477
479
|
watcher.fiber = Qnil;
|
|
@@ -481,7 +483,9 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
|
|
481
483
|
if (n < 0) {
|
|
482
484
|
int e = errno;
|
|
483
485
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
484
|
-
|
|
486
|
+
|
|
487
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
|
|
488
|
+
|
|
485
489
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
486
490
|
}
|
|
487
491
|
else {
|
|
@@ -492,6 +496,7 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
|
|
492
496
|
|
|
493
497
|
if (watcher.fiber == Qnil) {
|
|
494
498
|
switchpoint_result = libev_snooze();
|
|
499
|
+
|
|
495
500
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
496
501
|
}
|
|
497
502
|
|
|
@@ -500,11 +505,11 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
|
|
500
505
|
|
|
501
506
|
return INT2NUM(len);
|
|
502
507
|
error:
|
|
503
|
-
return
|
|
508
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
504
509
|
}
|
|
505
510
|
|
|
506
|
-
VALUE
|
|
507
|
-
|
|
511
|
+
VALUE LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
512
|
+
LibevBackend_t *backend;
|
|
508
513
|
struct libev_io watcher;
|
|
509
514
|
rb_io_t *fptr;
|
|
510
515
|
VALUE switchpoint_result = Qnil;
|
|
@@ -517,7 +522,7 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
|
517
522
|
|
|
518
523
|
underlying_io = rb_iv_get(io, "@io");
|
|
519
524
|
if (underlying_io != Qnil) io = underlying_io;
|
|
520
|
-
|
|
525
|
+
GetLibevBackend(self, backend);
|
|
521
526
|
io = rb_io_get_write_io(io);
|
|
522
527
|
GetOpenFile(io, fptr);
|
|
523
528
|
watcher.fiber = Qnil;
|
|
@@ -537,7 +542,8 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
|
537
542
|
int e = errno;
|
|
538
543
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
539
544
|
|
|
540
|
-
switchpoint_result =
|
|
545
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
|
|
546
|
+
|
|
541
547
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
542
548
|
}
|
|
543
549
|
else {
|
|
@@ -560,6 +566,7 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
|
560
566
|
}
|
|
561
567
|
if (watcher.fiber == Qnil) {
|
|
562
568
|
switchpoint_result = libev_snooze();
|
|
569
|
+
|
|
563
570
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
564
571
|
}
|
|
565
572
|
|
|
@@ -570,23 +577,23 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
|
570
577
|
return INT2NUM(total_written);
|
|
571
578
|
error:
|
|
572
579
|
free(iov);
|
|
573
|
-
return
|
|
580
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
574
581
|
}
|
|
575
582
|
|
|
576
|
-
VALUE
|
|
583
|
+
VALUE LibevBackend_write_m(int argc, VALUE *argv, VALUE self) {
|
|
577
584
|
if (argc < 2)
|
|
578
585
|
// TODO: raise ArgumentError
|
|
579
586
|
rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
|
|
580
|
-
|
|
587
|
+
|
|
581
588
|
return (argc == 2) ?
|
|
582
|
-
|
|
583
|
-
|
|
589
|
+
LibevBackend_write(self, argv[0], argv[1]) :
|
|
590
|
+
LibevBackend_writev(self, argv[0], argc - 1, argv + 1);
|
|
584
591
|
}
|
|
585
592
|
|
|
586
593
|
///////////////////////////////////////////////////////////////////////////
|
|
587
594
|
|
|
588
|
-
VALUE
|
|
589
|
-
|
|
595
|
+
VALUE LibevBackend_accept(VALUE self, VALUE sock) {
|
|
596
|
+
LibevBackend_t *backend;
|
|
590
597
|
struct libev_io watcher;
|
|
591
598
|
rb_io_t *fptr;
|
|
592
599
|
int fd;
|
|
@@ -596,7 +603,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
|
596
603
|
VALUE underlying_sock = rb_iv_get(sock, "@io");
|
|
597
604
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
|
598
605
|
|
|
599
|
-
|
|
606
|
+
GetLibevBackend(self, backend);
|
|
600
607
|
GetOpenFile(sock, fptr);
|
|
601
608
|
io_set_nonblock(fptr, sock);
|
|
602
609
|
watcher.fiber = Qnil;
|
|
@@ -606,13 +613,15 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
|
606
613
|
int e = errno;
|
|
607
614
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
608
615
|
|
|
609
|
-
switchpoint_result =
|
|
616
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
|
|
617
|
+
|
|
610
618
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
611
619
|
}
|
|
612
620
|
else {
|
|
613
621
|
VALUE socket;
|
|
614
622
|
rb_io_t *fp;
|
|
615
623
|
switchpoint_result = libev_snooze();
|
|
624
|
+
|
|
616
625
|
if (TEST_EXCEPTION(switchpoint_result)) {
|
|
617
626
|
close(fd); // close fd since we're raising an exception
|
|
618
627
|
goto error;
|
|
@@ -626,7 +635,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
|
626
635
|
rb_io_ascii8bit_binmode(socket);
|
|
627
636
|
io_set_nonblock(fp, socket);
|
|
628
637
|
rb_io_synchronized(fp);
|
|
629
|
-
|
|
638
|
+
|
|
630
639
|
// if (rsock_do_not_reverse_lookup) {
|
|
631
640
|
// fp->mode |= FMODE_NOREVLOOKUP;
|
|
632
641
|
// }
|
|
@@ -636,11 +645,11 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
|
636
645
|
RB_GC_GUARD(switchpoint_result);
|
|
637
646
|
return Qnil;
|
|
638
647
|
error:
|
|
639
|
-
return
|
|
648
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
640
649
|
}
|
|
641
650
|
|
|
642
|
-
VALUE
|
|
643
|
-
|
|
651
|
+
VALUE LibevBackend_accept_loop(VALUE self, VALUE sock) {
|
|
652
|
+
LibevBackend_t *backend;
|
|
644
653
|
struct libev_io watcher;
|
|
645
654
|
rb_io_t *fptr;
|
|
646
655
|
int fd;
|
|
@@ -651,7 +660,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
|
651
660
|
VALUE underlying_sock = rb_iv_get(sock, "@io");
|
|
652
661
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
|
653
662
|
|
|
654
|
-
|
|
663
|
+
GetLibevBackend(self, backend);
|
|
655
664
|
GetOpenFile(sock, fptr);
|
|
656
665
|
io_set_nonblock(fptr, sock);
|
|
657
666
|
watcher.fiber = Qnil;
|
|
@@ -662,17 +671,19 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
|
662
671
|
int e = errno;
|
|
663
672
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
664
673
|
|
|
665
|
-
switchpoint_result =
|
|
674
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
|
|
675
|
+
|
|
666
676
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
667
677
|
}
|
|
668
678
|
else {
|
|
669
679
|
rb_io_t *fp;
|
|
670
680
|
switchpoint_result = libev_snooze();
|
|
681
|
+
|
|
671
682
|
if (TEST_EXCEPTION(switchpoint_result)) {
|
|
672
683
|
close(fd); // close fd since we're raising an exception
|
|
673
684
|
goto error;
|
|
674
685
|
}
|
|
675
|
-
|
|
686
|
+
|
|
676
687
|
socket = rb_obj_alloc(cTCPSocket);
|
|
677
688
|
MakeOpenFile(socket, fp);
|
|
678
689
|
rb_update_max_fd(fd);
|
|
@@ -692,11 +703,11 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
|
692
703
|
RB_GC_GUARD(switchpoint_result);
|
|
693
704
|
return Qnil;
|
|
694
705
|
error:
|
|
695
|
-
return
|
|
706
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
696
707
|
}
|
|
697
708
|
|
|
698
|
-
VALUE
|
|
699
|
-
|
|
709
|
+
VALUE LibevBackend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
710
|
+
LibevBackend_t *backend;
|
|
700
711
|
struct libev_io watcher;
|
|
701
712
|
rb_io_t *fptr;
|
|
702
713
|
struct sockaddr_in addr;
|
|
@@ -705,12 +716,12 @@ VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
|
705
716
|
VALUE underlying_sock = rb_iv_get(sock, "@io");
|
|
706
717
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
|
707
718
|
|
|
708
|
-
|
|
719
|
+
GetLibevBackend(self, backend);
|
|
709
720
|
GetOpenFile(sock, fptr);
|
|
710
721
|
io_set_nonblock(fptr, sock);
|
|
711
722
|
watcher.fiber = Qnil;
|
|
712
723
|
|
|
713
|
-
addr.sin_family = AF_INET;
|
|
724
|
+
addr.sin_family = AF_INET;
|
|
714
725
|
addr.sin_addr.s_addr = inet_addr(host_buf);
|
|
715
726
|
addr.sin_port = htons(NUM2INT(port));
|
|
716
727
|
|
|
@@ -718,41 +729,32 @@ VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
|
718
729
|
if (result < 0) {
|
|
719
730
|
int e = errno;
|
|
720
731
|
if (e != EINPROGRESS) rb_syserr_fail(e, strerror(e));
|
|
721
|
-
|
|
732
|
+
|
|
733
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
|
|
734
|
+
|
|
722
735
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
723
736
|
}
|
|
724
737
|
else {
|
|
725
738
|
switchpoint_result = libev_snooze();
|
|
739
|
+
|
|
726
740
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
727
741
|
}
|
|
728
742
|
RB_GC_GUARD(switchpoint_result);
|
|
729
743
|
return sock;
|
|
730
744
|
error:
|
|
731
|
-
return
|
|
745
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
732
746
|
}
|
|
733
747
|
|
|
734
|
-
VALUE
|
|
735
|
-
|
|
736
|
-
struct libev_io watcher;
|
|
748
|
+
VALUE LibevBackend_wait_io(VALUE self, VALUE io, VALUE write) {
|
|
749
|
+
LibevBackend_t *backend;
|
|
737
750
|
rb_io_t *fptr;
|
|
738
|
-
VALUE switchpoint_result = Qnil;
|
|
739
751
|
int events = RTEST(write) ? EV_WRITE : EV_READ;
|
|
740
|
-
|
|
741
752
|
VALUE underlying_io = rb_iv_get(io, "@io");
|
|
742
|
-
GetLibevAgent(self, agent);
|
|
743
753
|
if (underlying_io != Qnil) io = underlying_io;
|
|
754
|
+
GetLibevBackend(self, backend);
|
|
744
755
|
GetOpenFile(io, fptr);
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
ev_io_init(&watcher.io, LibevAgent_io_callback, fptr->fd, events);
|
|
748
|
-
ev_io_start(agent->ev_loop, &watcher.io);
|
|
749
|
-
switchpoint_result = libev_await(agent);
|
|
750
|
-
ev_io_stop(agent->ev_loop, &watcher.io);
|
|
751
|
-
|
|
752
|
-
TEST_RESUME_EXCEPTION(switchpoint_result);
|
|
753
|
-
RB_GC_GUARD(watcher.fiber);
|
|
754
|
-
RB_GC_GUARD(switchpoint_result);
|
|
755
|
-
return switchpoint_result;
|
|
756
|
+
|
|
757
|
+
return libev_wait_fd(backend, fptr->fd, events, 1);
|
|
756
758
|
}
|
|
757
759
|
|
|
758
760
|
struct libev_timer {
|
|
@@ -760,26 +762,25 @@ struct libev_timer {
|
|
|
760
762
|
VALUE fiber;
|
|
761
763
|
};
|
|
762
764
|
|
|
763
|
-
void
|
|
765
|
+
void LibevBackend_timer_callback(EV_P_ ev_timer *w, int revents)
|
|
764
766
|
{
|
|
765
767
|
struct libev_timer *watcher = (struct libev_timer *)w;
|
|
766
768
|
Fiber_make_runnable(watcher->fiber, Qnil);
|
|
767
769
|
}
|
|
768
770
|
|
|
769
|
-
VALUE
|
|
770
|
-
|
|
771
|
+
VALUE LibevBackend_sleep(VALUE self, VALUE duration) {
|
|
772
|
+
LibevBackend_t *backend;
|
|
771
773
|
struct libev_timer watcher;
|
|
772
774
|
VALUE switchpoint_result = Qnil;
|
|
773
775
|
|
|
774
|
-
|
|
776
|
+
GetLibevBackend(self, backend);
|
|
775
777
|
watcher.fiber = rb_fiber_current();
|
|
776
|
-
ev_timer_init(&watcher.timer,
|
|
777
|
-
ev_timer_start(
|
|
778
|
+
ev_timer_init(&watcher.timer, LibevBackend_timer_callback, NUM2DBL(duration), 0.);
|
|
779
|
+
ev_timer_start(backend->ev_loop, &watcher.timer);
|
|
778
780
|
|
|
779
|
-
switchpoint_result = libev_await(
|
|
780
|
-
|
|
781
|
-
ev_timer_stop(agent->ev_loop, &watcher.timer);
|
|
781
|
+
switchpoint_result = libev_await(backend);
|
|
782
782
|
|
|
783
|
+
ev_timer_stop(backend->ev_loop, &watcher.timer);
|
|
783
784
|
TEST_RESUME_EXCEPTION(switchpoint_result);
|
|
784
785
|
RB_GC_GUARD(watcher.fiber);
|
|
785
786
|
RB_GC_GUARD(switchpoint_result);
|
|
@@ -791,7 +792,7 @@ struct libev_child {
|
|
|
791
792
|
VALUE fiber;
|
|
792
793
|
};
|
|
793
794
|
|
|
794
|
-
void
|
|
795
|
+
void LibevBackend_child_callback(EV_P_ ev_child *w, int revents)
|
|
795
796
|
{
|
|
796
797
|
struct libev_child *watcher = (struct libev_child *)w;
|
|
797
798
|
int exit_status = w->rstatus >> 8; // weird, why should we do this?
|
|
@@ -801,85 +802,88 @@ void LibevAgent_child_callback(EV_P_ ev_child *w, int revents)
|
|
|
801
802
|
Fiber_make_runnable(watcher->fiber, status);
|
|
802
803
|
}
|
|
803
804
|
|
|
804
|
-
VALUE
|
|
805
|
-
|
|
805
|
+
VALUE LibevBackend_waitpid(VALUE self, VALUE pid) {
|
|
806
|
+
LibevBackend_t *backend;
|
|
806
807
|
struct libev_child watcher;
|
|
807
808
|
VALUE switchpoint_result = Qnil;
|
|
808
|
-
|
|
809
|
+
GetLibevBackend(self, backend);
|
|
809
810
|
|
|
810
811
|
watcher.fiber = rb_fiber_current();
|
|
811
|
-
ev_child_init(&watcher.child,
|
|
812
|
-
ev_child_start(
|
|
813
|
-
|
|
814
|
-
switchpoint_result = libev_await(
|
|
815
|
-
ev_child_stop(agent->ev_loop, &watcher.child);
|
|
812
|
+
ev_child_init(&watcher.child, LibevBackend_child_callback, NUM2INT(pid), 0);
|
|
813
|
+
ev_child_start(backend->ev_loop, &watcher.child);
|
|
814
|
+
|
|
815
|
+
switchpoint_result = libev_await(backend);
|
|
816
816
|
|
|
817
|
+
ev_child_stop(backend->ev_loop, &watcher.child);
|
|
817
818
|
TEST_RESUME_EXCEPTION(switchpoint_result);
|
|
818
819
|
RB_GC_GUARD(watcher.fiber);
|
|
819
820
|
RB_GC_GUARD(switchpoint_result);
|
|
820
821
|
return switchpoint_result;
|
|
821
822
|
}
|
|
822
823
|
|
|
823
|
-
struct ev_loop *
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
return
|
|
824
|
+
struct ev_loop *LibevBackend_ev_loop(VALUE self) {
|
|
825
|
+
LibevBackend_t *backend;
|
|
826
|
+
GetLibevBackend(self, backend);
|
|
827
|
+
return backend->ev_loop;
|
|
827
828
|
}
|
|
828
829
|
|
|
829
|
-
void
|
|
830
|
+
void LibevBackend_async_callback(EV_P_ ev_async *w, int revents) { }
|
|
830
831
|
|
|
831
|
-
VALUE
|
|
832
|
-
|
|
833
|
-
struct ev_async async;
|
|
832
|
+
VALUE LibevBackend_wait_event(VALUE self, VALUE raise) {
|
|
833
|
+
LibevBackend_t *backend;
|
|
834
834
|
VALUE switchpoint_result = Qnil;
|
|
835
|
-
|
|
835
|
+
GetLibevBackend(self, backend);
|
|
836
|
+
|
|
837
|
+
struct ev_async async;
|
|
838
|
+
|
|
839
|
+
ev_async_init(&async, LibevBackend_async_callback);
|
|
840
|
+
ev_async_start(backend->ev_loop, &async);
|
|
836
841
|
|
|
837
|
-
|
|
838
|
-
ev_async_start(agent->ev_loop, &async);
|
|
839
|
-
|
|
840
|
-
switchpoint_result = libev_await(agent);
|
|
841
|
-
ev_async_stop(agent->ev_loop, &async);
|
|
842
|
+
switchpoint_result = libev_await(backend);
|
|
842
843
|
|
|
844
|
+
ev_async_stop(backend->ev_loop, &async);
|
|
843
845
|
if (RTEST(raise)) TEST_RESUME_EXCEPTION(switchpoint_result);
|
|
844
846
|
RB_GC_GUARD(switchpoint_result);
|
|
845
847
|
return switchpoint_result;
|
|
846
848
|
}
|
|
847
849
|
|
|
848
|
-
void
|
|
850
|
+
void Init_LibevBackend() {
|
|
849
851
|
rb_require("socket");
|
|
850
852
|
cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
|
|
851
853
|
|
|
852
|
-
VALUE
|
|
853
|
-
rb_define_alloc_func(
|
|
854
|
+
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cData);
|
|
855
|
+
rb_define_alloc_func(cBackend, LibevBackend_allocate);
|
|
854
856
|
|
|
855
|
-
rb_define_method(
|
|
856
|
-
rb_define_method(
|
|
857
|
-
rb_define_method(
|
|
858
|
-
rb_define_method(
|
|
857
|
+
rb_define_method(cBackend, "initialize", LibevBackend_initialize, 0);
|
|
858
|
+
rb_define_method(cBackend, "finalize", LibevBackend_finalize, 0);
|
|
859
|
+
rb_define_method(cBackend, "post_fork", LibevBackend_post_fork, 0);
|
|
860
|
+
rb_define_method(cBackend, "pending_count", LibevBackend_pending_count, 0);
|
|
859
861
|
|
|
860
|
-
rb_define_method(
|
|
861
|
-
rb_define_method(
|
|
862
|
+
rb_define_method(cBackend, "ref", LibevBackend_ref, 0);
|
|
863
|
+
rb_define_method(cBackend, "unref", LibevBackend_unref, 0);
|
|
862
864
|
|
|
863
|
-
rb_define_method(
|
|
864
|
-
rb_define_method(
|
|
865
|
+
rb_define_method(cBackend, "poll", LibevBackend_poll, 3);
|
|
866
|
+
rb_define_method(cBackend, "break", LibevBackend_wakeup, 0);
|
|
865
867
|
|
|
866
|
-
rb_define_method(
|
|
867
|
-
rb_define_method(
|
|
868
|
-
rb_define_method(
|
|
869
|
-
rb_define_method(
|
|
870
|
-
rb_define_method(
|
|
871
|
-
rb_define_method(
|
|
872
|
-
rb_define_method(
|
|
873
|
-
rb_define_method(
|
|
874
|
-
rb_define_method(
|
|
875
|
-
rb_define_method(
|
|
868
|
+
rb_define_method(cBackend, "read", LibevBackend_read, 4);
|
|
869
|
+
rb_define_method(cBackend, "read_loop", LibevBackend_read_loop, 1);
|
|
870
|
+
rb_define_method(cBackend, "write", LibevBackend_write_m, -1);
|
|
871
|
+
rb_define_method(cBackend, "accept", LibevBackend_accept, 1);
|
|
872
|
+
rb_define_method(cBackend, "accept_loop", LibevBackend_accept_loop, 1);
|
|
873
|
+
rb_define_method(cBackend, "connect", LibevBackend_connect, 3);
|
|
874
|
+
rb_define_method(cBackend, "wait_io", LibevBackend_wait_io, 2);
|
|
875
|
+
rb_define_method(cBackend, "sleep", LibevBackend_sleep, 1);
|
|
876
|
+
rb_define_method(cBackend, "waitpid", LibevBackend_waitpid, 1);
|
|
877
|
+
rb_define_method(cBackend, "wait_event", LibevBackend_wait_event, 1);
|
|
876
878
|
|
|
877
879
|
ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
|
|
878
880
|
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
881
|
+
__BACKEND__.pending_count = LibevBackend_pending_count;
|
|
882
|
+
__BACKEND__.poll = LibevBackend_poll;
|
|
883
|
+
__BACKEND__.ref = LibevBackend_ref;
|
|
884
|
+
__BACKEND__.ref_count = LibevBackend_ref_count;
|
|
885
|
+
__BACKEND__.reset_ref_count = LibevBackend_reset_ref_count;
|
|
886
|
+
__BACKEND__.unref = LibevBackend_unref;
|
|
887
|
+
__BACKEND__.wait_event = LibevBackend_wait_event;
|
|
888
|
+
__BACKEND__.wakeup = LibevBackend_wakeup;
|
|
885
889
|
}
|