polyphony 0.43.8 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.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
|
}
|