polyphony 0.43.9 → 0.45.1
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 +40 -0
- data/Gemfile.lock +16 -6
- data/Rakefile +1 -1
- data/TODO.md +14 -13
- 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/{libev_agent.c → libev_backend.c} +272 -265
- data/ext/polyphony/polyphony.c +4 -2
- data/ext/polyphony/polyphony.h +15 -28
- data/ext/polyphony/polyphony_ext.c +3 -4
- data/ext/polyphony/queue.c +32 -12
- data/ext/polyphony/ring_buffer.c +0 -1
- data/ext/polyphony/thread.c +58 -44
- 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 +15 -10
- 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 +5 -1
- data/lib/polyphony/extensions/io.rb +45 -12
- data/lib/polyphony/extensions/openssl.rb +6 -6
- data/lib/polyphony/extensions/socket.rb +22 -23
- 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 +21 -5
- 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
- data/test/test_throttler.rb +1 -0
- metadata +125 -96
- 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
|
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
|
@@ -3,164 +3,163 @@
|
|
|
3
3
|
#include <sys/uio.h>
|
|
4
4
|
#include <unistd.h>
|
|
5
5
|
#include <fcntl.h>
|
|
6
|
+
#include <netinet/in.h>
|
|
7
|
+
#include <arpa/inet.h>
|
|
6
8
|
|
|
7
9
|
#include "polyphony.h"
|
|
8
10
|
#include "../libev/ev.h"
|
|
9
11
|
|
|
10
|
-
VALUE cLibevAgent = Qnil;
|
|
11
12
|
VALUE cTCPSocket;
|
|
12
13
|
|
|
13
|
-
struct
|
|
14
|
+
typedef struct LibevBackend_t {
|
|
14
15
|
struct ev_loop *ev_loop;
|
|
15
16
|
struct ev_async break_async;
|
|
16
17
|
int running;
|
|
17
18
|
int ref_count;
|
|
18
19
|
int run_no_wait_count;
|
|
19
|
-
};
|
|
20
|
+
} LibevBackend_t;
|
|
20
21
|
|
|
21
|
-
static size_t
|
|
22
|
-
return sizeof(
|
|
22
|
+
static size_t LibevBackend_size(const void *ptr) {
|
|
23
|
+
return sizeof(LibevBackend_t);
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
static const rb_data_type_t
|
|
26
|
+
static const rb_data_type_t LibevBackend_type = {
|
|
26
27
|
"Libev",
|
|
27
|
-
{0, 0,
|
|
28
|
+
{0, 0, LibevBackend_size,},
|
|
28
29
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
|
29
30
|
};
|
|
30
31
|
|
|
31
|
-
static VALUE
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
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);
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
#define
|
|
38
|
-
TypedData_Get_Struct((obj),
|
|
38
|
+
#define GetLibevBackend(obj, backend) \
|
|
39
|
+
TypedData_Get_Struct((obj), LibevBackend_t, &LibevBackend_type, (backend))
|
|
39
40
|
|
|
40
41
|
void break_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
|
|
41
42
|
// This callback does nothing, the break async is used solely for breaking out
|
|
42
43
|
// of a *blocking* event loop (waking it up) in a thread-safe, signal-safe manner
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
static VALUE
|
|
46
|
-
|
|
46
|
+
static VALUE LibevBackend_initialize(VALUE self) {
|
|
47
|
+
LibevBackend_t *backend;
|
|
47
48
|
VALUE thread = rb_thread_current();
|
|
48
49
|
int is_main_thread = (thread == rb_thread_main());
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
GetLibevBackend(self, backend);
|
|
52
|
+
backend->ev_loop = is_main_thread ? EV_DEFAULT : ev_loop_new(EVFLAG_NOSIGMASK);
|
|
52
53
|
|
|
53
|
-
ev_async_init(&
|
|
54
|
-
ev_async_start(
|
|
55
|
-
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
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
backend->running = 0;
|
|
59
|
+
backend->ref_count = 0;
|
|
60
|
+
backend->run_no_wait_count = 0;
|
|
60
61
|
|
|
61
62
|
return Qnil;
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
VALUE
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
VALUE LibevBackend_finalize(VALUE self) {
|
|
66
|
+
LibevBackend_t *backend;
|
|
67
|
+
GetLibevBackend(self, backend);
|
|
67
68
|
|
|
68
|
-
ev_async_stop(
|
|
69
|
+
ev_async_stop(backend->ev_loop, &backend->break_async);
|
|
69
70
|
|
|
70
|
-
if (!ev_is_default_loop(
|
|
71
|
+
if (!ev_is_default_loop(backend->ev_loop)) ev_loop_destroy(backend->ev_loop);
|
|
71
72
|
|
|
72
73
|
return self;
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
VALUE
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (!ev_is_default_loop(agent->ev_loop)) {
|
|
80
|
-
// post_fork is called only for the main thread of the forked process. If
|
|
81
|
-
// the forked process was forked from a thread other than the main one,
|
|
82
|
-
// we remove the old non-default ev_loop and use the default one instead.
|
|
83
|
-
ev_loop_destroy(agent->ev_loop);
|
|
84
|
-
agent->ev_loop = EV_DEFAULT;
|
|
85
|
-
}
|
|
76
|
+
VALUE LibevBackend_post_fork(VALUE self) {
|
|
77
|
+
LibevBackend_t *backend;
|
|
78
|
+
GetLibevBackend(self, backend);
|
|
86
79
|
|
|
87
|
-
|
|
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;
|
|
88
87
|
|
|
89
88
|
return self;
|
|
90
89
|
}
|
|
91
90
|
|
|
92
|
-
VALUE
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
VALUE LibevBackend_ref(VALUE self) {
|
|
92
|
+
LibevBackend_t *backend;
|
|
93
|
+
GetLibevBackend(self, backend);
|
|
95
94
|
|
|
96
|
-
|
|
97
|
-
return self;
|
|
95
|
+
backend->ref_count++;
|
|
96
|
+
return self;
|
|
98
97
|
}
|
|
99
98
|
|
|
100
|
-
VALUE
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
VALUE LibevBackend_unref(VALUE self) {
|
|
100
|
+
LibevBackend_t *backend;
|
|
101
|
+
GetLibevBackend(self, backend);
|
|
103
102
|
|
|
104
|
-
|
|
105
|
-
return self;
|
|
103
|
+
backend->ref_count--;
|
|
104
|
+
return self;
|
|
106
105
|
}
|
|
107
106
|
|
|
108
|
-
int
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
int LibevBackend_ref_count(VALUE self) {
|
|
108
|
+
LibevBackend_t *backend;
|
|
109
|
+
GetLibevBackend(self, backend);
|
|
111
110
|
|
|
112
|
-
return
|
|
111
|
+
return backend->ref_count;
|
|
113
112
|
}
|
|
114
113
|
|
|
115
|
-
void
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
void LibevBackend_reset_ref_count(VALUE self) {
|
|
115
|
+
LibevBackend_t *backend;
|
|
116
|
+
GetLibevBackend(self, backend);
|
|
118
117
|
|
|
119
|
-
|
|
118
|
+
backend->ref_count = 0;
|
|
120
119
|
}
|
|
121
120
|
|
|
122
|
-
VALUE
|
|
121
|
+
VALUE LibevBackend_pending_count(VALUE self) {
|
|
123
122
|
int count;
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
count = ev_pending_count(
|
|
123
|
+
LibevBackend_t *backend;
|
|
124
|
+
GetLibevBackend(self, backend);
|
|
125
|
+
count = ev_pending_count(backend->ev_loop);
|
|
127
126
|
return INT2NUM(count);
|
|
128
127
|
}
|
|
129
128
|
|
|
130
|
-
VALUE
|
|
129
|
+
VALUE LibevBackend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue) {
|
|
131
130
|
int is_nowait = nowait == Qtrue;
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
LibevBackend_t *backend;
|
|
132
|
+
GetLibevBackend(self, backend);
|
|
134
133
|
|
|
135
134
|
if (is_nowait) {
|
|
136
135
|
long runnable_count = Queue_len(queue);
|
|
137
|
-
|
|
138
|
-
if (
|
|
136
|
+
backend->run_no_wait_count++;
|
|
137
|
+
if (backend->run_no_wait_count < runnable_count || backend->run_no_wait_count < 10)
|
|
139
138
|
return self;
|
|
140
139
|
}
|
|
141
140
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
ev_run(
|
|
147
|
-
|
|
148
|
-
|
|
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);
|
|
149
148
|
|
|
150
149
|
return self;
|
|
151
150
|
}
|
|
152
151
|
|
|
153
|
-
VALUE
|
|
154
|
-
|
|
155
|
-
|
|
152
|
+
VALUE LibevBackend_wakeup(VALUE self) {
|
|
153
|
+
LibevBackend_t *backend;
|
|
154
|
+
GetLibevBackend(self, backend);
|
|
156
155
|
|
|
157
|
-
if (
|
|
156
|
+
if (backend->running) {
|
|
158
157
|
// Since the loop will run until at least one event has occurred, we signal
|
|
159
158
|
// the selector's associated async watcher, which will cause the ev loop to
|
|
160
159
|
// return. In contrast to using `ev_break` to break out of the loop, which
|
|
161
160
|
// should be called from the same thread (from within the ev_loop), using an
|
|
162
161
|
// `ev_async` allows us to interrupt the event loop across threads.
|
|
163
|
-
ev_async_send(
|
|
162
|
+
ev_async_send(backend->ev_loop, &backend->break_async);
|
|
164
163
|
return Qtrue;
|
|
165
164
|
}
|
|
166
165
|
|
|
@@ -239,40 +238,47 @@ struct libev_io {
|
|
|
239
238
|
VALUE fiber;
|
|
240
239
|
};
|
|
241
240
|
|
|
242
|
-
void
|
|
241
|
+
void LibevBackend_io_callback(EV_P_ ev_io *w, int revents)
|
|
243
242
|
{
|
|
244
243
|
struct libev_io *watcher = (struct libev_io *)w;
|
|
245
244
|
Fiber_make_runnable(watcher->fiber, Qnil);
|
|
246
245
|
}
|
|
247
246
|
|
|
248
|
-
inline VALUE libev_await(
|
|
247
|
+
inline VALUE libev_await(LibevBackend_t *backend) {
|
|
249
248
|
VALUE ret;
|
|
250
|
-
|
|
249
|
+
backend->ref_count++;
|
|
251
250
|
ret = Thread_switch_fiber(rb_thread_current());
|
|
252
|
-
|
|
251
|
+
backend->ref_count--;
|
|
253
252
|
RB_GC_GUARD(ret);
|
|
254
253
|
return ret;
|
|
255
254
|
}
|
|
256
255
|
|
|
257
|
-
VALUE
|
|
258
|
-
struct LibevAgent_t *agent;
|
|
259
|
-
GetLibevAgent(self, agent);
|
|
260
|
-
return libev_await(agent);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
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) {
|
|
264
257
|
VALUE switchpoint_result;
|
|
265
258
|
|
|
266
259
|
if (watcher->fiber == Qnil) {
|
|
267
260
|
watcher->fiber = rb_fiber_current();
|
|
268
|
-
ev_io_init(&watcher->io,
|
|
261
|
+
ev_io_init(&watcher->io, LibevBackend_io_callback, fd, events);
|
|
269
262
|
}
|
|
270
|
-
ev_io_start(
|
|
271
|
-
switchpoint_result = libev_await(agent);
|
|
272
|
-
ev_io_stop(agent->ev_loop, &watcher->io);
|
|
263
|
+
ev_io_start(backend->ev_loop, &watcher->io);
|
|
273
264
|
|
|
265
|
+
switchpoint_result = libev_await(backend);
|
|
266
|
+
|
|
267
|
+
ev_io_stop(backend->ev_loop, &watcher->io);
|
|
274
268
|
RB_GC_GUARD(switchpoint_result);
|
|
275
|
-
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;
|
|
276
282
|
}
|
|
277
283
|
|
|
278
284
|
VALUE libev_snooze() {
|
|
@@ -289,24 +295,23 @@ ID ID_ivar_is_nonblocking;
|
|
|
289
295
|
// benchmarks (with a "hello world" HTTP server) show throughput is improved
|
|
290
296
|
// by 10-13%.
|
|
291
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
|
+
|
|
292
303
|
#ifdef _WIN32
|
|
293
|
-
|
|
304
|
+
rb_w32_set_nonblock(fptr->fd);
|
|
294
305
|
#elif defined(F_GETFL)
|
|
295
|
-
|
|
296
|
-
if (
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if (oflags == -1) return;
|
|
300
|
-
if (oflags & O_NONBLOCK) return;
|
|
301
|
-
oflags |= O_NONBLOCK;
|
|
302
|
-
fcntl(fptr->fd, F_SETFL, oflags);
|
|
303
|
-
}
|
|
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);
|
|
304
310
|
#endif
|
|
305
|
-
return;
|
|
306
311
|
}
|
|
307
312
|
|
|
308
|
-
VALUE
|
|
309
|
-
|
|
313
|
+
VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
|
|
314
|
+
LibevBackend_t *backend;
|
|
310
315
|
struct libev_io watcher;
|
|
311
316
|
rb_io_t *fptr;
|
|
312
317
|
long dynamic_len = length == Qnil;
|
|
@@ -318,13 +323,13 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
|
318
323
|
int read_to_eof = RTEST(to_eof);
|
|
319
324
|
VALUE underlying_io = rb_iv_get(io, "@io");
|
|
320
325
|
|
|
321
|
-
|
|
326
|
+
GetLibevBackend(self, backend);
|
|
322
327
|
if (underlying_io != Qnil) io = underlying_io;
|
|
323
328
|
GetOpenFile(io, fptr);
|
|
324
329
|
rb_io_check_byte_readable(fptr);
|
|
325
330
|
io_set_nonblock(fptr, io);
|
|
326
331
|
watcher.fiber = Qnil;
|
|
327
|
-
|
|
332
|
+
|
|
328
333
|
OBJ_TAINT(str);
|
|
329
334
|
|
|
330
335
|
// Apparently after reopening a closed file, the file position is not reset,
|
|
@@ -341,12 +346,14 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
|
341
346
|
if (n < 0) {
|
|
342
347
|
int e = errno;
|
|
343
348
|
if (e != EWOULDBLOCK && e != EAGAIN) rb_syserr_fail(e, strerror(e));
|
|
344
|
-
|
|
345
|
-
switchpoint_result =
|
|
349
|
+
|
|
350
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
|
|
351
|
+
|
|
346
352
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
347
353
|
}
|
|
348
354
|
else {
|
|
349
355
|
switchpoint_result = libev_snooze();
|
|
356
|
+
|
|
350
357
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
351
358
|
|
|
352
359
|
if (n == 0) break; // EOF
|
|
@@ -355,7 +362,7 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
|
355
362
|
|
|
356
363
|
if (total == len) {
|
|
357
364
|
if (!dynamic_len) break;
|
|
358
|
-
|
|
365
|
+
|
|
359
366
|
rb_str_resize(str, total);
|
|
360
367
|
rb_str_modify_expand(str, len);
|
|
361
368
|
buf = RSTRING_PTR(str) + total;
|
|
@@ -365,20 +372,21 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
|
365
372
|
else buf += n;
|
|
366
373
|
}
|
|
367
374
|
}
|
|
368
|
-
if (total == 0) return Qnil;
|
|
369
375
|
|
|
370
376
|
io_set_read_length(str, total, shrinkable);
|
|
371
377
|
io_enc_str(str, fptr);
|
|
372
|
-
|
|
378
|
+
|
|
379
|
+
if (total == 0) return Qnil;
|
|
380
|
+
|
|
373
381
|
RB_GC_GUARD(watcher.fiber);
|
|
374
382
|
RB_GC_GUARD(switchpoint_result);
|
|
375
383
|
|
|
376
384
|
return str;
|
|
377
385
|
error:
|
|
378
|
-
return
|
|
386
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
379
387
|
}
|
|
380
388
|
|
|
381
|
-
VALUE
|
|
389
|
+
VALUE LibevBackend_read_loop(VALUE self, VALUE io) {
|
|
382
390
|
|
|
383
391
|
#define PREPARE_STR() { \
|
|
384
392
|
str = Qnil; \
|
|
@@ -395,7 +403,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
|
395
403
|
PREPARE_STR(); \
|
|
396
404
|
}
|
|
397
405
|
|
|
398
|
-
|
|
406
|
+
LibevBackend_t *backend;
|
|
399
407
|
struct libev_io watcher;
|
|
400
408
|
rb_io_t *fptr;
|
|
401
409
|
VALUE str;
|
|
@@ -408,7 +416,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
|
408
416
|
|
|
409
417
|
PREPARE_STR();
|
|
410
418
|
|
|
411
|
-
|
|
419
|
+
GetLibevBackend(self, backend);
|
|
412
420
|
if (underlying_io != Qnil) io = underlying_io;
|
|
413
421
|
GetOpenFile(io, fptr);
|
|
414
422
|
rb_io_check_byte_readable(fptr);
|
|
@@ -430,22 +438,17 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
|
430
438
|
int e = errno;
|
|
431
439
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
432
440
|
|
|
433
|
-
switchpoint_result =
|
|
441
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
|
|
434
442
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
435
443
|
}
|
|
436
444
|
else {
|
|
437
445
|
switchpoint_result = libev_snooze();
|
|
438
|
-
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
439
446
|
|
|
440
|
-
if (
|
|
447
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
441
448
|
|
|
449
|
+
if (n == 0) break; // EOF
|
|
442
450
|
total = n;
|
|
443
451
|
YIELD_STR();
|
|
444
|
-
Fiber_make_runnable(rb_fiber_current(), Qnil);
|
|
445
|
-
switchpoint_result = Thread_switch_fiber(rb_thread_current());
|
|
446
|
-
if (TEST_EXCEPTION(switchpoint_result)) {
|
|
447
|
-
goto error;
|
|
448
|
-
}
|
|
449
452
|
}
|
|
450
453
|
}
|
|
451
454
|
|
|
@@ -455,11 +458,11 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
|
455
458
|
|
|
456
459
|
return io;
|
|
457
460
|
error:
|
|
458
|
-
return
|
|
461
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
459
462
|
}
|
|
460
463
|
|
|
461
|
-
VALUE
|
|
462
|
-
|
|
464
|
+
VALUE LibevBackend_write(VALUE self, VALUE io, VALUE str) {
|
|
465
|
+
LibevBackend_t *backend;
|
|
463
466
|
struct libev_io watcher;
|
|
464
467
|
rb_io_t *fptr;
|
|
465
468
|
VALUE switchpoint_result = Qnil;
|
|
@@ -470,7 +473,7 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
|
|
470
473
|
|
|
471
474
|
underlying_io = rb_iv_get(io, "@io");
|
|
472
475
|
if (underlying_io != Qnil) io = underlying_io;
|
|
473
|
-
|
|
476
|
+
GetLibevBackend(self, backend);
|
|
474
477
|
io = rb_io_get_write_io(io);
|
|
475
478
|
GetOpenFile(io, fptr);
|
|
476
479
|
watcher.fiber = Qnil;
|
|
@@ -480,7 +483,9 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
|
|
480
483
|
if (n < 0) {
|
|
481
484
|
int e = errno;
|
|
482
485
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
483
|
-
|
|
486
|
+
|
|
487
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
|
|
488
|
+
|
|
484
489
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
485
490
|
}
|
|
486
491
|
else {
|
|
@@ -491,6 +496,7 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
|
|
491
496
|
|
|
492
497
|
if (watcher.fiber == Qnil) {
|
|
493
498
|
switchpoint_result = libev_snooze();
|
|
499
|
+
|
|
494
500
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
495
501
|
}
|
|
496
502
|
|
|
@@ -499,11 +505,11 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
|
|
499
505
|
|
|
500
506
|
return INT2NUM(len);
|
|
501
507
|
error:
|
|
502
|
-
return
|
|
508
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
503
509
|
}
|
|
504
510
|
|
|
505
|
-
VALUE
|
|
506
|
-
|
|
511
|
+
VALUE LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
512
|
+
LibevBackend_t *backend;
|
|
507
513
|
struct libev_io watcher;
|
|
508
514
|
rb_io_t *fptr;
|
|
509
515
|
VALUE switchpoint_result = Qnil;
|
|
@@ -516,7 +522,7 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
|
516
522
|
|
|
517
523
|
underlying_io = rb_iv_get(io, "@io");
|
|
518
524
|
if (underlying_io != Qnil) io = underlying_io;
|
|
519
|
-
|
|
525
|
+
GetLibevBackend(self, backend);
|
|
520
526
|
io = rb_io_get_write_io(io);
|
|
521
527
|
GetOpenFile(io, fptr);
|
|
522
528
|
watcher.fiber = Qnil;
|
|
@@ -536,7 +542,8 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
|
536
542
|
int e = errno;
|
|
537
543
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
538
544
|
|
|
539
|
-
switchpoint_result =
|
|
545
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
|
|
546
|
+
|
|
540
547
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
541
548
|
}
|
|
542
549
|
else {
|
|
@@ -559,6 +566,7 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
|
559
566
|
}
|
|
560
567
|
if (watcher.fiber == Qnil) {
|
|
561
568
|
switchpoint_result = libev_snooze();
|
|
569
|
+
|
|
562
570
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
563
571
|
}
|
|
564
572
|
|
|
@@ -569,23 +577,23 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
|
569
577
|
return INT2NUM(total_written);
|
|
570
578
|
error:
|
|
571
579
|
free(iov);
|
|
572
|
-
return
|
|
580
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
573
581
|
}
|
|
574
582
|
|
|
575
|
-
VALUE
|
|
583
|
+
VALUE LibevBackend_write_m(int argc, VALUE *argv, VALUE self) {
|
|
576
584
|
if (argc < 2)
|
|
577
585
|
// TODO: raise ArgumentError
|
|
578
586
|
rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
|
|
579
|
-
|
|
587
|
+
|
|
580
588
|
return (argc == 2) ?
|
|
581
|
-
|
|
582
|
-
|
|
589
|
+
LibevBackend_write(self, argv[0], argv[1]) :
|
|
590
|
+
LibevBackend_writev(self, argv[0], argc - 1, argv + 1);
|
|
583
591
|
}
|
|
584
592
|
|
|
585
593
|
///////////////////////////////////////////////////////////////////////////
|
|
586
594
|
|
|
587
|
-
VALUE
|
|
588
|
-
|
|
595
|
+
VALUE LibevBackend_accept(VALUE self, VALUE sock) {
|
|
596
|
+
LibevBackend_t *backend;
|
|
589
597
|
struct libev_io watcher;
|
|
590
598
|
rb_io_t *fptr;
|
|
591
599
|
int fd;
|
|
@@ -595,7 +603,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
|
595
603
|
VALUE underlying_sock = rb_iv_get(sock, "@io");
|
|
596
604
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
|
597
605
|
|
|
598
|
-
|
|
606
|
+
GetLibevBackend(self, backend);
|
|
599
607
|
GetOpenFile(sock, fptr);
|
|
600
608
|
io_set_nonblock(fptr, sock);
|
|
601
609
|
watcher.fiber = Qnil;
|
|
@@ -605,13 +613,15 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
|
605
613
|
int e = errno;
|
|
606
614
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
607
615
|
|
|
608
|
-
switchpoint_result =
|
|
616
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
|
|
617
|
+
|
|
609
618
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
610
619
|
}
|
|
611
620
|
else {
|
|
612
621
|
VALUE socket;
|
|
613
622
|
rb_io_t *fp;
|
|
614
623
|
switchpoint_result = libev_snooze();
|
|
624
|
+
|
|
615
625
|
if (TEST_EXCEPTION(switchpoint_result)) {
|
|
616
626
|
close(fd); // close fd since we're raising an exception
|
|
617
627
|
goto error;
|
|
@@ -625,7 +635,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
|
625
635
|
rb_io_ascii8bit_binmode(socket);
|
|
626
636
|
io_set_nonblock(fp, socket);
|
|
627
637
|
rb_io_synchronized(fp);
|
|
628
|
-
|
|
638
|
+
|
|
629
639
|
// if (rsock_do_not_reverse_lookup) {
|
|
630
640
|
// fp->mode |= FMODE_NOREVLOOKUP;
|
|
631
641
|
// }
|
|
@@ -635,11 +645,11 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
|
635
645
|
RB_GC_GUARD(switchpoint_result);
|
|
636
646
|
return Qnil;
|
|
637
647
|
error:
|
|
638
|
-
return
|
|
648
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
639
649
|
}
|
|
640
650
|
|
|
641
|
-
VALUE
|
|
642
|
-
|
|
651
|
+
VALUE LibevBackend_accept_loop(VALUE self, VALUE sock) {
|
|
652
|
+
LibevBackend_t *backend;
|
|
643
653
|
struct libev_io watcher;
|
|
644
654
|
rb_io_t *fptr;
|
|
645
655
|
int fd;
|
|
@@ -650,7 +660,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
|
650
660
|
VALUE underlying_sock = rb_iv_get(sock, "@io");
|
|
651
661
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
|
652
662
|
|
|
653
|
-
|
|
663
|
+
GetLibevBackend(self, backend);
|
|
654
664
|
GetOpenFile(sock, fptr);
|
|
655
665
|
io_set_nonblock(fptr, sock);
|
|
656
666
|
watcher.fiber = Qnil;
|
|
@@ -661,17 +671,19 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
|
661
671
|
int e = errno;
|
|
662
672
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
663
673
|
|
|
664
|
-
switchpoint_result =
|
|
674
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
|
|
675
|
+
|
|
665
676
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
666
677
|
}
|
|
667
678
|
else {
|
|
668
679
|
rb_io_t *fp;
|
|
669
680
|
switchpoint_result = libev_snooze();
|
|
681
|
+
|
|
670
682
|
if (TEST_EXCEPTION(switchpoint_result)) {
|
|
671
683
|
close(fd); // close fd since we're raising an exception
|
|
672
684
|
goto error;
|
|
673
685
|
}
|
|
674
|
-
|
|
686
|
+
|
|
675
687
|
socket = rb_obj_alloc(cTCPSocket);
|
|
676
688
|
MakeOpenFile(socket, fp);
|
|
677
689
|
rb_update_max_fd(fd);
|
|
@@ -691,72 +703,58 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
|
691
703
|
RB_GC_GUARD(switchpoint_result);
|
|
692
704
|
return Qnil;
|
|
693
705
|
error:
|
|
694
|
-
return
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
// struct libev_io watcher;
|
|
700
|
-
// rb_io_t *fptr;
|
|
701
|
-
// struct sockaddr_in addr;
|
|
702
|
-
// char *host_buf = StringValueCStr(host);
|
|
703
|
-
// VALUE switchpoint_result = Qnil;
|
|
704
|
-
// VALUE underlying_sock = rb_iv_get(sock, "@io");
|
|
705
|
-
// if (underlying_sock != Qnil) sock = underlying_sock;
|
|
706
|
-
|
|
707
|
-
// GetLibevAgent(self, agent);
|
|
708
|
-
// GetOpenFile(sock, fptr);
|
|
709
|
-
// io_set_nonblock(fptr, sock);
|
|
710
|
-
// watcher.fiber = Qnil;
|
|
711
|
-
|
|
712
|
-
// addr.sin_family = AF_INET;
|
|
713
|
-
// addr.sin_addr.s_addr = inet_addr(host_buf);
|
|
714
|
-
// addr.sin_port = htons(NUM2INT(port));
|
|
715
|
-
|
|
716
|
-
// while (1) {
|
|
717
|
-
// int result = connect(fptr->fd, &addr, sizeof(addr));
|
|
718
|
-
// if (result < 0) {
|
|
719
|
-
// int e = errno;
|
|
720
|
-
// if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
721
|
-
|
|
722
|
-
// switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
|
|
723
|
-
// if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
724
|
-
// }
|
|
725
|
-
// else {
|
|
726
|
-
// switchpoint_result = libev_snooze();
|
|
727
|
-
// if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
728
|
-
|
|
729
|
-
// return sock;
|
|
730
|
-
// }
|
|
731
|
-
// }
|
|
732
|
-
// RB_GC_GUARD(switchpoint_result);
|
|
733
|
-
// return Qnil;
|
|
734
|
-
// error:
|
|
735
|
-
// return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
|
736
|
-
// }
|
|
737
|
-
|
|
738
|
-
VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write) {
|
|
739
|
-
struct LibevAgent_t *agent;
|
|
706
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
VALUE LibevBackend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
710
|
+
LibevBackend_t *backend;
|
|
740
711
|
struct libev_io watcher;
|
|
741
712
|
rb_io_t *fptr;
|
|
713
|
+
struct sockaddr_in addr;
|
|
714
|
+
char *host_buf = StringValueCStr(host);
|
|
742
715
|
VALUE switchpoint_result = Qnil;
|
|
743
|
-
|
|
716
|
+
VALUE underlying_sock = rb_iv_get(sock, "@io");
|
|
717
|
+
if (underlying_sock != Qnil) sock = underlying_sock;
|
|
744
718
|
|
|
719
|
+
GetLibevBackend(self, backend);
|
|
720
|
+
GetOpenFile(sock, fptr);
|
|
721
|
+
io_set_nonblock(fptr, sock);
|
|
722
|
+
watcher.fiber = Qnil;
|
|
723
|
+
|
|
724
|
+
addr.sin_family = AF_INET;
|
|
725
|
+
addr.sin_addr.s_addr = inet_addr(host_buf);
|
|
726
|
+
addr.sin_port = htons(NUM2INT(port));
|
|
727
|
+
|
|
728
|
+
int result = connect(fptr->fd, (struct sockaddr *)&addr, sizeof(addr));
|
|
729
|
+
if (result < 0) {
|
|
730
|
+
int e = errno;
|
|
731
|
+
if (e != EINPROGRESS) rb_syserr_fail(e, strerror(e));
|
|
732
|
+
|
|
733
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
|
|
734
|
+
|
|
735
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
736
|
+
}
|
|
737
|
+
else {
|
|
738
|
+
switchpoint_result = libev_snooze();
|
|
739
|
+
|
|
740
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
741
|
+
}
|
|
742
|
+
RB_GC_GUARD(switchpoint_result);
|
|
743
|
+
return sock;
|
|
744
|
+
error:
|
|
745
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
VALUE LibevBackend_wait_io(VALUE self, VALUE io, VALUE write) {
|
|
749
|
+
LibevBackend_t *backend;
|
|
750
|
+
rb_io_t *fptr;
|
|
751
|
+
int events = RTEST(write) ? EV_WRITE : EV_READ;
|
|
745
752
|
VALUE underlying_io = rb_iv_get(io, "@io");
|
|
746
|
-
GetLibevAgent(self, agent);
|
|
747
753
|
if (underlying_io != Qnil) io = underlying_io;
|
|
754
|
+
GetLibevBackend(self, backend);
|
|
748
755
|
GetOpenFile(io, fptr);
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
ev_io_init(&watcher.io, LibevAgent_io_callback, fptr->fd, events);
|
|
752
|
-
ev_io_start(agent->ev_loop, &watcher.io);
|
|
753
|
-
switchpoint_result = libev_await(agent);
|
|
754
|
-
ev_io_stop(agent->ev_loop, &watcher.io);
|
|
755
|
-
|
|
756
|
-
TEST_RESUME_EXCEPTION(switchpoint_result);
|
|
757
|
-
RB_GC_GUARD(watcher.fiber);
|
|
758
|
-
RB_GC_GUARD(switchpoint_result);
|
|
759
|
-
return switchpoint_result;
|
|
756
|
+
|
|
757
|
+
return libev_wait_fd(backend, fptr->fd, events, 1);
|
|
760
758
|
}
|
|
761
759
|
|
|
762
760
|
struct libev_timer {
|
|
@@ -764,26 +762,25 @@ struct libev_timer {
|
|
|
764
762
|
VALUE fiber;
|
|
765
763
|
};
|
|
766
764
|
|
|
767
|
-
void
|
|
765
|
+
void LibevBackend_timer_callback(EV_P_ ev_timer *w, int revents)
|
|
768
766
|
{
|
|
769
767
|
struct libev_timer *watcher = (struct libev_timer *)w;
|
|
770
768
|
Fiber_make_runnable(watcher->fiber, Qnil);
|
|
771
769
|
}
|
|
772
770
|
|
|
773
|
-
VALUE
|
|
774
|
-
|
|
771
|
+
VALUE LibevBackend_sleep(VALUE self, VALUE duration) {
|
|
772
|
+
LibevBackend_t *backend;
|
|
775
773
|
struct libev_timer watcher;
|
|
776
774
|
VALUE switchpoint_result = Qnil;
|
|
777
775
|
|
|
778
|
-
|
|
776
|
+
GetLibevBackend(self, backend);
|
|
779
777
|
watcher.fiber = rb_fiber_current();
|
|
780
|
-
ev_timer_init(&watcher.timer,
|
|
781
|
-
ev_timer_start(
|
|
782
|
-
|
|
783
|
-
switchpoint_result = libev_await(agent);
|
|
778
|
+
ev_timer_init(&watcher.timer, LibevBackend_timer_callback, NUM2DBL(duration), 0.);
|
|
779
|
+
ev_timer_start(backend->ev_loop, &watcher.timer);
|
|
784
780
|
|
|
785
|
-
|
|
781
|
+
switchpoint_result = libev_await(backend);
|
|
786
782
|
|
|
783
|
+
ev_timer_stop(backend->ev_loop, &watcher.timer);
|
|
787
784
|
TEST_RESUME_EXCEPTION(switchpoint_result);
|
|
788
785
|
RB_GC_GUARD(watcher.fiber);
|
|
789
786
|
RB_GC_GUARD(switchpoint_result);
|
|
@@ -795,7 +792,7 @@ struct libev_child {
|
|
|
795
792
|
VALUE fiber;
|
|
796
793
|
};
|
|
797
794
|
|
|
798
|
-
void
|
|
795
|
+
void LibevBackend_child_callback(EV_P_ ev_child *w, int revents)
|
|
799
796
|
{
|
|
800
797
|
struct libev_child *watcher = (struct libev_child *)w;
|
|
801
798
|
int exit_status = w->rstatus >> 8; // weird, why should we do this?
|
|
@@ -805,78 +802,88 @@ void LibevAgent_child_callback(EV_P_ ev_child *w, int revents)
|
|
|
805
802
|
Fiber_make_runnable(watcher->fiber, status);
|
|
806
803
|
}
|
|
807
804
|
|
|
808
|
-
VALUE
|
|
809
|
-
|
|
805
|
+
VALUE LibevBackend_waitpid(VALUE self, VALUE pid) {
|
|
806
|
+
LibevBackend_t *backend;
|
|
810
807
|
struct libev_child watcher;
|
|
811
808
|
VALUE switchpoint_result = Qnil;
|
|
812
|
-
|
|
809
|
+
GetLibevBackend(self, backend);
|
|
813
810
|
|
|
814
811
|
watcher.fiber = rb_fiber_current();
|
|
815
|
-
ev_child_init(&watcher.child,
|
|
816
|
-
ev_child_start(
|
|
817
|
-
|
|
818
|
-
switchpoint_result = libev_await(
|
|
819
|
-
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);
|
|
820
816
|
|
|
817
|
+
ev_child_stop(backend->ev_loop, &watcher.child);
|
|
821
818
|
TEST_RESUME_EXCEPTION(switchpoint_result);
|
|
822
819
|
RB_GC_GUARD(watcher.fiber);
|
|
823
820
|
RB_GC_GUARD(switchpoint_result);
|
|
824
821
|
return switchpoint_result;
|
|
825
822
|
}
|
|
826
823
|
|
|
827
|
-
struct ev_loop *
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
return
|
|
824
|
+
struct ev_loop *LibevBackend_ev_loop(VALUE self) {
|
|
825
|
+
LibevBackend_t *backend;
|
|
826
|
+
GetLibevBackend(self, backend);
|
|
827
|
+
return backend->ev_loop;
|
|
831
828
|
}
|
|
832
829
|
|
|
833
|
-
void
|
|
830
|
+
void LibevBackend_async_callback(EV_P_ ev_async *w, int revents) { }
|
|
834
831
|
|
|
835
|
-
VALUE
|
|
836
|
-
|
|
837
|
-
struct ev_async async;
|
|
832
|
+
VALUE LibevBackend_wait_event(VALUE self, VALUE raise) {
|
|
833
|
+
LibevBackend_t *backend;
|
|
838
834
|
VALUE switchpoint_result = Qnil;
|
|
839
|
-
|
|
835
|
+
GetLibevBackend(self, backend);
|
|
836
|
+
|
|
837
|
+
struct ev_async async;
|
|
840
838
|
|
|
841
|
-
ev_async_init(&async,
|
|
842
|
-
ev_async_start(
|
|
843
|
-
|
|
844
|
-
switchpoint_result = libev_await(agent);
|
|
845
|
-
ev_async_stop(agent->ev_loop, &async);
|
|
839
|
+
ev_async_init(&async, LibevBackend_async_callback);
|
|
840
|
+
ev_async_start(backend->ev_loop, &async);
|
|
846
841
|
|
|
842
|
+
switchpoint_result = libev_await(backend);
|
|
843
|
+
|
|
844
|
+
ev_async_stop(backend->ev_loop, &async);
|
|
847
845
|
if (RTEST(raise)) TEST_RESUME_EXCEPTION(switchpoint_result);
|
|
848
846
|
RB_GC_GUARD(switchpoint_result);
|
|
849
847
|
return switchpoint_result;
|
|
850
848
|
}
|
|
851
849
|
|
|
852
|
-
void
|
|
850
|
+
void Init_LibevBackend() {
|
|
853
851
|
rb_require("socket");
|
|
854
852
|
cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
|
|
855
853
|
|
|
856
|
-
|
|
857
|
-
rb_define_alloc_func(
|
|
854
|
+
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cData);
|
|
855
|
+
rb_define_alloc_func(cBackend, LibevBackend_allocate);
|
|
858
856
|
|
|
859
|
-
rb_define_method(
|
|
860
|
-
rb_define_method(
|
|
861
|
-
rb_define_method(
|
|
862
|
-
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);
|
|
863
861
|
|
|
864
|
-
rb_define_method(
|
|
865
|
-
rb_define_method(
|
|
862
|
+
rb_define_method(cBackend, "ref", LibevBackend_ref, 0);
|
|
863
|
+
rb_define_method(cBackend, "unref", LibevBackend_unref, 0);
|
|
866
864
|
|
|
867
|
-
rb_define_method(
|
|
868
|
-
rb_define_method(
|
|
865
|
+
rb_define_method(cBackend, "poll", LibevBackend_poll, 3);
|
|
866
|
+
rb_define_method(cBackend, "break", LibevBackend_wakeup, 0);
|
|
869
867
|
|
|
870
|
-
rb_define_method(
|
|
871
|
-
rb_define_method(
|
|
872
|
-
rb_define_method(
|
|
873
|
-
rb_define_method(
|
|
874
|
-
rb_define_method(
|
|
875
|
-
|
|
876
|
-
rb_define_method(
|
|
877
|
-
rb_define_method(
|
|
878
|
-
rb_define_method(
|
|
879
|
-
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);
|
|
880
878
|
|
|
881
879
|
ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
|
|
880
|
+
|
|
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;
|
|
882
889
|
}
|