polyphony 0.43.5 → 0.43.11
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/CHANGELOG.md +45 -0
- data/Gemfile.lock +1 -1
- data/README.md +21 -4
- data/TODO.md +0 -7
- data/bin/stress.rb +28 -0
- data/docs/_includes/head.html +40 -0
- data/docs/_includes/title.html +1 -0
- data/docs/_user-guide/web-server.md +11 -11
- data/docs/getting-started/overview.md +2 -2
- data/docs/index.md +3 -1
- data/docs/polyphony-logo.png +0 -0
- data/examples/core/xx-channels.rb +4 -2
- data/examples/core/xx-using-a-mutex.rb +2 -1
- data/examples/io/xx-happy-eyeballs.rb +21 -22
- data/examples/io/xx-zip.rb +19 -0
- data/examples/performance/fiber_transfer.rb +47 -0
- data/examples/xx-spin.rb +32 -0
- data/ext/polyphony/agent.h +41 -0
- data/ext/polyphony/event.c +86 -0
- data/ext/polyphony/fiber.c +0 -5
- data/ext/polyphony/libev_agent.c +277 -135
- data/ext/polyphony/polyphony.c +2 -2
- data/ext/polyphony/polyphony.h +14 -21
- data/ext/polyphony/polyphony_ext.c +4 -2
- data/ext/polyphony/queue.c +208 -0
- data/ext/polyphony/ring_buffer.c +0 -24
- data/ext/polyphony/thread.c +42 -31
- data/lib/polyphony.rb +6 -7
- data/lib/polyphony/core/channel.rb +3 -34
- data/lib/polyphony/core/resource_pool.rb +13 -75
- data/lib/polyphony/core/sync.rb +12 -9
- data/lib/polyphony/extensions/fiber.rb +8 -8
- data/lib/polyphony/extensions/openssl.rb +8 -0
- data/lib/polyphony/extensions/socket.rb +11 -9
- data/lib/polyphony/extensions/thread.rb +1 -1
- data/lib/polyphony/net.rb +2 -1
- data/lib/polyphony/version.rb +1 -1
- data/test/helper.rb +2 -2
- data/test/test_agent.rb +2 -2
- data/test/test_event.rb +12 -0
- data/test/test_fiber.rb +1 -1
- data/test/test_io.rb +14 -0
- data/test/test_queue.rb +33 -0
- data/test/test_resource_pool.rb +24 -58
- data/test/test_trace.rb +18 -17
- metadata +12 -5
- data/ext/polyphony/libev_queue.c +0 -288
- data/lib/polyphony/event.rb +0 -27
data/examples/xx-spin.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
puts "pid: #{Process.pid}"
|
7
|
+
GC.disable
|
8
|
+
|
9
|
+
def mem_usage
|
10
|
+
# orig_backtick('ps -o rss #{$$}').split.last.to_i
|
11
|
+
`ps -o rss #{$$}`.split.last.to_i
|
12
|
+
end
|
13
|
+
|
14
|
+
f = File.open('spin.log', 'w+')
|
15
|
+
|
16
|
+
m0 = mem_usage
|
17
|
+
|
18
|
+
X = ARGV[0] ? ARGV[0].to_i : 10
|
19
|
+
STDOUT.orig_write "Starting #{X} fibers...\n"
|
20
|
+
t0 = Time.now
|
21
|
+
x = nil
|
22
|
+
X.times do |i|
|
23
|
+
spin { p i; suspend }
|
24
|
+
end
|
25
|
+
|
26
|
+
suspend
|
27
|
+
f.close
|
28
|
+
t1 = Time.now
|
29
|
+
m1 = mem_usage
|
30
|
+
rate = X / (t1 - t0)
|
31
|
+
mem_cost = (m1 - m0) / X.to_f
|
32
|
+
STDOUT.orig_write("#{ { time: t1 - t0, spin_rate: rate, fiber_mem_cost: mem_cost }.inspect }\n")
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#ifndef AGENT_H
|
2
|
+
#define AGENT_H
|
3
|
+
|
4
|
+
#include "ruby.h"
|
5
|
+
|
6
|
+
// agent interface function signatures
|
7
|
+
|
8
|
+
// VALUE LibevAgent_accept(VALUE self, VALUE sock);
|
9
|
+
// VALUE LibevAgent_accept_loop(VALUE self, VALUE sock);
|
10
|
+
// VALUE libev_agent_await(VALUE self);
|
11
|
+
// VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port);
|
12
|
+
// VALUE LibevAgent_finalize(VALUE self);
|
13
|
+
// VALUE LibevAgent_post_fork(VALUE self);
|
14
|
+
// VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof);
|
15
|
+
// VALUE LibevAgent_read_loop(VALUE self, VALUE io);
|
16
|
+
// VALUE LibevAgent_sleep(VALUE self, VALUE duration);
|
17
|
+
// VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write);
|
18
|
+
// VALUE LibevAgent_wait_pid(VALUE self, VALUE pid);
|
19
|
+
// VALUE LibevAgent_write(int argc, VALUE *argv, VALUE self);
|
20
|
+
|
21
|
+
typedef VALUE (* agent_pending_count_t)(VALUE self);
|
22
|
+
typedef VALUE (*agent_poll_t)(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue);
|
23
|
+
typedef VALUE (* agent_ref_t)(VALUE self);
|
24
|
+
typedef int (* agent_ref_count_t)(VALUE self);
|
25
|
+
typedef void (* agent_reset_ref_count_t)(VALUE self);
|
26
|
+
typedef VALUE (* agent_unref_t)(VALUE self);
|
27
|
+
typedef VALUE (* agent_wait_event_t)(VALUE self, VALUE raise_on_exception);
|
28
|
+
typedef VALUE (* agent_wakeup_t)(VALUE self);
|
29
|
+
|
30
|
+
typedef struct agent_interface {
|
31
|
+
agent_pending_count_t pending_count;
|
32
|
+
agent_poll_t poll;
|
33
|
+
agent_ref_t ref;
|
34
|
+
agent_ref_count_t ref_count;
|
35
|
+
agent_reset_ref_count_t reset_ref_count;
|
36
|
+
agent_unref_t unref;
|
37
|
+
agent_wait_event_t wait_event;
|
38
|
+
agent_wakeup_t wakeup;
|
39
|
+
} agent_interface_t;
|
40
|
+
|
41
|
+
#endif /* AGENT_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 agent = rb_ivar_get(rb_thread_current(), ID_ivar_agent);
|
68
|
+
event->waiting_fiber = rb_fiber_current();
|
69
|
+
VALUE switchpoint_result = __AGENT__.wait_event(agent, Qnil);
|
70
|
+
event->waiting_fiber = Qnil;
|
71
|
+
|
72
|
+
TEST_RESUME_EXCEPTION(switchpoint_result);
|
73
|
+
RB_GC_GUARD(agent);
|
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/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) {
|
data/ext/polyphony/libev_agent.c
CHANGED
@@ -1,22 +1,26 @@
|
|
1
1
|
#include <netdb.h>
|
2
2
|
#include <sys/socket.h>
|
3
|
+
#include <sys/uio.h>
|
4
|
+
#include <unistd.h>
|
5
|
+
#include <fcntl.h>
|
6
|
+
#include <netinet/in.h>
|
7
|
+
#include <arpa/inet.h>
|
3
8
|
|
4
9
|
#include "polyphony.h"
|
5
10
|
#include "../libev/ev.h"
|
6
11
|
|
7
|
-
VALUE cLibevAgent = Qnil;
|
8
12
|
VALUE cTCPSocket;
|
9
13
|
|
10
|
-
struct LibevAgent_t {
|
14
|
+
typedef struct LibevAgent_t {
|
11
15
|
struct ev_loop *ev_loop;
|
12
16
|
struct ev_async break_async;
|
13
17
|
int running;
|
14
18
|
int ref_count;
|
15
19
|
int run_no_wait_count;
|
16
|
-
};
|
20
|
+
} LibevAgent_t;
|
17
21
|
|
18
22
|
static size_t LibevAgent_size(const void *ptr) {
|
19
|
-
return sizeof(
|
23
|
+
return sizeof(LibevAgent_t);
|
20
24
|
}
|
21
25
|
|
22
26
|
static const rb_data_type_t LibevAgent_type = {
|
@@ -26,13 +30,13 @@ static const rb_data_type_t LibevAgent_type = {
|
|
26
30
|
};
|
27
31
|
|
28
32
|
static VALUE LibevAgent_allocate(VALUE klass) {
|
29
|
-
|
33
|
+
LibevAgent_t *agent = ALLOC(LibevAgent_t);
|
30
34
|
|
31
35
|
return TypedData_Wrap_Struct(klass, &LibevAgent_type, agent);
|
32
36
|
}
|
33
37
|
|
34
38
|
#define GetLibevAgent(obj, agent) \
|
35
|
-
TypedData_Get_Struct((obj),
|
39
|
+
TypedData_Get_Struct((obj), LibevAgent_t, &LibevAgent_type, (agent))
|
36
40
|
|
37
41
|
void break_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
|
38
42
|
// This callback does nothing, the break async is used solely for breaking out
|
@@ -40,7 +44,7 @@ void break_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, in
|
|
40
44
|
}
|
41
45
|
|
42
46
|
static VALUE LibevAgent_initialize(VALUE self) {
|
43
|
-
|
47
|
+
LibevAgent_t *agent;
|
44
48
|
VALUE thread = rb_thread_current();
|
45
49
|
int is_main_thread = (thread == rb_thread_main());
|
46
50
|
|
@@ -59,7 +63,7 @@ static VALUE LibevAgent_initialize(VALUE self) {
|
|
59
63
|
}
|
60
64
|
|
61
65
|
VALUE LibevAgent_finalize(VALUE self) {
|
62
|
-
|
66
|
+
LibevAgent_t *agent;
|
63
67
|
GetLibevAgent(self, agent);
|
64
68
|
|
65
69
|
ev_async_stop(agent->ev_loop, &agent->break_async);
|
@@ -70,7 +74,7 @@ VALUE LibevAgent_finalize(VALUE self) {
|
|
70
74
|
}
|
71
75
|
|
72
76
|
VALUE LibevAgent_post_fork(VALUE self) {
|
73
|
-
|
77
|
+
LibevAgent_t *agent;
|
74
78
|
GetLibevAgent(self, agent);
|
75
79
|
|
76
80
|
if (!ev_is_default_loop(agent->ev_loop)) {
|
@@ -87,7 +91,7 @@ VALUE LibevAgent_post_fork(VALUE self) {
|
|
87
91
|
}
|
88
92
|
|
89
93
|
VALUE LibevAgent_ref(VALUE self) {
|
90
|
-
|
94
|
+
LibevAgent_t *agent;
|
91
95
|
GetLibevAgent(self, agent);
|
92
96
|
|
93
97
|
agent->ref_count++;
|
@@ -95,7 +99,7 @@ VALUE LibevAgent_ref(VALUE self) {
|
|
95
99
|
}
|
96
100
|
|
97
101
|
VALUE LibevAgent_unref(VALUE self) {
|
98
|
-
|
102
|
+
LibevAgent_t *agent;
|
99
103
|
GetLibevAgent(self, agent);
|
100
104
|
|
101
105
|
agent->ref_count--;
|
@@ -103,14 +107,14 @@ VALUE LibevAgent_unref(VALUE self) {
|
|
103
107
|
}
|
104
108
|
|
105
109
|
int LibevAgent_ref_count(VALUE self) {
|
106
|
-
|
110
|
+
LibevAgent_t *agent;
|
107
111
|
GetLibevAgent(self, agent);
|
108
112
|
|
109
113
|
return agent->ref_count;
|
110
114
|
}
|
111
115
|
|
112
116
|
void LibevAgent_reset_ref_count(VALUE self) {
|
113
|
-
|
117
|
+
LibevAgent_t *agent;
|
114
118
|
GetLibevAgent(self, agent);
|
115
119
|
|
116
120
|
agent->ref_count = 0;
|
@@ -118,7 +122,7 @@ void LibevAgent_reset_ref_count(VALUE self) {
|
|
118
122
|
|
119
123
|
VALUE LibevAgent_pending_count(VALUE self) {
|
120
124
|
int count;
|
121
|
-
|
125
|
+
LibevAgent_t *agent;
|
122
126
|
GetLibevAgent(self, agent);
|
123
127
|
count = ev_pending_count(agent->ev_loop);
|
124
128
|
return INT2NUM(count);
|
@@ -126,11 +130,11 @@ VALUE LibevAgent_pending_count(VALUE self) {
|
|
126
130
|
|
127
131
|
VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue) {
|
128
132
|
int is_nowait = nowait == Qtrue;
|
129
|
-
|
133
|
+
LibevAgent_t *agent;
|
130
134
|
GetLibevAgent(self, agent);
|
131
135
|
|
132
136
|
if (is_nowait) {
|
133
|
-
long runnable_count =
|
137
|
+
long runnable_count = Queue_len(queue);
|
134
138
|
agent->run_no_wait_count++;
|
135
139
|
if (agent->run_no_wait_count < runnable_count || agent->run_no_wait_count < 10)
|
136
140
|
return self;
|
@@ -147,8 +151,8 @@ VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue
|
|
147
151
|
return self;
|
148
152
|
}
|
149
153
|
|
150
|
-
VALUE
|
151
|
-
|
154
|
+
VALUE LibevAgent_wakeup(VALUE self) {
|
155
|
+
LibevAgent_t *agent;
|
152
156
|
GetLibevAgent(self, agent);
|
153
157
|
|
154
158
|
if (agent->running) {
|
@@ -242,7 +246,7 @@ void LibevAgent_io_callback(EV_P_ ev_io *w, int revents)
|
|
242
246
|
Fiber_make_runnable(watcher->fiber, Qnil);
|
243
247
|
}
|
244
248
|
|
245
|
-
inline VALUE libev_await(
|
249
|
+
inline VALUE libev_await(LibevAgent_t *agent) {
|
246
250
|
VALUE ret;
|
247
251
|
agent->ref_count++;
|
248
252
|
ret = Thread_switch_fiber(rb_thread_current());
|
@@ -252,12 +256,12 @@ inline VALUE libev_await(struct LibevAgent_t *agent) {
|
|
252
256
|
}
|
253
257
|
|
254
258
|
VALUE libev_agent_await(VALUE self) {
|
255
|
-
|
259
|
+
LibevAgent_t *agent;
|
256
260
|
GetLibevAgent(self, agent);
|
257
261
|
return libev_await(agent);
|
258
262
|
}
|
259
263
|
|
260
|
-
VALUE libev_io_wait(
|
264
|
+
VALUE libev_io_wait(LibevAgent_t *agent, struct libev_io *watcher, rb_io_t *fptr, int flags) {
|
261
265
|
VALUE switchpoint_result;
|
262
266
|
|
263
267
|
if (watcher->fiber == Qnil) {
|
@@ -277,8 +281,33 @@ VALUE libev_snooze() {
|
|
277
281
|
return Thread_switch_fiber(rb_thread_current());
|
278
282
|
}
|
279
283
|
|
284
|
+
ID ID_ivar_is_nonblocking;
|
285
|
+
|
286
|
+
// Since we need to ensure that fd's are non-blocking before every I/O
|
287
|
+
// operation, here we improve upon Ruby's rb_io_set_nonblock by caching the
|
288
|
+
// "nonblock" state in an instance variable. Calling rb_ivar_get on every read
|
289
|
+
// is still much cheaper than doing a fcntl syscall on every read! Preliminary
|
290
|
+
// benchmarks (with a "hello world" HTTP server) show throughput is improved
|
291
|
+
// by 10-13%.
|
292
|
+
inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
|
293
|
+
#ifdef _WIN32
|
294
|
+
return rb_w32_set_nonblock(fptr->fd);
|
295
|
+
#elif defined(F_GETFL)
|
296
|
+
VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
|
297
|
+
if (is_nonblocking == Qnil) {
|
298
|
+
rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
|
299
|
+
int oflags = fcntl(fptr->fd, F_GETFL);
|
300
|
+
if (oflags == -1) return;
|
301
|
+
if (oflags & O_NONBLOCK) return;
|
302
|
+
oflags |= O_NONBLOCK;
|
303
|
+
fcntl(fptr->fd, F_SETFL, oflags);
|
304
|
+
}
|
305
|
+
#endif
|
306
|
+
return;
|
307
|
+
}
|
308
|
+
|
280
309
|
VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
|
281
|
-
|
310
|
+
LibevAgent_t *agent;
|
282
311
|
struct libev_io watcher;
|
283
312
|
rb_io_t *fptr;
|
284
313
|
long dynamic_len = length == Qnil;
|
@@ -294,11 +323,20 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
294
323
|
if (underlying_io != Qnil) io = underlying_io;
|
295
324
|
GetOpenFile(io, fptr);
|
296
325
|
rb_io_check_byte_readable(fptr);
|
297
|
-
|
326
|
+
io_set_nonblock(fptr, io);
|
298
327
|
watcher.fiber = Qnil;
|
299
328
|
|
300
329
|
OBJ_TAINT(str);
|
301
330
|
|
331
|
+
// Apparently after reopening a closed file, the file position is not reset,
|
332
|
+
// which causes the read to fail. Fortunately we can use fptr->rbuf.len to
|
333
|
+
// find out if that's the case.
|
334
|
+
// See: https://github.com/digital-fabric/polyphony/issues/30
|
335
|
+
if (fptr->rbuf.len > 0) {
|
336
|
+
lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
|
337
|
+
fptr->rbuf.len = 0;
|
338
|
+
}
|
339
|
+
|
302
340
|
while (1) {
|
303
341
|
ssize_t n = read(fptr->fd, buf, len - total);
|
304
342
|
if (n < 0) {
|
@@ -348,6 +386,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
348
386
|
shrinkable = io_setstrbuf(&str, len); \
|
349
387
|
buf = RSTRING_PTR(str); \
|
350
388
|
total = 0; \
|
389
|
+
OBJ_TAINT(str); \
|
351
390
|
}
|
352
391
|
|
353
392
|
#define YIELD_STR() { \
|
@@ -357,7 +396,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
357
396
|
PREPARE_STR(); \
|
358
397
|
}
|
359
398
|
|
360
|
-
|
399
|
+
LibevAgent_t *agent;
|
361
400
|
struct libev_io watcher;
|
362
401
|
rb_io_t *fptr;
|
363
402
|
VALUE str;
|
@@ -374,10 +413,17 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
374
413
|
if (underlying_io != Qnil) io = underlying_io;
|
375
414
|
GetOpenFile(io, fptr);
|
376
415
|
rb_io_check_byte_readable(fptr);
|
377
|
-
|
416
|
+
io_set_nonblock(fptr, io);
|
378
417
|
watcher.fiber = Qnil;
|
379
418
|
|
380
|
-
|
419
|
+
// Apparently after reopening a closed file, the file position is not reset,
|
420
|
+
// which causes the read to fail. Fortunately we can use fptr->rbuf.len to
|
421
|
+
// find out if that's the case.
|
422
|
+
// See: https://github.com/digital-fabric/polyphony/issues/30
|
423
|
+
if (fptr->rbuf.len > 0) {
|
424
|
+
lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
|
425
|
+
fptr->rbuf.len = 0;
|
426
|
+
}
|
381
427
|
|
382
428
|
while (1) {
|
383
429
|
ssize_t n = read(fptr->fd, buf, len);
|
@@ -413,20 +459,62 @@ error:
|
|
413
459
|
return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
414
460
|
}
|
415
461
|
|
416
|
-
VALUE LibevAgent_write(
|
417
|
-
|
462
|
+
VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
463
|
+
LibevAgent_t *agent;
|
464
|
+
struct libev_io watcher;
|
465
|
+
rb_io_t *fptr;
|
466
|
+
VALUE switchpoint_result = Qnil;
|
467
|
+
VALUE underlying_io;
|
468
|
+
char *buf = StringValuePtr(str);
|
469
|
+
long len = RSTRING_LEN(str);
|
470
|
+
long left = len;
|
471
|
+
|
472
|
+
underlying_io = rb_iv_get(io, "@io");
|
473
|
+
if (underlying_io != Qnil) io = underlying_io;
|
474
|
+
GetLibevAgent(self, agent);
|
475
|
+
io = rb_io_get_write_io(io);
|
476
|
+
GetOpenFile(io, fptr);
|
477
|
+
watcher.fiber = Qnil;
|
478
|
+
|
479
|
+
while (left > 0) {
|
480
|
+
ssize_t n = write(fptr->fd, buf, left);
|
481
|
+
if (n < 0) {
|
482
|
+
int e = errno;
|
483
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
484
|
+
switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
|
485
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
486
|
+
}
|
487
|
+
else {
|
488
|
+
buf += n;
|
489
|
+
left -= n;
|
490
|
+
}
|
491
|
+
}
|
492
|
+
|
493
|
+
if (watcher.fiber == Qnil) {
|
494
|
+
switchpoint_result = libev_snooze();
|
495
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
496
|
+
}
|
497
|
+
|
498
|
+
RB_GC_GUARD(watcher.fiber);
|
499
|
+
RB_GC_GUARD(switchpoint_result);
|
500
|
+
|
501
|
+
return INT2NUM(len);
|
502
|
+
error:
|
503
|
+
return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
504
|
+
}
|
505
|
+
|
506
|
+
VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
507
|
+
LibevAgent_t *agent;
|
418
508
|
struct libev_io watcher;
|
419
509
|
rb_io_t *fptr;
|
420
510
|
VALUE switchpoint_result = Qnil;
|
421
|
-
VALUE io;
|
422
511
|
VALUE underlying_io;
|
512
|
+
long total_length = 0;
|
423
513
|
long total_written = 0;
|
424
|
-
|
514
|
+
struct iovec *iov = 0;
|
515
|
+
struct iovec *iov_ptr = 0;
|
516
|
+
int iov_count = argc;
|
425
517
|
|
426
|
-
if (argc < 2)
|
427
|
-
rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
|
428
|
-
|
429
|
-
io = argv[0];
|
430
518
|
underlying_io = rb_iv_get(io, "@io");
|
431
519
|
if (underlying_io != Qnil) io = underlying_io;
|
432
520
|
GetLibevAgent(self, agent);
|
@@ -434,30 +522,42 @@ VALUE LibevAgent_write(int argc, VALUE *argv, VALUE self) {
|
|
434
522
|
GetOpenFile(io, fptr);
|
435
523
|
watcher.fiber = Qnil;
|
436
524
|
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
525
|
+
iov = malloc(iov_count * sizeof(struct iovec));
|
526
|
+
for (int i = 0; i < argc; i++) {
|
527
|
+
VALUE str = argv[i];
|
528
|
+
iov[i].iov_base = StringValuePtr(str);
|
529
|
+
iov[i].iov_len = RSTRING_LEN(str);
|
530
|
+
total_length += iov[i].iov_len;
|
531
|
+
}
|
532
|
+
iov_ptr = iov;
|
442
533
|
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
534
|
+
while (1) {
|
535
|
+
ssize_t n = writev(fptr->fd, iov_ptr, iov_count);
|
536
|
+
if (n < 0) {
|
537
|
+
int e = errno;
|
538
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
448
539
|
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
540
|
+
switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
|
541
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
542
|
+
}
|
543
|
+
else {
|
544
|
+
total_written += n;
|
545
|
+
if (total_written == total_length) break;
|
546
|
+
|
547
|
+
while (n > 0) {
|
548
|
+
if ((size_t) n < iov_ptr[0].iov_len) {
|
549
|
+
iov_ptr[0].iov_base = (char *) iov_ptr[0].iov_base + n;
|
550
|
+
iov_ptr[0].iov_len -= n;
|
551
|
+
n = 0;
|
552
|
+
}
|
553
|
+
else {
|
554
|
+
n -= iov_ptr[0].iov_len;
|
555
|
+
iov_ptr += 1;
|
556
|
+
iov_count -= 1;
|
557
|
+
}
|
455
558
|
}
|
456
559
|
}
|
457
|
-
total_written += len;
|
458
|
-
arg_idx++;
|
459
560
|
}
|
460
|
-
|
461
561
|
if (watcher.fiber == Qnil) {
|
462
562
|
switchpoint_result = libev_snooze();
|
463
563
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
@@ -466,15 +566,27 @@ VALUE LibevAgent_write(int argc, VALUE *argv, VALUE self) {
|
|
466
566
|
RB_GC_GUARD(watcher.fiber);
|
467
567
|
RB_GC_GUARD(switchpoint_result);
|
468
568
|
|
569
|
+
free(iov);
|
469
570
|
return INT2NUM(total_written);
|
470
571
|
error:
|
572
|
+
free(iov);
|
471
573
|
return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
472
574
|
}
|
473
575
|
|
576
|
+
VALUE LibevAgent_write_m(int argc, VALUE *argv, VALUE self) {
|
577
|
+
if (argc < 2)
|
578
|
+
// TODO: raise ArgumentError
|
579
|
+
rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
|
580
|
+
|
581
|
+
return (argc == 2) ?
|
582
|
+
LibevAgent_write(self, argv[0], argv[1]) :
|
583
|
+
LibevAgent_writev(self, argv[0], argc - 1, argv + 1);
|
584
|
+
}
|
585
|
+
|
474
586
|
///////////////////////////////////////////////////////////////////////////
|
475
587
|
|
476
588
|
VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
477
|
-
|
589
|
+
LibevAgent_t *agent;
|
478
590
|
struct libev_io watcher;
|
479
591
|
rb_io_t *fptr;
|
480
592
|
int fd;
|
@@ -486,7 +598,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
486
598
|
|
487
599
|
GetLibevAgent(self, agent);
|
488
600
|
GetOpenFile(sock, fptr);
|
489
|
-
|
601
|
+
io_set_nonblock(fptr, sock);
|
490
602
|
watcher.fiber = Qnil;
|
491
603
|
while (1) {
|
492
604
|
fd = accept(fptr->fd, &addr, &len);
|
@@ -512,7 +624,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
512
624
|
fp->fd = fd;
|
513
625
|
fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
|
514
626
|
rb_io_ascii8bit_binmode(socket);
|
515
|
-
|
627
|
+
io_set_nonblock(fp, socket);
|
516
628
|
rb_io_synchronized(fp);
|
517
629
|
|
518
630
|
// if (rsock_do_not_reverse_lookup) {
|
@@ -528,7 +640,7 @@ error:
|
|
528
640
|
}
|
529
641
|
|
530
642
|
VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
531
|
-
|
643
|
+
LibevAgent_t *agent;
|
532
644
|
struct libev_io watcher;
|
533
645
|
rb_io_t *fptr;
|
534
646
|
int fd;
|
@@ -541,7 +653,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
541
653
|
|
542
654
|
GetLibevAgent(self, agent);
|
543
655
|
GetOpenFile(sock, fptr);
|
544
|
-
|
656
|
+
io_set_nonblock(fptr, sock);
|
545
657
|
watcher.fiber = Qnil;
|
546
658
|
|
547
659
|
while (1) {
|
@@ -567,7 +679,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
567
679
|
fp->fd = fd;
|
568
680
|
fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
|
569
681
|
rb_io_ascii8bit_binmode(socket);
|
570
|
-
|
682
|
+
io_set_nonblock(fp, socket);
|
571
683
|
rb_io_synchronized(fp);
|
572
684
|
|
573
685
|
rb_yield(socket);
|
@@ -583,71 +695,69 @@ error:
|
|
583
695
|
return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
584
696
|
}
|
585
697
|
|
586
|
-
|
587
|
-
|
588
|
-
// struct libev_io watcher;
|
589
|
-
// rb_io_t *fptr;
|
590
|
-
// struct sockaddr_in addr;
|
591
|
-
// char *host_buf = StringValueCStr(host);
|
592
|
-
// VALUE switchpoint_result = Qnil;
|
593
|
-
// VALUE underlying_sock = rb_iv_get(sock, "@io");
|
594
|
-
// if (underlying_sock != Qnil) sock = underlying_sock;
|
595
|
-
|
596
|
-
// GetLibevAgent(self, agent);
|
597
|
-
// GetOpenFile(sock, fptr);
|
598
|
-
// rb_io_set_nonblock(fptr);
|
599
|
-
// watcher.fiber = Qnil;
|
600
|
-
|
601
|
-
// addr.sin_family = AF_INET;
|
602
|
-
// addr.sin_addr.s_addr = inet_addr(host_buf);
|
603
|
-
// addr.sin_port = htons(NUM2INT(port));
|
604
|
-
|
605
|
-
// while (1) {
|
606
|
-
// int result = connect(fptr->fd, &addr, sizeof(addr));
|
607
|
-
// if (result < 0) {
|
608
|
-
// int e = errno;
|
609
|
-
// if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
610
|
-
|
611
|
-
// switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
|
612
|
-
// if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
613
|
-
// }
|
614
|
-
// else {
|
615
|
-
// switchpoint_result = libev_snooze();
|
616
|
-
// if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
617
|
-
|
618
|
-
// return sock;
|
619
|
-
// }
|
620
|
-
// }
|
621
|
-
// RB_GC_GUARD(switchpoint_result);
|
622
|
-
// return Qnil;
|
623
|
-
// error:
|
624
|
-
// return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
625
|
-
// }
|
626
|
-
|
627
|
-
VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write) {
|
628
|
-
struct LibevAgent_t *agent;
|
698
|
+
VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
699
|
+
LibevAgent_t *agent;
|
629
700
|
struct libev_io watcher;
|
630
701
|
rb_io_t *fptr;
|
702
|
+
struct sockaddr_in addr;
|
703
|
+
char *host_buf = StringValueCStr(host);
|
631
704
|
VALUE switchpoint_result = Qnil;
|
632
|
-
|
705
|
+
VALUE underlying_sock = rb_iv_get(sock, "@io");
|
706
|
+
if (underlying_sock != Qnil) sock = underlying_sock;
|
633
707
|
|
634
|
-
VALUE underlying_io = rb_iv_get(io, "@io");
|
635
708
|
GetLibevAgent(self, agent);
|
636
|
-
|
637
|
-
|
709
|
+
GetOpenFile(sock, fptr);
|
710
|
+
io_set_nonblock(fptr, sock);
|
711
|
+
watcher.fiber = Qnil;
|
712
|
+
|
713
|
+
addr.sin_family = AF_INET;
|
714
|
+
addr.sin_addr.s_addr = inet_addr(host_buf);
|
715
|
+
addr.sin_port = htons(NUM2INT(port));
|
716
|
+
|
717
|
+
int result = connect(fptr->fd, (struct sockaddr *)&addr, sizeof(addr));
|
718
|
+
if (result < 0) {
|
719
|
+
int e = errno;
|
720
|
+
if (e != EINPROGRESS) rb_syserr_fail(e, strerror(e));
|
721
|
+
switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
|
722
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
723
|
+
}
|
724
|
+
else {
|
725
|
+
switchpoint_result = libev_snooze();
|
726
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
727
|
+
}
|
728
|
+
RB_GC_GUARD(switchpoint_result);
|
729
|
+
return sock;
|
730
|
+
error:
|
731
|
+
return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
732
|
+
}
|
733
|
+
|
734
|
+
VALUE libev_wait_fd(LibevAgent_t *agent, int fd, int events, int raise_exception) {
|
735
|
+
struct libev_io watcher;
|
736
|
+
VALUE switchpoint_result = Qnil;
|
638
737
|
|
639
738
|
watcher.fiber = rb_fiber_current();
|
640
|
-
ev_io_init(&watcher.io, LibevAgent_io_callback,
|
739
|
+
ev_io_init(&watcher.io, LibevAgent_io_callback, fd, events);
|
641
740
|
ev_io_start(agent->ev_loop, &watcher.io);
|
642
741
|
switchpoint_result = libev_await(agent);
|
643
742
|
ev_io_stop(agent->ev_loop, &watcher.io);
|
644
743
|
|
645
|
-
TEST_RESUME_EXCEPTION(switchpoint_result);
|
646
|
-
RB_GC_GUARD(watcher.fiber);
|
744
|
+
if (raise_exception) TEST_RESUME_EXCEPTION(switchpoint_result);
|
647
745
|
RB_GC_GUARD(switchpoint_result);
|
648
746
|
return switchpoint_result;
|
649
747
|
}
|
650
748
|
|
749
|
+
VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write) {
|
750
|
+
LibevAgent_t *agent;
|
751
|
+
rb_io_t *fptr;
|
752
|
+
int events = RTEST(write) ? EV_WRITE : EV_READ;
|
753
|
+
VALUE underlying_io = rb_iv_get(io, "@io");
|
754
|
+
if (underlying_io != Qnil) io = underlying_io;
|
755
|
+
GetLibevAgent(self, agent);
|
756
|
+
GetOpenFile(io, fptr);
|
757
|
+
|
758
|
+
return libev_wait_fd(agent, fptr->fd, events, 1);
|
759
|
+
}
|
760
|
+
|
651
761
|
struct libev_timer {
|
652
762
|
struct ev_timer timer;
|
653
763
|
VALUE fiber;
|
@@ -660,7 +770,7 @@ void LibevAgent_timer_callback(EV_P_ ev_timer *w, int revents)
|
|
660
770
|
}
|
661
771
|
|
662
772
|
VALUE LibevAgent_sleep(VALUE self, VALUE duration) {
|
663
|
-
|
773
|
+
LibevAgent_t *agent;
|
664
774
|
struct libev_timer watcher;
|
665
775
|
VALUE switchpoint_result = Qnil;
|
666
776
|
|
@@ -670,6 +780,7 @@ VALUE LibevAgent_sleep(VALUE self, VALUE duration) {
|
|
670
780
|
ev_timer_start(agent->ev_loop, &watcher.timer);
|
671
781
|
|
672
782
|
switchpoint_result = libev_await(agent);
|
783
|
+
|
673
784
|
ev_timer_stop(agent->ev_loop, &watcher.timer);
|
674
785
|
|
675
786
|
TEST_RESUME_EXCEPTION(switchpoint_result);
|
@@ -694,7 +805,7 @@ void LibevAgent_child_callback(EV_P_ ev_child *w, int revents)
|
|
694
805
|
}
|
695
806
|
|
696
807
|
VALUE LibevAgent_waitpid(VALUE self, VALUE pid) {
|
697
|
-
|
808
|
+
LibevAgent_t *agent;
|
698
809
|
struct libev_child watcher;
|
699
810
|
VALUE switchpoint_result = Qnil;
|
700
811
|
GetLibevAgent(self, agent);
|
@@ -713,36 +824,67 @@ VALUE LibevAgent_waitpid(VALUE self, VALUE pid) {
|
|
713
824
|
}
|
714
825
|
|
715
826
|
struct ev_loop *LibevAgent_ev_loop(VALUE self) {
|
716
|
-
|
827
|
+
LibevAgent_t *agent;
|
717
828
|
GetLibevAgent(self, agent);
|
718
829
|
return agent->ev_loop;
|
719
830
|
}
|
720
831
|
|
832
|
+
void LibevAgent_async_callback(EV_P_ ev_async *w, int revents) { }
|
833
|
+
|
834
|
+
VALUE LibevAgent_wait_event(VALUE self, VALUE raise) {
|
835
|
+
LibevAgent_t *agent;
|
836
|
+
VALUE switchpoint_result = Qnil;
|
837
|
+
GetLibevAgent(self, agent);
|
838
|
+
|
839
|
+
struct ev_async async;
|
840
|
+
|
841
|
+
ev_async_init(&async, LibevAgent_async_callback);
|
842
|
+
ev_async_start(agent->ev_loop, &async);
|
843
|
+
switchpoint_result = libev_await(agent);
|
844
|
+
ev_async_stop(agent->ev_loop, &async);
|
845
|
+
|
846
|
+
if (RTEST(raise)) TEST_RESUME_EXCEPTION(switchpoint_result);
|
847
|
+
RB_GC_GUARD(switchpoint_result);
|
848
|
+
return switchpoint_result;
|
849
|
+
}
|
850
|
+
|
721
851
|
void Init_LibevAgent() {
|
722
852
|
rb_require("socket");
|
723
853
|
cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
|
724
854
|
|
725
|
-
|
726
|
-
rb_define_alloc_func(
|
727
|
-
|
728
|
-
rb_define_method(
|
729
|
-
rb_define_method(
|
730
|
-
rb_define_method(
|
731
|
-
rb_define_method(
|
732
|
-
|
733
|
-
rb_define_method(
|
734
|
-
rb_define_method(
|
735
|
-
|
736
|
-
rb_define_method(
|
737
|
-
rb_define_method(
|
738
|
-
|
739
|
-
rb_define_method(
|
740
|
-
rb_define_method(
|
741
|
-
rb_define_method(
|
742
|
-
rb_define_method(
|
743
|
-
rb_define_method(
|
744
|
-
|
745
|
-
rb_define_method(
|
746
|
-
rb_define_method(
|
747
|
-
rb_define_method(
|
855
|
+
VALUE cAgent = rb_define_class_under(mPolyphony, "Agent", rb_cData);
|
856
|
+
rb_define_alloc_func(cAgent, LibevAgent_allocate);
|
857
|
+
|
858
|
+
rb_define_method(cAgent, "initialize", LibevAgent_initialize, 0);
|
859
|
+
rb_define_method(cAgent, "finalize", LibevAgent_finalize, 0);
|
860
|
+
rb_define_method(cAgent, "post_fork", LibevAgent_post_fork, 0);
|
861
|
+
rb_define_method(cAgent, "pending_count", LibevAgent_pending_count, 0);
|
862
|
+
|
863
|
+
rb_define_method(cAgent, "ref", LibevAgent_ref, 0);
|
864
|
+
rb_define_method(cAgent, "unref", LibevAgent_unref, 0);
|
865
|
+
|
866
|
+
rb_define_method(cAgent, "poll", LibevAgent_poll, 3);
|
867
|
+
rb_define_method(cAgent, "break", LibevAgent_wakeup, 0);
|
868
|
+
|
869
|
+
rb_define_method(cAgent, "read", LibevAgent_read, 4);
|
870
|
+
rb_define_method(cAgent, "read_loop", LibevAgent_read_loop, 1);
|
871
|
+
rb_define_method(cAgent, "write", LibevAgent_write_m, -1);
|
872
|
+
rb_define_method(cAgent, "accept", LibevAgent_accept, 1);
|
873
|
+
rb_define_method(cAgent, "accept_loop", LibevAgent_accept_loop, 1);
|
874
|
+
rb_define_method(cAgent, "connect", LibevAgent_connect, 3);
|
875
|
+
rb_define_method(cAgent, "wait_io", LibevAgent_wait_io, 2);
|
876
|
+
rb_define_method(cAgent, "sleep", LibevAgent_sleep, 1);
|
877
|
+
rb_define_method(cAgent, "waitpid", LibevAgent_waitpid, 1);
|
878
|
+
rb_define_method(cAgent, "wait_event", LibevAgent_wait_event, 1);
|
879
|
+
|
880
|
+
ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
|
881
|
+
|
882
|
+
__AGENT__.pending_count = LibevAgent_pending_count;
|
883
|
+
__AGENT__.poll = LibevAgent_poll;
|
884
|
+
__AGENT__.ref = LibevAgent_ref;
|
885
|
+
__AGENT__.ref_count = LibevAgent_ref_count;
|
886
|
+
__AGENT__.reset_ref_count = LibevAgent_reset_ref_count;
|
887
|
+
__AGENT__.unref = LibevAgent_unref;
|
888
|
+
__AGENT__.wait_event = LibevAgent_wait_event;
|
889
|
+
__AGENT__.wakeup = LibevAgent_wakeup;
|
748
890
|
}
|