polyphony 0.43.6 → 0.44.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/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
|
}
|