polyphony 0.43.8 → 0.45.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -1
- data/CHANGELOG.md +38 -0
- data/Gemfile.lock +13 -11
- data/README.md +20 -5
- data/Rakefile +1 -1
- data/TODO.md +16 -14
- data/bin/stress.rb +28 -0
- 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/sequel_mysql.rb +23 -0
- data/examples/adapters/sequel_mysql_pool.rb +33 -0
- data/examples/core/{xx-agent.rb → xx-backend.rb} +5 -5
- data/examples/core/xx-channels.rb +4 -2
- data/examples/core/xx-using-a-mutex.rb +2 -1
- data/examples/io/xx-pry.rb +18 -0
- data/examples/io/xx-rack_server.rb +71 -0
- data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
- data/ext/polyphony/backend.h +41 -0
- data/ext/polyphony/event.c +86 -0
- data/ext/polyphony/extconf.rb +1 -1
- data/ext/polyphony/fiber.c +0 -5
- data/ext/polyphony/{libev_agent.c → libev_backend.c} +234 -228
- data/ext/polyphony/polyphony.c +4 -0
- data/ext/polyphony/polyphony.h +16 -16
- data/ext/polyphony/polyphony_ext.c +4 -2
- data/ext/polyphony/queue.c +52 -12
- data/ext/polyphony/thread.c +55 -42
- data/lib/polyphony.rb +25 -39
- 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/sequel.rb +45 -0
- data/lib/polyphony/core/channel.rb +3 -34
- data/lib/polyphony/core/exceptions.rb +11 -0
- data/lib/polyphony/core/global_api.rb +11 -6
- data/lib/polyphony/core/resource_pool.rb +22 -71
- data/lib/polyphony/core/sync.rb +48 -9
- data/lib/polyphony/core/throttler.rb +1 -1
- data/lib/polyphony/extensions/core.rb +37 -19
- data/lib/polyphony/extensions/fiber.rb +5 -1
- data/lib/polyphony/extensions/io.rb +7 -8
- data/lib/polyphony/extensions/openssl.rb +6 -6
- data/lib/polyphony/extensions/socket.rb +12 -22
- 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 +6 -3
- data/test/helper.rb +1 -1
- data/test/{test_agent.rb → test_backend.rb} +22 -22
- data/test/test_event.rb +1 -0
- data/test/test_fiber.rb +21 -5
- data/test/test_io.rb +1 -1
- data/test/test_kernel.rb +5 -0
- data/test/test_queue.rb +20 -0
- data/test/test_resource_pool.rb +34 -43
- data/test/test_signal.rb +5 -29
- data/test/test_sync.rb +52 -0
- metadata +74 -30
- data/.gitbook.yaml +0 -4
- data/lib/polyphony/event.rb +0 -17
@@ -123,7 +123,7 @@ suspend # The main fiber suspends, waiting for all other work to finish
|
|
123
123
|
sleep 1 # The sleeper fiber goes to sleep
|
124
124
|
Gyro::Timer.new(1, 0).await # A timer event watcher is setup and yields
|
125
125
|
Thread.current.switch_fiber # Polyphony looks for other runnable fibers
|
126
|
-
Thread.current.
|
126
|
+
Thread.current.backend.poll # With no work left, the event loop is ran
|
127
127
|
fiber.schedule # The timer event fires, scheduling the sleeper fiber
|
128
128
|
# <= The sleep method returns
|
129
129
|
puts "Woke up"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony/adapters/sequel'
|
5
|
+
require 'polyphony/adapters/mysql2'
|
6
|
+
|
7
|
+
time_printer = spin do
|
8
|
+
last = Time.now
|
9
|
+
throttled_loop(10) do
|
10
|
+
now = Time.now
|
11
|
+
puts now - last
|
12
|
+
last = now
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
db = Sequel.connect('mysql2://localhost/test')
|
17
|
+
|
18
|
+
x = 10_000
|
19
|
+
t0 = Time.now
|
20
|
+
x.times { db.execute('select 1 as test') }
|
21
|
+
puts "query rate: #{x / (Time.now - t0)} reqs/s"
|
22
|
+
|
23
|
+
time_printer.stop
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony/adapters/sequel'
|
5
|
+
require 'polyphony/adapters/mysql2'
|
6
|
+
|
7
|
+
CONCURRENCY = ARGV.first ? ARGV.first.to_i : 1000
|
8
|
+
puts "concurrency: #{CONCURRENCY}"
|
9
|
+
|
10
|
+
db = Sequel.connect(
|
11
|
+
'mysql2://localhost/test',
|
12
|
+
max_connections: 100,
|
13
|
+
preconnect: true
|
14
|
+
)
|
15
|
+
|
16
|
+
t0 = Time.now
|
17
|
+
count = 0
|
18
|
+
|
19
|
+
fibers = Array.new(CONCURRENCY) do
|
20
|
+
spin do
|
21
|
+
loop do
|
22
|
+
db.execute('select sleep(0.001) as test')
|
23
|
+
count += 1
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
sleep 0.1
|
29
|
+
fibers.first.terminate # Interrupt mid-query
|
30
|
+
|
31
|
+
sleep 2
|
32
|
+
puts "query rate: #{count / (Time.now - t0)} reqs/s"
|
33
|
+
fibers.each(&:interrupt)
|
@@ -27,17 +27,17 @@ class Test
|
|
27
27
|
|
28
28
|
def test_file
|
29
29
|
f = File.open(__FILE__, 'r')
|
30
|
-
puts Thread.current.
|
30
|
+
puts Thread.current.backend.read(f, +'', 10000, true)
|
31
31
|
|
32
|
-
Thread.current.
|
32
|
+
Thread.current.backend.write(STDOUT, "Write something: ")
|
33
33
|
str = +''
|
34
|
-
Thread.current.
|
34
|
+
Thread.current.backend.read(STDIN, str, 5, false)
|
35
35
|
puts str
|
36
36
|
end
|
37
37
|
|
38
38
|
def test_fork
|
39
39
|
pid = fork do
|
40
|
-
Thread.current.
|
40
|
+
Thread.current.backend.post_fork
|
41
41
|
puts 'child going to sleep'
|
42
42
|
sleep 1
|
43
43
|
puts 'child done sleeping'
|
@@ -45,7 +45,7 @@ class Test
|
|
45
45
|
end
|
46
46
|
|
47
47
|
puts "Waiting for pid #{pid}"
|
48
|
-
result = Thread.current.
|
48
|
+
result = Thread.current.backend.waitpid(pid)
|
49
49
|
puts "Done waiting"
|
50
50
|
p result
|
51
51
|
end
|
@@ -2,10 +2,12 @@
|
|
2
2
|
|
3
3
|
require 'bundler/setup'
|
4
4
|
require 'polyphony'
|
5
|
+
require 'polyphony/core/channel'
|
5
6
|
|
6
7
|
def echo(cin, cout)
|
7
8
|
puts 'start echoer'
|
8
9
|
while (msg = cin.receive)
|
10
|
+
puts "echoer received #{msg}"
|
9
11
|
cout << "you said: #{msg}"
|
10
12
|
end
|
11
13
|
ensure
|
@@ -20,7 +22,7 @@ spin do
|
|
20
22
|
puts 'start receiver'
|
21
23
|
while (msg = chan2.receive)
|
22
24
|
puts msg
|
23
|
-
$main.
|
25
|
+
$main.schedule if msg =~ /world/
|
24
26
|
end
|
25
27
|
ensure
|
26
28
|
puts 'receiver stopped'
|
@@ -42,4 +44,4 @@ $main = spin do
|
|
42
44
|
puts "done #{Time.now - t0}"
|
43
45
|
end
|
44
46
|
|
45
|
-
|
47
|
+
$main.await
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'bundler/setup'
|
4
4
|
require 'polyphony'
|
5
|
+
require 'polyphony/core/sync'
|
5
6
|
|
6
7
|
def loop_it(number, lock)
|
7
8
|
loop do
|
@@ -13,7 +14,7 @@ def loop_it(number, lock)
|
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
16
|
-
lock = Polyphony::
|
17
|
+
lock = Polyphony::Mutex.new
|
17
18
|
spin { loop_it(1, lock) }
|
18
19
|
spin { loop_it(2, lock) }
|
19
20
|
spin { loop_it(3, lock) }
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
require 'polyphony/adapters/readline'
|
6
|
+
require 'pry'
|
7
|
+
|
8
|
+
$counter = 0
|
9
|
+
timer = spin do
|
10
|
+
throttled_loop(5) do
|
11
|
+
$counter += 1
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
at_exit { timer.stop }
|
16
|
+
|
17
|
+
puts 'try typing $counter to see the counter incremented in the background'
|
18
|
+
binding.pry
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
require 'http/parser'
|
6
|
+
require 'rack'
|
7
|
+
|
8
|
+
module RackAdapter
|
9
|
+
class << self
|
10
|
+
def run(app)
|
11
|
+
->(socket, req) { respond(socket, req, app.(env(req))) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def env(req)
|
15
|
+
{}
|
16
|
+
end
|
17
|
+
|
18
|
+
def respond(socket, request, (status_code, headers, body))
|
19
|
+
body = body.join
|
20
|
+
headers = "Content-Type: text/plain\r\nContent-Length: #{body.bytesize}\r\n"
|
21
|
+
socket.write "HTTP/1.1 #{status_code}\r\n#{headers}\r\n#{body}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
$connection_count = 0
|
27
|
+
|
28
|
+
def handle_client(socket, &handler)
|
29
|
+
$connection_count += 1
|
30
|
+
parser = Http::Parser.new
|
31
|
+
reqs = []
|
32
|
+
parser.on_message_complete = proc do |env|
|
33
|
+
reqs << Object.new # parser
|
34
|
+
end
|
35
|
+
socket.read_loop do |data|
|
36
|
+
parser << data
|
37
|
+
while (req = reqs.shift)
|
38
|
+
handler.call(socket, req)
|
39
|
+
req = nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
rescue IOError, SystemCallError => e
|
43
|
+
# do nothing
|
44
|
+
ensure
|
45
|
+
$connection_count -= 1
|
46
|
+
socket&.close
|
47
|
+
end
|
48
|
+
|
49
|
+
def handle_request(client, parser)
|
50
|
+
status_code = "200 OK"
|
51
|
+
data = "Hello world!\n"
|
52
|
+
headers = "Content-Type: text/plain\r\nContent-Length: #{data.bytesize}\r\n"
|
53
|
+
client.write "HTTP/1.1 #{status_code}\r\n#{headers}\r\n#{data}"
|
54
|
+
end
|
55
|
+
|
56
|
+
server = TCPServer.open('0.0.0.0', 1234)
|
57
|
+
puts "pid #{Process.pid}"
|
58
|
+
puts "listening on port 1234"
|
59
|
+
|
60
|
+
app = RackAdapter.run(lambda { |env|
|
61
|
+
[
|
62
|
+
200,
|
63
|
+
{"Content-Type" => "text/plain"},
|
64
|
+
["Hello, world!\n"]
|
65
|
+
]
|
66
|
+
})
|
67
|
+
|
68
|
+
loop do
|
69
|
+
client = server.accept
|
70
|
+
spin { handle_client(client, &app) }
|
71
|
+
end
|
@@ -0,0 +1,41 @@
|
|
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 libev_backend_await(VALUE self);
|
11
|
+
// VALUE LibevBackend_connect(VALUE self, VALUE sock, VALUE host, VALUE port);
|
12
|
+
// VALUE LibevBackend_finalize(VALUE self);
|
13
|
+
// VALUE LibevBackend_post_fork(VALUE self);
|
14
|
+
// VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof);
|
15
|
+
// VALUE LibevBackend_read_loop(VALUE self, VALUE io);
|
16
|
+
// VALUE LibevBackend_sleep(VALUE self, VALUE duration);
|
17
|
+
// VALUE LibevBackend_wait_io(VALUE self, VALUE io, VALUE write);
|
18
|
+
// VALUE LibevBackend_wait_pid(VALUE self, VALUE pid);
|
19
|
+
// VALUE LibevBackend_write(int argc, VALUE *argv, VALUE self);
|
20
|
+
|
21
|
+
typedef VALUE (* backend_pending_count_t)(VALUE self);
|
22
|
+
typedef VALUE (*backend_poll_t)(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue);
|
23
|
+
typedef VALUE (* backend_ref_t)(VALUE self);
|
24
|
+
typedef int (* backend_ref_count_t)(VALUE self);
|
25
|
+
typedef void (* backend_reset_ref_count_t)(VALUE self);
|
26
|
+
typedef VALUE (* backend_unref_t)(VALUE self);
|
27
|
+
typedef VALUE (* backend_wait_event_t)(VALUE self, VALUE raise_on_exception);
|
28
|
+
typedef VALUE (* backend_wakeup_t)(VALUE self);
|
29
|
+
|
30
|
+
typedef struct backend_interface {
|
31
|
+
backend_pending_count_t pending_count;
|
32
|
+
backend_poll_t poll;
|
33
|
+
backend_ref_t ref;
|
34
|
+
backend_ref_count_t ref_count;
|
35
|
+
backend_reset_ref_count_t reset_ref_count;
|
36
|
+
backend_unref_t unref;
|
37
|
+
backend_wait_event_t wait_event;
|
38
|
+
backend_wakeup_t wakeup;
|
39
|
+
} backend_interface_t;
|
40
|
+
|
41
|
+
#endif /* BACKEND_H */
|
@@ -0,0 +1,86 @@
|
|
1
|
+
#include "polyphony.h"
|
2
|
+
#include "ring_buffer.h"
|
3
|
+
|
4
|
+
typedef struct event {
|
5
|
+
VALUE waiting_fiber;
|
6
|
+
} Event_t;
|
7
|
+
|
8
|
+
VALUE cEvent = Qnil;
|
9
|
+
|
10
|
+
static void Event_mark(void *ptr) {
|
11
|
+
Event_t *event = ptr;
|
12
|
+
rb_gc_mark(event->waiting_fiber);
|
13
|
+
}
|
14
|
+
|
15
|
+
static void Event_free(void *ptr) {
|
16
|
+
xfree(ptr);
|
17
|
+
}
|
18
|
+
|
19
|
+
static size_t Event_size(const void *ptr) {
|
20
|
+
return sizeof(Event_t);
|
21
|
+
}
|
22
|
+
|
23
|
+
static const rb_data_type_t Event_type = {
|
24
|
+
"Event",
|
25
|
+
{Event_mark, Event_free, Event_size,},
|
26
|
+
0, 0, 0
|
27
|
+
};
|
28
|
+
|
29
|
+
static VALUE Event_allocate(VALUE klass) {
|
30
|
+
Event_t *event;
|
31
|
+
|
32
|
+
event = ALLOC(Event_t);
|
33
|
+
return TypedData_Wrap_Struct(klass, &Event_type, event);
|
34
|
+
}
|
35
|
+
|
36
|
+
#define GetEvent(obj, event) \
|
37
|
+
TypedData_Get_Struct((obj), Event_t, &Event_type, (event))
|
38
|
+
|
39
|
+
static VALUE Event_initialize(VALUE self) {
|
40
|
+
Event_t *event;
|
41
|
+
GetEvent(self, event);
|
42
|
+
|
43
|
+
event->waiting_fiber = Qnil;
|
44
|
+
|
45
|
+
return self;
|
46
|
+
}
|
47
|
+
|
48
|
+
VALUE Event_signal(int argc, VALUE *argv, VALUE self) {
|
49
|
+
VALUE value = argc > 0 ? argv[0] : Qnil;
|
50
|
+
Event_t *event;
|
51
|
+
GetEvent(self, event);
|
52
|
+
|
53
|
+
if (event->waiting_fiber != Qnil) {
|
54
|
+
Fiber_make_runnable(event->waiting_fiber, value);
|
55
|
+
event->waiting_fiber = Qnil;
|
56
|
+
}
|
57
|
+
return self;
|
58
|
+
}
|
59
|
+
|
60
|
+
VALUE Event_await(VALUE self) {
|
61
|
+
Event_t *event;
|
62
|
+
GetEvent(self, event);
|
63
|
+
|
64
|
+
if (event->waiting_fiber != Qnil)
|
65
|
+
rb_raise(rb_eRuntimeError, "Event is already awaited by another fiber");
|
66
|
+
|
67
|
+
VALUE backend = rb_ivar_get(rb_thread_current(), ID_ivar_backend);
|
68
|
+
event->waiting_fiber = rb_fiber_current();
|
69
|
+
VALUE switchpoint_result = __BACKEND__.wait_event(backend, Qnil);
|
70
|
+
event->waiting_fiber = Qnil;
|
71
|
+
|
72
|
+
TEST_RESUME_EXCEPTION(switchpoint_result);
|
73
|
+
RB_GC_GUARD(backend);
|
74
|
+
RB_GC_GUARD(switchpoint_result);
|
75
|
+
|
76
|
+
return switchpoint_result;
|
77
|
+
}
|
78
|
+
|
79
|
+
void Init_Event() {
|
80
|
+
cEvent = rb_define_class_under(mPolyphony, "Event", rb_cData);
|
81
|
+
rb_define_alloc_func(cEvent, Event_allocate);
|
82
|
+
|
83
|
+
rb_define_method(cEvent, "initialize", Event_initialize, 0);
|
84
|
+
rb_define_method(cEvent, "await", Event_await, 0);
|
85
|
+
rb_define_method(cEvent, "signal", Event_signal, -1);
|
86
|
+
}
|
data/ext/polyphony/extconf.rb
CHANGED
data/ext/polyphony/fiber.c
CHANGED
@@ -9,8 +9,6 @@ ID ID_trace_runnable;
|
|
9
9
|
ID ID_trace_terminate;
|
10
10
|
ID ID_trace_wait;
|
11
11
|
|
12
|
-
VALUE cEvent = Qnil;
|
13
|
-
|
14
12
|
VALUE SYM_dead;
|
15
13
|
VALUE SYM_running;
|
16
14
|
VALUE SYM_runnable;
|
@@ -35,9 +33,6 @@ static VALUE Fiber_safe_transfer(int argc, VALUE *argv, VALUE self) {
|
|
35
33
|
|
36
34
|
inline VALUE Fiber_auto_watcher(VALUE self) {
|
37
35
|
VALUE watcher;
|
38
|
-
if (cEvent == Qnil) {
|
39
|
-
cEvent = rb_const_get(mPolyphony, rb_intern("Event"));
|
40
|
-
}
|
41
36
|
|
42
37
|
watcher = rb_ivar_get(self, ID_ivar_auto_watcher);
|
43
38
|
if (watcher == Qnil) {
|
@@ -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
|
-
|
32
|
+
static VALUE LibevBackend_allocate(VALUE klass) {
|
33
|
+
LibevBackend_t *backend = ALLOC(LibevBackend_t);
|
33
34
|
|
34
|
-
return TypedData_Wrap_Struct(klass, &
|
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
|
-
|
95
|
+
backend->ref_count++;
|
97
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
|
-
|
103
|
+
backend->ref_count--;
|
105
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
|
-
|
141
|
+
backend->run_no_wait_count = 0;
|
143
142
|
|
144
|
-
|
145
|
-
|
146
|
-
ev_run(
|
147
|
-
|
148
|
-
|
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,37 +238,37 @@ 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
|
-
|
259
|
-
|
260
|
-
return libev_await(
|
256
|
+
VALUE libev_backend_await(VALUE self) {
|
257
|
+
LibevBackend_t *backend;
|
258
|
+
GetLibevBackend(self, backend);
|
259
|
+
return libev_await(backend);
|
261
260
|
}
|
262
261
|
|
263
|
-
VALUE libev_io_wait(
|
262
|
+
VALUE libev_io_wait(LibevBackend_t *backend, struct libev_io *watcher, rb_io_t *fptr, int flags) {
|
264
263
|
VALUE switchpoint_result;
|
265
264
|
|
266
265
|
if (watcher->fiber == Qnil) {
|
267
266
|
watcher->fiber = rb_fiber_current();
|
268
|
-
ev_io_init(&watcher->io,
|
267
|
+
ev_io_init(&watcher->io, LibevBackend_io_callback, fptr->fd, flags);
|
269
268
|
}
|
270
|
-
ev_io_start(
|
271
|
-
switchpoint_result = libev_await(
|
272
|
-
ev_io_stop(
|
269
|
+
ev_io_start(backend->ev_loop, &watcher->io);
|
270
|
+
switchpoint_result = libev_await(backend);
|
271
|
+
ev_io_stop(backend->ev_loop, &watcher->io);
|
273
272
|
|
274
273
|
RB_GC_GUARD(switchpoint_result);
|
275
274
|
return switchpoint_result;
|
@@ -305,8 +304,8 @@ inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
|
|
305
304
|
return;
|
306
305
|
}
|
307
306
|
|
308
|
-
VALUE
|
309
|
-
|
307
|
+
VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
|
308
|
+
LibevBackend_t *backend;
|
310
309
|
struct libev_io watcher;
|
311
310
|
rb_io_t *fptr;
|
312
311
|
long dynamic_len = length == Qnil;
|
@@ -318,7 +317,7 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
318
317
|
int read_to_eof = RTEST(to_eof);
|
319
318
|
VALUE underlying_io = rb_iv_get(io, "@io");
|
320
319
|
|
321
|
-
|
320
|
+
GetLibevBackend(self, backend);
|
322
321
|
if (underlying_io != Qnil) io = underlying_io;
|
323
322
|
GetOpenFile(io, fptr);
|
324
323
|
rb_io_check_byte_readable(fptr);
|
@@ -342,7 +341,7 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
342
341
|
int e = errno;
|
343
342
|
if (e != EWOULDBLOCK && e != EAGAIN) rb_syserr_fail(e, strerror(e));
|
344
343
|
|
345
|
-
switchpoint_result = libev_io_wait(
|
344
|
+
switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_READ);
|
346
345
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
347
346
|
}
|
348
347
|
else {
|
@@ -375,10 +374,10 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
375
374
|
|
376
375
|
return str;
|
377
376
|
error:
|
378
|
-
return
|
377
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
379
378
|
}
|
380
379
|
|
381
|
-
VALUE
|
380
|
+
VALUE LibevBackend_read_loop(VALUE self, VALUE io) {
|
382
381
|
|
383
382
|
#define PREPARE_STR() { \
|
384
383
|
str = Qnil; \
|
@@ -395,7 +394,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
395
394
|
PREPARE_STR(); \
|
396
395
|
}
|
397
396
|
|
398
|
-
|
397
|
+
LibevBackend_t *backend;
|
399
398
|
struct libev_io watcher;
|
400
399
|
rb_io_t *fptr;
|
401
400
|
VALUE str;
|
@@ -408,7 +407,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
408
407
|
|
409
408
|
PREPARE_STR();
|
410
409
|
|
411
|
-
|
410
|
+
GetLibevBackend(self, backend);
|
412
411
|
if (underlying_io != Qnil) io = underlying_io;
|
413
412
|
GetOpenFile(io, fptr);
|
414
413
|
rb_io_check_byte_readable(fptr);
|
@@ -430,7 +429,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
430
429
|
int e = errno;
|
431
430
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
432
431
|
|
433
|
-
switchpoint_result = libev_io_wait(
|
432
|
+
switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_READ);
|
434
433
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
435
434
|
}
|
436
435
|
else {
|
@@ -455,11 +454,11 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
455
454
|
|
456
455
|
return io;
|
457
456
|
error:
|
458
|
-
return
|
457
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
459
458
|
}
|
460
459
|
|
461
|
-
VALUE
|
462
|
-
|
460
|
+
VALUE LibevBackend_write(VALUE self, VALUE io, VALUE str) {
|
461
|
+
LibevBackend_t *backend;
|
463
462
|
struct libev_io watcher;
|
464
463
|
rb_io_t *fptr;
|
465
464
|
VALUE switchpoint_result = Qnil;
|
@@ -470,7 +469,7 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
|
470
469
|
|
471
470
|
underlying_io = rb_iv_get(io, "@io");
|
472
471
|
if (underlying_io != Qnil) io = underlying_io;
|
473
|
-
|
472
|
+
GetLibevBackend(self, backend);
|
474
473
|
io = rb_io_get_write_io(io);
|
475
474
|
GetOpenFile(io, fptr);
|
476
475
|
watcher.fiber = Qnil;
|
@@ -480,7 +479,7 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
|
480
479
|
if (n < 0) {
|
481
480
|
int e = errno;
|
482
481
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
483
|
-
switchpoint_result = libev_io_wait(
|
482
|
+
switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_WRITE);
|
484
483
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
485
484
|
}
|
486
485
|
else {
|
@@ -499,11 +498,11 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
|
499
498
|
|
500
499
|
return INT2NUM(len);
|
501
500
|
error:
|
502
|
-
return
|
501
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
503
502
|
}
|
504
503
|
|
505
|
-
VALUE
|
506
|
-
|
504
|
+
VALUE LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
505
|
+
LibevBackend_t *backend;
|
507
506
|
struct libev_io watcher;
|
508
507
|
rb_io_t *fptr;
|
509
508
|
VALUE switchpoint_result = Qnil;
|
@@ -516,7 +515,7 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
516
515
|
|
517
516
|
underlying_io = rb_iv_get(io, "@io");
|
518
517
|
if (underlying_io != Qnil) io = underlying_io;
|
519
|
-
|
518
|
+
GetLibevBackend(self, backend);
|
520
519
|
io = rb_io_get_write_io(io);
|
521
520
|
GetOpenFile(io, fptr);
|
522
521
|
watcher.fiber = Qnil;
|
@@ -536,7 +535,7 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
536
535
|
int e = errno;
|
537
536
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
538
537
|
|
539
|
-
switchpoint_result = libev_io_wait(
|
538
|
+
switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_WRITE);
|
540
539
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
541
540
|
}
|
542
541
|
else {
|
@@ -569,23 +568,23 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
569
568
|
return INT2NUM(total_written);
|
570
569
|
error:
|
571
570
|
free(iov);
|
572
|
-
return
|
571
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
573
572
|
}
|
574
573
|
|
575
|
-
VALUE
|
574
|
+
VALUE LibevBackend_write_m(int argc, VALUE *argv, VALUE self) {
|
576
575
|
if (argc < 2)
|
577
576
|
// TODO: raise ArgumentError
|
578
577
|
rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
|
579
578
|
|
580
579
|
return (argc == 2) ?
|
581
|
-
|
582
|
-
|
580
|
+
LibevBackend_write(self, argv[0], argv[1]) :
|
581
|
+
LibevBackend_writev(self, argv[0], argc - 1, argv + 1);
|
583
582
|
}
|
584
583
|
|
585
584
|
///////////////////////////////////////////////////////////////////////////
|
586
585
|
|
587
|
-
VALUE
|
588
|
-
|
586
|
+
VALUE LibevBackend_accept(VALUE self, VALUE sock) {
|
587
|
+
LibevBackend_t *backend;
|
589
588
|
struct libev_io watcher;
|
590
589
|
rb_io_t *fptr;
|
591
590
|
int fd;
|
@@ -595,7 +594,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
595
594
|
VALUE underlying_sock = rb_iv_get(sock, "@io");
|
596
595
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
597
596
|
|
598
|
-
|
597
|
+
GetLibevBackend(self, backend);
|
599
598
|
GetOpenFile(sock, fptr);
|
600
599
|
io_set_nonblock(fptr, sock);
|
601
600
|
watcher.fiber = Qnil;
|
@@ -605,7 +604,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
605
604
|
int e = errno;
|
606
605
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
607
606
|
|
608
|
-
switchpoint_result = libev_io_wait(
|
607
|
+
switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_READ);
|
609
608
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
610
609
|
}
|
611
610
|
else {
|
@@ -635,11 +634,11 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
635
634
|
RB_GC_GUARD(switchpoint_result);
|
636
635
|
return Qnil;
|
637
636
|
error:
|
638
|
-
return
|
637
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
639
638
|
}
|
640
639
|
|
641
|
-
VALUE
|
642
|
-
|
640
|
+
VALUE LibevBackend_accept_loop(VALUE self, VALUE sock) {
|
641
|
+
LibevBackend_t *backend;
|
643
642
|
struct libev_io watcher;
|
644
643
|
rb_io_t *fptr;
|
645
644
|
int fd;
|
@@ -650,7 +649,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
650
649
|
VALUE underlying_sock = rb_iv_get(sock, "@io");
|
651
650
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
652
651
|
|
653
|
-
|
652
|
+
GetLibevBackend(self, backend);
|
654
653
|
GetOpenFile(sock, fptr);
|
655
654
|
io_set_nonblock(fptr, sock);
|
656
655
|
watcher.fiber = Qnil;
|
@@ -661,7 +660,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
661
660
|
int e = errno;
|
662
661
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
663
662
|
|
664
|
-
switchpoint_result = libev_io_wait(
|
663
|
+
switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_READ);
|
665
664
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
666
665
|
}
|
667
666
|
else {
|
@@ -691,98 +690,96 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
691
690
|
RB_GC_GUARD(switchpoint_result);
|
692
691
|
return Qnil;
|
693
692
|
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;
|
693
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
694
|
+
}
|
695
|
+
|
696
|
+
VALUE LibevBackend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
697
|
+
LibevBackend_t *backend;
|
740
698
|
struct libev_io watcher;
|
741
699
|
rb_io_t *fptr;
|
700
|
+
struct sockaddr_in addr;
|
701
|
+
char *host_buf = StringValueCStr(host);
|
742
702
|
VALUE switchpoint_result = Qnil;
|
743
|
-
|
703
|
+
VALUE underlying_sock = rb_iv_get(sock, "@io");
|
704
|
+
if (underlying_sock != Qnil) sock = underlying_sock;
|
744
705
|
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
706
|
+
GetLibevBackend(self, backend);
|
707
|
+
GetOpenFile(sock, fptr);
|
708
|
+
io_set_nonblock(fptr, sock);
|
709
|
+
watcher.fiber = Qnil;
|
710
|
+
|
711
|
+
addr.sin_family = AF_INET;
|
712
|
+
addr.sin_addr.s_addr = inet_addr(host_buf);
|
713
|
+
addr.sin_port = htons(NUM2INT(port));
|
714
|
+
|
715
|
+
int result = connect(fptr->fd, (struct sockaddr *)&addr, sizeof(addr));
|
716
|
+
if (result < 0) {
|
717
|
+
int e = errno;
|
718
|
+
if (e != EINPROGRESS) rb_syserr_fail(e, strerror(e));
|
719
|
+
switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_WRITE);
|
720
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
721
|
+
}
|
722
|
+
else {
|
723
|
+
switchpoint_result = libev_snooze();
|
724
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
725
|
+
}
|
726
|
+
RB_GC_GUARD(switchpoint_result);
|
727
|
+
return sock;
|
728
|
+
error:
|
729
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
730
|
+
}
|
731
|
+
|
732
|
+
VALUE libev_wait_fd(LibevBackend_t *backend, int fd, int events, int raise_exception) {
|
733
|
+
struct libev_io watcher;
|
734
|
+
VALUE switchpoint_result = Qnil;
|
749
735
|
|
750
736
|
watcher.fiber = rb_fiber_current();
|
751
|
-
ev_io_init(&watcher.io,
|
752
|
-
ev_io_start(
|
753
|
-
switchpoint_result = libev_await(
|
754
|
-
ev_io_stop(
|
737
|
+
ev_io_init(&watcher.io, LibevBackend_io_callback, fd, events);
|
738
|
+
ev_io_start(backend->ev_loop, &watcher.io);
|
739
|
+
switchpoint_result = libev_await(backend);
|
740
|
+
ev_io_stop(backend->ev_loop, &watcher.io);
|
755
741
|
|
756
|
-
TEST_RESUME_EXCEPTION(switchpoint_result);
|
757
|
-
RB_GC_GUARD(watcher.fiber);
|
742
|
+
if (raise_exception) TEST_RESUME_EXCEPTION(switchpoint_result);
|
758
743
|
RB_GC_GUARD(switchpoint_result);
|
759
744
|
return switchpoint_result;
|
760
745
|
}
|
761
746
|
|
747
|
+
VALUE LibevBackend_wait_io(VALUE self, VALUE io, VALUE write) {
|
748
|
+
LibevBackend_t *backend;
|
749
|
+
rb_io_t *fptr;
|
750
|
+
int events = RTEST(write) ? EV_WRITE : EV_READ;
|
751
|
+
VALUE underlying_io = rb_iv_get(io, "@io");
|
752
|
+
if (underlying_io != Qnil) io = underlying_io;
|
753
|
+
GetLibevBackend(self, backend);
|
754
|
+
GetOpenFile(io, fptr);
|
755
|
+
|
756
|
+
return libev_wait_fd(backend, fptr->fd, events, 1);
|
757
|
+
}
|
758
|
+
|
762
759
|
struct libev_timer {
|
763
760
|
struct ev_timer timer;
|
764
761
|
VALUE fiber;
|
765
762
|
};
|
766
763
|
|
767
|
-
void
|
764
|
+
void LibevBackend_timer_callback(EV_P_ ev_timer *w, int revents)
|
768
765
|
{
|
769
766
|
struct libev_timer *watcher = (struct libev_timer *)w;
|
770
767
|
Fiber_make_runnable(watcher->fiber, Qnil);
|
771
768
|
}
|
772
769
|
|
773
|
-
VALUE
|
774
|
-
|
770
|
+
VALUE LibevBackend_sleep(VALUE self, VALUE duration) {
|
771
|
+
LibevBackend_t *backend;
|
775
772
|
struct libev_timer watcher;
|
776
773
|
VALUE switchpoint_result = Qnil;
|
777
774
|
|
778
|
-
|
775
|
+
GetLibevBackend(self, backend);
|
779
776
|
watcher.fiber = rb_fiber_current();
|
780
|
-
ev_timer_init(&watcher.timer,
|
781
|
-
ev_timer_start(
|
777
|
+
ev_timer_init(&watcher.timer, LibevBackend_timer_callback, NUM2DBL(duration), 0.);
|
778
|
+
ev_timer_start(backend->ev_loop, &watcher.timer);
|
782
779
|
|
783
|
-
switchpoint_result = libev_await(
|
780
|
+
switchpoint_result = libev_await(backend);
|
784
781
|
|
785
|
-
ev_timer_stop(
|
782
|
+
ev_timer_stop(backend->ev_loop, &watcher.timer);
|
786
783
|
|
787
784
|
TEST_RESUME_EXCEPTION(switchpoint_result);
|
788
785
|
RB_GC_GUARD(watcher.fiber);
|
@@ -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,18 +802,18 @@ 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(
|
812
|
+
ev_child_init(&watcher.child, LibevBackend_child_callback, NUM2INT(pid), 0);
|
813
|
+
ev_child_start(backend->ev_loop, &watcher.child);
|
817
814
|
|
818
|
-
switchpoint_result = libev_await(
|
819
|
-
ev_child_stop(
|
815
|
+
switchpoint_result = libev_await(backend);
|
816
|
+
ev_child_stop(backend->ev_loop, &watcher.child);
|
820
817
|
|
821
818
|
TEST_RESUME_EXCEPTION(switchpoint_result);
|
822
819
|
RB_GC_GUARD(watcher.fiber);
|
@@ -824,59 +821,68 @@ VALUE LibevAgent_waitpid(VALUE self, VALUE pid) {
|
|
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);
|
840
836
|
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
837
|
+
struct ev_async async;
|
838
|
+
|
839
|
+
ev_async_init(&async, LibevBackend_async_callback);
|
840
|
+
ev_async_start(backend->ev_loop, &async);
|
841
|
+
switchpoint_result = libev_await(backend);
|
842
|
+
ev_async_stop(backend->ev_loop, &async);
|
846
843
|
|
847
844
|
if (RTEST(raise)) TEST_RESUME_EXCEPTION(switchpoint_result);
|
848
845
|
RB_GC_GUARD(switchpoint_result);
|
849
846
|
return switchpoint_result;
|
850
847
|
}
|
851
848
|
|
852
|
-
void
|
849
|
+
void Init_LibevBackend() {
|
853
850
|
rb_require("socket");
|
854
851
|
cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
|
855
852
|
|
856
|
-
|
857
|
-
rb_define_alloc_func(
|
853
|
+
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cData);
|
854
|
+
rb_define_alloc_func(cBackend, LibevBackend_allocate);
|
858
855
|
|
859
|
-
rb_define_method(
|
860
|
-
rb_define_method(
|
861
|
-
rb_define_method(
|
862
|
-
rb_define_method(
|
856
|
+
rb_define_method(cBackend, "initialize", LibevBackend_initialize, 0);
|
857
|
+
rb_define_method(cBackend, "finalize", LibevBackend_finalize, 0);
|
858
|
+
rb_define_method(cBackend, "post_fork", LibevBackend_post_fork, 0);
|
859
|
+
rb_define_method(cBackend, "pending_count", LibevBackend_pending_count, 0);
|
863
860
|
|
864
|
-
rb_define_method(
|
865
|
-
rb_define_method(
|
861
|
+
rb_define_method(cBackend, "ref", LibevBackend_ref, 0);
|
862
|
+
rb_define_method(cBackend, "unref", LibevBackend_unref, 0);
|
866
863
|
|
867
|
-
rb_define_method(
|
868
|
-
rb_define_method(
|
864
|
+
rb_define_method(cBackend, "poll", LibevBackend_poll, 3);
|
865
|
+
rb_define_method(cBackend, "break", LibevBackend_wakeup, 0);
|
869
866
|
|
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(
|
867
|
+
rb_define_method(cBackend, "read", LibevBackend_read, 4);
|
868
|
+
rb_define_method(cBackend, "read_loop", LibevBackend_read_loop, 1);
|
869
|
+
rb_define_method(cBackend, "write", LibevBackend_write_m, -1);
|
870
|
+
rb_define_method(cBackend, "accept", LibevBackend_accept, 1);
|
871
|
+
rb_define_method(cBackend, "accept_loop", LibevBackend_accept_loop, 1);
|
872
|
+
rb_define_method(cBackend, "connect", LibevBackend_connect, 3);
|
873
|
+
rb_define_method(cBackend, "wait_io", LibevBackend_wait_io, 2);
|
874
|
+
rb_define_method(cBackend, "sleep", LibevBackend_sleep, 1);
|
875
|
+
rb_define_method(cBackend, "waitpid", LibevBackend_waitpid, 1);
|
876
|
+
rb_define_method(cBackend, "wait_event", LibevBackend_wait_event, 1);
|
880
877
|
|
881
878
|
ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
|
879
|
+
|
880
|
+
__BACKEND__.pending_count = LibevBackend_pending_count;
|
881
|
+
__BACKEND__.poll = LibevBackend_poll;
|
882
|
+
__BACKEND__.ref = LibevBackend_ref;
|
883
|
+
__BACKEND__.ref_count = LibevBackend_ref_count;
|
884
|
+
__BACKEND__.reset_ref_count = LibevBackend_reset_ref_count;
|
885
|
+
__BACKEND__.unref = LibevBackend_unref;
|
886
|
+
__BACKEND__.wait_event = LibevBackend_wait_event;
|
887
|
+
__BACKEND__.wakeup = LibevBackend_wakeup;
|
882
888
|
}
|