polyphony 0.43.6 → 0.44.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +45 -0
- data/Gemfile.lock +5 -1
- data/README.md +20 -5
- data/TODO.md +10 -14
- data/bin/stress.rb +28 -0
- data/docs/getting-started/overview.md +2 -2
- data/examples/adapters/sequel_mysql.rb +23 -0
- data/examples/adapters/sequel_mysql_pool.rb +33 -0
- data/examples/core/xx-channels.rb +4 -2
- data/examples/core/xx-using-a-mutex.rb +2 -1
- data/examples/performance/fiber_transfer.rb +47 -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 +201 -128
- data/ext/polyphony/polyphony.c +4 -2
- data/ext/polyphony/polyphony.h +24 -24
- 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 +53 -38
- data/lib/polyphony.rb +13 -31
- data/lib/polyphony/adapters/mysql2.rb +19 -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/resource_pool.rb +23 -72
- data/lib/polyphony/core/sync.rb +12 -9
- data/lib/polyphony/extensions/core.rb +15 -8
- data/lib/polyphony/extensions/fiber.rb +4 -0
- data/lib/polyphony/extensions/socket.rb +9 -9
- data/lib/polyphony/extensions/thread.rb +1 -1
- data/lib/polyphony/net.rb +2 -1
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +2 -0
- 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 +17 -1
- data/test/test_io.rb +14 -0
- data/test/test_queue.rb +33 -0
- data/test/test_resource_pool.rb +34 -43
- data/test/test_signal.rb +2 -26
- data/test/test_socket.rb +0 -43
- data/test/test_trace.rb +18 -17
- metadata +40 -5
- data/ext/polyphony/libev_queue.c +0 -288
- data/lib/polyphony/event.rb +0 -27
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,23 +1,26 @@
|
|
1
1
|
#include <netdb.h>
|
2
2
|
#include <sys/socket.h>
|
3
3
|
#include <sys/uio.h>
|
4
|
+
#include <unistd.h>
|
5
|
+
#include <fcntl.h>
|
6
|
+
#include <netinet/in.h>
|
7
|
+
#include <arpa/inet.h>
|
4
8
|
|
5
9
|
#include "polyphony.h"
|
6
10
|
#include "../libev/ev.h"
|
7
11
|
|
8
|
-
VALUE cLibevAgent = Qnil;
|
9
12
|
VALUE cTCPSocket;
|
10
13
|
|
11
|
-
struct LibevAgent_t {
|
14
|
+
typedef struct LibevAgent_t {
|
12
15
|
struct ev_loop *ev_loop;
|
13
16
|
struct ev_async break_async;
|
14
17
|
int running;
|
15
18
|
int ref_count;
|
16
19
|
int run_no_wait_count;
|
17
|
-
};
|
20
|
+
} LibevAgent_t;
|
18
21
|
|
19
22
|
static size_t LibevAgent_size(const void *ptr) {
|
20
|
-
return sizeof(
|
23
|
+
return sizeof(LibevAgent_t);
|
21
24
|
}
|
22
25
|
|
23
26
|
static const rb_data_type_t LibevAgent_type = {
|
@@ -27,13 +30,13 @@ static const rb_data_type_t LibevAgent_type = {
|
|
27
30
|
};
|
28
31
|
|
29
32
|
static VALUE LibevAgent_allocate(VALUE klass) {
|
30
|
-
|
33
|
+
LibevAgent_t *agent = ALLOC(LibevAgent_t);
|
31
34
|
|
32
35
|
return TypedData_Wrap_Struct(klass, &LibevAgent_type, agent);
|
33
36
|
}
|
34
37
|
|
35
38
|
#define GetLibevAgent(obj, agent) \
|
36
|
-
TypedData_Get_Struct((obj),
|
39
|
+
TypedData_Get_Struct((obj), LibevAgent_t, &LibevAgent_type, (agent))
|
37
40
|
|
38
41
|
void break_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
|
39
42
|
// This callback does nothing, the break async is used solely for breaking out
|
@@ -41,7 +44,7 @@ void break_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, in
|
|
41
44
|
}
|
42
45
|
|
43
46
|
static VALUE LibevAgent_initialize(VALUE self) {
|
44
|
-
|
47
|
+
LibevAgent_t *agent;
|
45
48
|
VALUE thread = rb_thread_current();
|
46
49
|
int is_main_thread = (thread == rb_thread_main());
|
47
50
|
|
@@ -60,7 +63,7 @@ static VALUE LibevAgent_initialize(VALUE self) {
|
|
60
63
|
}
|
61
64
|
|
62
65
|
VALUE LibevAgent_finalize(VALUE self) {
|
63
|
-
|
66
|
+
LibevAgent_t *agent;
|
64
67
|
GetLibevAgent(self, agent);
|
65
68
|
|
66
69
|
ev_async_stop(agent->ev_loop, &agent->break_async);
|
@@ -71,24 +74,22 @@ VALUE LibevAgent_finalize(VALUE self) {
|
|
71
74
|
}
|
72
75
|
|
73
76
|
VALUE LibevAgent_post_fork(VALUE self) {
|
74
|
-
|
77
|
+
LibevAgent_t *agent;
|
75
78
|
GetLibevAgent(self, agent);
|
76
79
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
ev_loop_fork(agent->ev_loop);
|
80
|
+
// After fork there may be some watchers still active left over from the
|
81
|
+
// parent, so we destroy the loop, even if it's the default one, then use the
|
82
|
+
// default one, as post_fork is called only from the main thread of the forked
|
83
|
+
// process. That way we don't need to call ev_loop_fork, since the loop is
|
84
|
+
// always a fresh one.
|
85
|
+
ev_loop_destroy(agent->ev_loop);
|
86
|
+
agent->ev_loop = EV_DEFAULT;
|
86
87
|
|
87
88
|
return self;
|
88
89
|
}
|
89
90
|
|
90
91
|
VALUE LibevAgent_ref(VALUE self) {
|
91
|
-
|
92
|
+
LibevAgent_t *agent;
|
92
93
|
GetLibevAgent(self, agent);
|
93
94
|
|
94
95
|
agent->ref_count++;
|
@@ -96,7 +97,7 @@ VALUE LibevAgent_ref(VALUE self) {
|
|
96
97
|
}
|
97
98
|
|
98
99
|
VALUE LibevAgent_unref(VALUE self) {
|
99
|
-
|
100
|
+
LibevAgent_t *agent;
|
100
101
|
GetLibevAgent(self, agent);
|
101
102
|
|
102
103
|
agent->ref_count--;
|
@@ -104,14 +105,14 @@ VALUE LibevAgent_unref(VALUE self) {
|
|
104
105
|
}
|
105
106
|
|
106
107
|
int LibevAgent_ref_count(VALUE self) {
|
107
|
-
|
108
|
+
LibevAgent_t *agent;
|
108
109
|
GetLibevAgent(self, agent);
|
109
110
|
|
110
111
|
return agent->ref_count;
|
111
112
|
}
|
112
113
|
|
113
114
|
void LibevAgent_reset_ref_count(VALUE self) {
|
114
|
-
|
115
|
+
LibevAgent_t *agent;
|
115
116
|
GetLibevAgent(self, agent);
|
116
117
|
|
117
118
|
agent->ref_count = 0;
|
@@ -119,7 +120,7 @@ void LibevAgent_reset_ref_count(VALUE self) {
|
|
119
120
|
|
120
121
|
VALUE LibevAgent_pending_count(VALUE self) {
|
121
122
|
int count;
|
122
|
-
|
123
|
+
LibevAgent_t *agent;
|
123
124
|
GetLibevAgent(self, agent);
|
124
125
|
count = ev_pending_count(agent->ev_loop);
|
125
126
|
return INT2NUM(count);
|
@@ -127,11 +128,11 @@ VALUE LibevAgent_pending_count(VALUE self) {
|
|
127
128
|
|
128
129
|
VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue) {
|
129
130
|
int is_nowait = nowait == Qtrue;
|
130
|
-
|
131
|
+
LibevAgent_t *agent;
|
131
132
|
GetLibevAgent(self, agent);
|
132
133
|
|
133
134
|
if (is_nowait) {
|
134
|
-
long runnable_count =
|
135
|
+
long runnable_count = Queue_len(queue);
|
135
136
|
agent->run_no_wait_count++;
|
136
137
|
if (agent->run_no_wait_count < runnable_count || agent->run_no_wait_count < 10)
|
137
138
|
return self;
|
@@ -139,17 +140,17 @@ VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue
|
|
139
140
|
|
140
141
|
agent->run_no_wait_count = 0;
|
141
142
|
|
142
|
-
|
143
|
+
COND_TRACE(2, SYM_fiber_ev_loop_enter, current_fiber);
|
143
144
|
agent->running = 1;
|
144
145
|
ev_run(agent->ev_loop, is_nowait ? EVRUN_NOWAIT : EVRUN_ONCE);
|
145
146
|
agent->running = 0;
|
146
|
-
|
147
|
+
COND_TRACE(2, SYM_fiber_ev_loop_leave, current_fiber);
|
147
148
|
|
148
149
|
return self;
|
149
150
|
}
|
150
151
|
|
151
|
-
VALUE
|
152
|
-
|
152
|
+
VALUE LibevAgent_wakeup(VALUE self) {
|
153
|
+
LibevAgent_t *agent;
|
153
154
|
GetLibevAgent(self, agent);
|
154
155
|
|
155
156
|
if (agent->running) {
|
@@ -243,7 +244,7 @@ void LibevAgent_io_callback(EV_P_ ev_io *w, int revents)
|
|
243
244
|
Fiber_make_runnable(watcher->fiber, Qnil);
|
244
245
|
}
|
245
246
|
|
246
|
-
inline VALUE libev_await(
|
247
|
+
inline VALUE libev_await(LibevAgent_t *agent) {
|
247
248
|
VALUE ret;
|
248
249
|
agent->ref_count++;
|
249
250
|
ret = Thread_switch_fiber(rb_thread_current());
|
@@ -253,12 +254,12 @@ inline VALUE libev_await(struct LibevAgent_t *agent) {
|
|
253
254
|
}
|
254
255
|
|
255
256
|
VALUE libev_agent_await(VALUE self) {
|
256
|
-
|
257
|
+
LibevAgent_t *agent;
|
257
258
|
GetLibevAgent(self, agent);
|
258
259
|
return libev_await(agent);
|
259
260
|
}
|
260
261
|
|
261
|
-
VALUE libev_io_wait(
|
262
|
+
VALUE libev_io_wait(LibevAgent_t *agent, struct libev_io *watcher, rb_io_t *fptr, int flags) {
|
262
263
|
VALUE switchpoint_result;
|
263
264
|
|
264
265
|
if (watcher->fiber == Qnil) {
|
@@ -278,8 +279,33 @@ VALUE libev_snooze() {
|
|
278
279
|
return Thread_switch_fiber(rb_thread_current());
|
279
280
|
}
|
280
281
|
|
282
|
+
ID ID_ivar_is_nonblocking;
|
283
|
+
|
284
|
+
// Since we need to ensure that fd's are non-blocking before every I/O
|
285
|
+
// operation, here we improve upon Ruby's rb_io_set_nonblock by caching the
|
286
|
+
// "nonblock" state in an instance variable. Calling rb_ivar_get on every read
|
287
|
+
// is still much cheaper than doing a fcntl syscall on every read! Preliminary
|
288
|
+
// benchmarks (with a "hello world" HTTP server) show throughput is improved
|
289
|
+
// by 10-13%.
|
290
|
+
inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
|
291
|
+
#ifdef _WIN32
|
292
|
+
return rb_w32_set_nonblock(fptr->fd);
|
293
|
+
#elif defined(F_GETFL)
|
294
|
+
VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
|
295
|
+
if (is_nonblocking == Qnil) {
|
296
|
+
rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
|
297
|
+
int oflags = fcntl(fptr->fd, F_GETFL);
|
298
|
+
if (oflags == -1) return;
|
299
|
+
if (oflags & O_NONBLOCK) return;
|
300
|
+
oflags |= O_NONBLOCK;
|
301
|
+
fcntl(fptr->fd, F_SETFL, oflags);
|
302
|
+
}
|
303
|
+
#endif
|
304
|
+
return;
|
305
|
+
}
|
306
|
+
|
281
307
|
VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
|
282
|
-
|
308
|
+
LibevAgent_t *agent;
|
283
309
|
struct libev_io watcher;
|
284
310
|
rb_io_t *fptr;
|
285
311
|
long dynamic_len = length == Qnil;
|
@@ -295,11 +321,20 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
295
321
|
if (underlying_io != Qnil) io = underlying_io;
|
296
322
|
GetOpenFile(io, fptr);
|
297
323
|
rb_io_check_byte_readable(fptr);
|
298
|
-
|
324
|
+
io_set_nonblock(fptr, io);
|
299
325
|
watcher.fiber = Qnil;
|
300
326
|
|
301
327
|
OBJ_TAINT(str);
|
302
328
|
|
329
|
+
// Apparently after reopening a closed file, the file position is not reset,
|
330
|
+
// which causes the read to fail. Fortunately we can use fptr->rbuf.len to
|
331
|
+
// find out if that's the case.
|
332
|
+
// See: https://github.com/digital-fabric/polyphony/issues/30
|
333
|
+
if (fptr->rbuf.len > 0) {
|
334
|
+
lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
|
335
|
+
fptr->rbuf.len = 0;
|
336
|
+
}
|
337
|
+
|
303
338
|
while (1) {
|
304
339
|
ssize_t n = read(fptr->fd, buf, len - total);
|
305
340
|
if (n < 0) {
|
@@ -339,7 +374,7 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
339
374
|
|
340
375
|
return str;
|
341
376
|
error:
|
342
|
-
return
|
377
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
343
378
|
}
|
344
379
|
|
345
380
|
VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
@@ -349,6 +384,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
349
384
|
shrinkable = io_setstrbuf(&str, len); \
|
350
385
|
buf = RSTRING_PTR(str); \
|
351
386
|
total = 0; \
|
387
|
+
OBJ_TAINT(str); \
|
352
388
|
}
|
353
389
|
|
354
390
|
#define YIELD_STR() { \
|
@@ -358,7 +394,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
358
394
|
PREPARE_STR(); \
|
359
395
|
}
|
360
396
|
|
361
|
-
|
397
|
+
LibevAgent_t *agent;
|
362
398
|
struct libev_io watcher;
|
363
399
|
rb_io_t *fptr;
|
364
400
|
VALUE str;
|
@@ -375,10 +411,17 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
375
411
|
if (underlying_io != Qnil) io = underlying_io;
|
376
412
|
GetOpenFile(io, fptr);
|
377
413
|
rb_io_check_byte_readable(fptr);
|
378
|
-
|
414
|
+
io_set_nonblock(fptr, io);
|
379
415
|
watcher.fiber = Qnil;
|
380
416
|
|
381
|
-
|
417
|
+
// Apparently after reopening a closed file, the file position is not reset,
|
418
|
+
// which causes the read to fail. Fortunately we can use fptr->rbuf.len to
|
419
|
+
// find out if that's the case.
|
420
|
+
// See: https://github.com/digital-fabric/polyphony/issues/30
|
421
|
+
if (fptr->rbuf.len > 0) {
|
422
|
+
lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
|
423
|
+
fptr->rbuf.len = 0;
|
424
|
+
}
|
382
425
|
|
383
426
|
while (1) {
|
384
427
|
ssize_t n = read(fptr->fd, buf, len);
|
@@ -411,11 +454,11 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
411
454
|
|
412
455
|
return io;
|
413
456
|
error:
|
414
|
-
return
|
457
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
415
458
|
}
|
416
459
|
|
417
460
|
VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
418
|
-
|
461
|
+
LibevAgent_t *agent;
|
419
462
|
struct libev_io watcher;
|
420
463
|
rb_io_t *fptr;
|
421
464
|
VALUE switchpoint_result = Qnil;
|
@@ -455,11 +498,11 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
|
455
498
|
|
456
499
|
return INT2NUM(len);
|
457
500
|
error:
|
458
|
-
return
|
501
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
459
502
|
}
|
460
503
|
|
461
504
|
VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
462
|
-
|
505
|
+
LibevAgent_t *agent;
|
463
506
|
struct libev_io watcher;
|
464
507
|
rb_io_t *fptr;
|
465
508
|
VALUE switchpoint_result = Qnil;
|
@@ -525,7 +568,7 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
525
568
|
return INT2NUM(total_written);
|
526
569
|
error:
|
527
570
|
free(iov);
|
528
|
-
return
|
571
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
529
572
|
}
|
530
573
|
|
531
574
|
VALUE LibevAgent_write_m(int argc, VALUE *argv, VALUE self) {
|
@@ -541,7 +584,7 @@ VALUE LibevAgent_write_m(int argc, VALUE *argv, VALUE self) {
|
|
541
584
|
///////////////////////////////////////////////////////////////////////////
|
542
585
|
|
543
586
|
VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
544
|
-
|
587
|
+
LibevAgent_t *agent;
|
545
588
|
struct libev_io watcher;
|
546
589
|
rb_io_t *fptr;
|
547
590
|
int fd;
|
@@ -553,7 +596,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
553
596
|
|
554
597
|
GetLibevAgent(self, agent);
|
555
598
|
GetOpenFile(sock, fptr);
|
556
|
-
|
599
|
+
io_set_nonblock(fptr, sock);
|
557
600
|
watcher.fiber = Qnil;
|
558
601
|
while (1) {
|
559
602
|
fd = accept(fptr->fd, &addr, &len);
|
@@ -579,7 +622,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
579
622
|
fp->fd = fd;
|
580
623
|
fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
|
581
624
|
rb_io_ascii8bit_binmode(socket);
|
582
|
-
|
625
|
+
io_set_nonblock(fp, socket);
|
583
626
|
rb_io_synchronized(fp);
|
584
627
|
|
585
628
|
// if (rsock_do_not_reverse_lookup) {
|
@@ -591,11 +634,11 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
591
634
|
RB_GC_GUARD(switchpoint_result);
|
592
635
|
return Qnil;
|
593
636
|
error:
|
594
|
-
return
|
637
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
595
638
|
}
|
596
639
|
|
597
640
|
VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
598
|
-
|
641
|
+
LibevAgent_t *agent;
|
599
642
|
struct libev_io watcher;
|
600
643
|
rb_io_t *fptr;
|
601
644
|
int fd;
|
@@ -608,7 +651,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
608
651
|
|
609
652
|
GetLibevAgent(self, agent);
|
610
653
|
GetOpenFile(sock, fptr);
|
611
|
-
|
654
|
+
io_set_nonblock(fptr, sock);
|
612
655
|
watcher.fiber = Qnil;
|
613
656
|
|
614
657
|
while (1) {
|
@@ -634,7 +677,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
634
677
|
fp->fd = fd;
|
635
678
|
fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
|
636
679
|
rb_io_ascii8bit_binmode(socket);
|
637
|
-
|
680
|
+
io_set_nonblock(fp, socket);
|
638
681
|
rb_io_synchronized(fp);
|
639
682
|
|
640
683
|
rb_yield(socket);
|
@@ -647,74 +690,72 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
647
690
|
RB_GC_GUARD(switchpoint_result);
|
648
691
|
return Qnil;
|
649
692
|
error:
|
650
|
-
return
|
651
|
-
}
|
652
|
-
|
653
|
-
// VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
654
|
-
// struct LibevAgent_t *agent;
|
655
|
-
// struct libev_io watcher;
|
656
|
-
// rb_io_t *fptr;
|
657
|
-
// struct sockaddr_in addr;
|
658
|
-
// char *host_buf = StringValueCStr(host);
|
659
|
-
// VALUE switchpoint_result = Qnil;
|
660
|
-
// VALUE underlying_sock = rb_iv_get(sock, "@io");
|
661
|
-
// if (underlying_sock != Qnil) sock = underlying_sock;
|
662
|
-
|
663
|
-
// GetLibevAgent(self, agent);
|
664
|
-
// GetOpenFile(sock, fptr);
|
665
|
-
// rb_io_set_nonblock(fptr);
|
666
|
-
// watcher.fiber = Qnil;
|
667
|
-
|
668
|
-
// addr.sin_family = AF_INET;
|
669
|
-
// addr.sin_addr.s_addr = inet_addr(host_buf);
|
670
|
-
// addr.sin_port = htons(NUM2INT(port));
|
671
|
-
|
672
|
-
// while (1) {
|
673
|
-
// int result = connect(fptr->fd, &addr, sizeof(addr));
|
674
|
-
// if (result < 0) {
|
675
|
-
// int e = errno;
|
676
|
-
// if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
677
|
-
|
678
|
-
// switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
|
679
|
-
// if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
680
|
-
// }
|
681
|
-
// else {
|
682
|
-
// switchpoint_result = libev_snooze();
|
683
|
-
// if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
684
|
-
|
685
|
-
// return sock;
|
686
|
-
// }
|
687
|
-
// }
|
688
|
-
// RB_GC_GUARD(switchpoint_result);
|
689
|
-
// return Qnil;
|
690
|
-
// error:
|
691
|
-
// return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
692
|
-
// }
|
693
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
694
|
+
}
|
693
695
|
|
694
|
-
VALUE
|
695
|
-
|
696
|
+
VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
697
|
+
LibevAgent_t *agent;
|
696
698
|
struct libev_io watcher;
|
697
699
|
rb_io_t *fptr;
|
700
|
+
struct sockaddr_in addr;
|
701
|
+
char *host_buf = StringValueCStr(host);
|
698
702
|
VALUE switchpoint_result = Qnil;
|
699
|
-
|
703
|
+
VALUE underlying_sock = rb_iv_get(sock, "@io");
|
704
|
+
if (underlying_sock != Qnil) sock = underlying_sock;
|
700
705
|
|
701
|
-
VALUE underlying_io = rb_iv_get(io, "@io");
|
702
706
|
GetLibevAgent(self, agent);
|
703
|
-
|
704
|
-
|
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(agent, &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(LibevAgent_t *agent, int fd, int events, int raise_exception) {
|
733
|
+
struct libev_io watcher;
|
734
|
+
VALUE switchpoint_result = Qnil;
|
705
735
|
|
706
736
|
watcher.fiber = rb_fiber_current();
|
707
|
-
ev_io_init(&watcher.io, LibevAgent_io_callback,
|
737
|
+
ev_io_init(&watcher.io, LibevAgent_io_callback, fd, events);
|
708
738
|
ev_io_start(agent->ev_loop, &watcher.io);
|
709
739
|
switchpoint_result = libev_await(agent);
|
710
740
|
ev_io_stop(agent->ev_loop, &watcher.io);
|
711
741
|
|
712
|
-
TEST_RESUME_EXCEPTION(switchpoint_result);
|
713
|
-
RB_GC_GUARD(watcher.fiber);
|
742
|
+
if (raise_exception) TEST_RESUME_EXCEPTION(switchpoint_result);
|
714
743
|
RB_GC_GUARD(switchpoint_result);
|
715
744
|
return switchpoint_result;
|
716
745
|
}
|
717
746
|
|
747
|
+
VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write) {
|
748
|
+
LibevAgent_t *agent;
|
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
|
+
GetLibevAgent(self, agent);
|
754
|
+
GetOpenFile(io, fptr);
|
755
|
+
|
756
|
+
return libev_wait_fd(agent, fptr->fd, events, 1);
|
757
|
+
}
|
758
|
+
|
718
759
|
struct libev_timer {
|
719
760
|
struct ev_timer timer;
|
720
761
|
VALUE fiber;
|
@@ -727,7 +768,7 @@ void LibevAgent_timer_callback(EV_P_ ev_timer *w, int revents)
|
|
727
768
|
}
|
728
769
|
|
729
770
|
VALUE LibevAgent_sleep(VALUE self, VALUE duration) {
|
730
|
-
|
771
|
+
LibevAgent_t *agent;
|
731
772
|
struct libev_timer watcher;
|
732
773
|
VALUE switchpoint_result = Qnil;
|
733
774
|
|
@@ -737,6 +778,7 @@ VALUE LibevAgent_sleep(VALUE self, VALUE duration) {
|
|
737
778
|
ev_timer_start(agent->ev_loop, &watcher.timer);
|
738
779
|
|
739
780
|
switchpoint_result = libev_await(agent);
|
781
|
+
|
740
782
|
ev_timer_stop(agent->ev_loop, &watcher.timer);
|
741
783
|
|
742
784
|
TEST_RESUME_EXCEPTION(switchpoint_result);
|
@@ -761,7 +803,7 @@ void LibevAgent_child_callback(EV_P_ ev_child *w, int revents)
|
|
761
803
|
}
|
762
804
|
|
763
805
|
VALUE LibevAgent_waitpid(VALUE self, VALUE pid) {
|
764
|
-
|
806
|
+
LibevAgent_t *agent;
|
765
807
|
struct libev_child watcher;
|
766
808
|
VALUE switchpoint_result = Qnil;
|
767
809
|
GetLibevAgent(self, agent);
|
@@ -780,36 +822,67 @@ VALUE LibevAgent_waitpid(VALUE self, VALUE pid) {
|
|
780
822
|
}
|
781
823
|
|
782
824
|
struct ev_loop *LibevAgent_ev_loop(VALUE self) {
|
783
|
-
|
825
|
+
LibevAgent_t *agent;
|
784
826
|
GetLibevAgent(self, agent);
|
785
827
|
return agent->ev_loop;
|
786
828
|
}
|
787
829
|
|
830
|
+
void LibevAgent_async_callback(EV_P_ ev_async *w, int revents) { }
|
831
|
+
|
832
|
+
VALUE LibevAgent_wait_event(VALUE self, VALUE raise) {
|
833
|
+
LibevAgent_t *agent;
|
834
|
+
VALUE switchpoint_result = Qnil;
|
835
|
+
GetLibevAgent(self, agent);
|
836
|
+
|
837
|
+
struct ev_async async;
|
838
|
+
|
839
|
+
ev_async_init(&async, LibevAgent_async_callback);
|
840
|
+
ev_async_start(agent->ev_loop, &async);
|
841
|
+
switchpoint_result = libev_await(agent);
|
842
|
+
ev_async_stop(agent->ev_loop, &async);
|
843
|
+
|
844
|
+
if (RTEST(raise)) TEST_RESUME_EXCEPTION(switchpoint_result);
|
845
|
+
RB_GC_GUARD(switchpoint_result);
|
846
|
+
return switchpoint_result;
|
847
|
+
}
|
848
|
+
|
788
849
|
void Init_LibevAgent() {
|
789
850
|
rb_require("socket");
|
790
851
|
cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
|
791
852
|
|
792
|
-
|
793
|
-
rb_define_alloc_func(
|
794
|
-
|
795
|
-
rb_define_method(
|
796
|
-
rb_define_method(
|
797
|
-
rb_define_method(
|
798
|
-
rb_define_method(
|
799
|
-
|
800
|
-
rb_define_method(
|
801
|
-
rb_define_method(
|
802
|
-
|
803
|
-
rb_define_method(
|
804
|
-
rb_define_method(
|
805
|
-
|
806
|
-
rb_define_method(
|
807
|
-
rb_define_method(
|
808
|
-
rb_define_method(
|
809
|
-
rb_define_method(
|
810
|
-
rb_define_method(
|
811
|
-
|
812
|
-
rb_define_method(
|
813
|
-
rb_define_method(
|
814
|
-
rb_define_method(
|
853
|
+
VALUE cAgent = rb_define_class_under(mPolyphony, "Agent", rb_cData);
|
854
|
+
rb_define_alloc_func(cAgent, LibevAgent_allocate);
|
855
|
+
|
856
|
+
rb_define_method(cAgent, "initialize", LibevAgent_initialize, 0);
|
857
|
+
rb_define_method(cAgent, "finalize", LibevAgent_finalize, 0);
|
858
|
+
rb_define_method(cAgent, "post_fork", LibevAgent_post_fork, 0);
|
859
|
+
rb_define_method(cAgent, "pending_count", LibevAgent_pending_count, 0);
|
860
|
+
|
861
|
+
rb_define_method(cAgent, "ref", LibevAgent_ref, 0);
|
862
|
+
rb_define_method(cAgent, "unref", LibevAgent_unref, 0);
|
863
|
+
|
864
|
+
rb_define_method(cAgent, "poll", LibevAgent_poll, 3);
|
865
|
+
rb_define_method(cAgent, "break", LibevAgent_wakeup, 0);
|
866
|
+
|
867
|
+
rb_define_method(cAgent, "read", LibevAgent_read, 4);
|
868
|
+
rb_define_method(cAgent, "read_loop", LibevAgent_read_loop, 1);
|
869
|
+
rb_define_method(cAgent, "write", LibevAgent_write_m, -1);
|
870
|
+
rb_define_method(cAgent, "accept", LibevAgent_accept, 1);
|
871
|
+
rb_define_method(cAgent, "accept_loop", LibevAgent_accept_loop, 1);
|
872
|
+
rb_define_method(cAgent, "connect", LibevAgent_connect, 3);
|
873
|
+
rb_define_method(cAgent, "wait_io", LibevAgent_wait_io, 2);
|
874
|
+
rb_define_method(cAgent, "sleep", LibevAgent_sleep, 1);
|
875
|
+
rb_define_method(cAgent, "waitpid", LibevAgent_waitpid, 1);
|
876
|
+
rb_define_method(cAgent, "wait_event", LibevAgent_wait_event, 1);
|
877
|
+
|
878
|
+
ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
|
879
|
+
|
880
|
+
__AGENT__.pending_count = LibevAgent_pending_count;
|
881
|
+
__AGENT__.poll = LibevAgent_poll;
|
882
|
+
__AGENT__.ref = LibevAgent_ref;
|
883
|
+
__AGENT__.ref_count = LibevAgent_ref_count;
|
884
|
+
__AGENT__.reset_ref_count = LibevAgent_reset_ref_count;
|
885
|
+
__AGENT__.unref = LibevAgent_unref;
|
886
|
+
__AGENT__.wait_event = LibevAgent_wait_event;
|
887
|
+
__AGENT__.wakeup = LibevAgent_wakeup;
|
815
888
|
}
|