polyphony 0.45.0 → 0.46.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/.github/workflows/test.yml +2 -0
- data/.gitmodules +0 -0
- data/.rubocop.yml +1 -0
- data/CHANGELOG.md +38 -0
- data/Gemfile.lock +11 -3
- data/README.md +3 -3
- data/Rakefile +1 -1
- data/TODO.md +10 -18
- data/examples/adapters/redis_client.rb +3 -1
- data/examples/adapters/redis_pubsub_perf.rb +11 -8
- data/examples/adapters/sequel_mysql.rb +1 -1
- data/examples/adapters/sequel_pg.rb +24 -0
- data/examples/core/{02-awaiting-fibers.rb → await.rb} +0 -0
- data/examples/core/{xx-channels.rb → channels.rb} +0 -0
- data/examples/core/deferring-an-operation.rb +16 -0
- data/examples/core/{xx-erlang-style-genserver.rb → erlang-style-genserver.rb} +16 -9
- data/examples/core/{xx-forking.rb → forking.rb} +1 -1
- data/examples/core/handling-signals.rb +11 -0
- data/examples/core/{03-interrupting.rb → interrupt.rb} +0 -0
- data/examples/core/{xx-pingpong.rb → pingpong.rb} +7 -5
- data/examples/core/{xx-recurrent-timer.rb → recurrent-timer.rb} +1 -1
- data/examples/core/{xx-resource_delegate.rb → resource_delegate.rb} +3 -4
- data/examples/core/{01-spinning-up-fibers.rb → spin.rb} +1 -1
- data/examples/core/{xx-spin_error_backtrace.rb → spin_error_backtrace.rb} +1 -1
- data/examples/core/{xx-supervise-process.rb → supervise-process.rb} +8 -5
- data/examples/core/supervisor.rb +20 -0
- data/examples/core/{xx-thread-sleep.rb → thread-sleep.rb} +0 -0
- data/examples/core/{xx-thread_pool.rb → thread_pool.rb} +0 -0
- data/examples/core/{xx-throttling.rb → throttling.rb} +0 -0
- data/examples/core/{xx-timeout.rb → timeout.rb} +0 -0
- data/examples/core/{xx-using-a-mutex.rb → using-a-mutex.rb} +0 -0
- data/examples/core/{xx-worker-thread.rb → worker-thread.rb} +2 -2
- data/examples/io/{xx-backticks.rb → backticks.rb} +0 -0
- data/examples/io/{xx-echo_client.rb → echo_client.rb} +1 -1
- data/examples/io/{xx-echo_client_from_stdin.rb → echo_client_from_stdin.rb} +2 -2
- data/examples/io/{xx-echo_pipe.rb → echo_pipe.rb} +1 -1
- data/examples/io/{xx-echo_server.rb → echo_server.rb} +0 -0
- data/examples/io/{xx-echo_server_with_timeout.rb → echo_server_with_timeout.rb} +1 -1
- data/examples/io/{xx-echo_stdin.rb → echo_stdin.rb} +0 -0
- data/examples/io/{xx-happy-eyeballs.rb → happy-eyeballs.rb} +0 -0
- data/examples/io/{xx-httparty.rb → httparty.rb} +4 -13
- data/examples/io/{xx-irb.rb → irb.rb} +0 -0
- data/examples/io/{xx-net-http.rb → net-http.rb} +0 -0
- data/examples/io/{xx-open.rb → open.rb} +0 -0
- data/examples/io/{xx-pry.rb → pry.rb} +0 -0
- data/examples/io/{xx-rack_server.rb → rack_server.rb} +0 -0
- data/examples/io/raw.rb +14 -0
- data/examples/io/reline.rb +18 -0
- data/examples/io/{xx-system.rb → system.rb} +1 -1
- data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
- data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
- data/examples/io/tunnel.rb +6 -1
- data/examples/io/{xx-zip.rb → zip.rb} +0 -0
- data/examples/performance/fiber_transfer.rb +2 -1
- data/examples/performance/fs_read.rb +5 -6
- data/examples/performance/multi_snooze.rb +0 -1
- data/examples/{io/xx-switch.rb → performance/switch.rb} +2 -1
- data/examples/performance/thread-vs-fiber/{xx-httparty_multi.rb → httparty_multi.rb} +3 -4
- data/examples/performance/thread-vs-fiber/{xx-httparty_threaded.rb → httparty_threaded.rb} +0 -0
- data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -2
- data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
- data/examples/performance/thread_pool_perf.rb +6 -7
- data/ext/liburing/liburing.h +585 -0
- data/ext/liburing/liburing/README.md +4 -0
- data/ext/liburing/liburing/barrier.h +73 -0
- data/ext/liburing/liburing/compat.h +15 -0
- data/ext/liburing/liburing/io_uring.h +343 -0
- data/ext/liburing/queue.c +333 -0
- data/ext/liburing/register.c +187 -0
- data/ext/liburing/setup.c +210 -0
- data/ext/liburing/syscall.c +54 -0
- data/ext/liburing/syscall.h +18 -0
- data/ext/polyphony/backend.h +1 -16
- data/ext/polyphony/backend_common.h +109 -0
- data/ext/polyphony/backend_io_uring.c +884 -0
- data/ext/polyphony/backend_io_uring_context.c +73 -0
- data/ext/polyphony/backend_io_uring_context.h +52 -0
- data/ext/polyphony/{libev_backend.c → backend_libev.c} +255 -345
- data/ext/polyphony/event.c +1 -1
- data/ext/polyphony/extconf.rb +31 -13
- data/ext/polyphony/fiber.c +111 -27
- data/ext/polyphony/libev.c +4 -0
- data/ext/polyphony/libev.h +8 -2
- data/ext/polyphony/liburing.c +8 -0
- data/ext/polyphony/playground.c +51 -0
- data/ext/polyphony/polyphony.c +6 -8
- data/ext/polyphony/polyphony.h +29 -25
- data/ext/polyphony/polyphony_ext.c +13 -6
- data/ext/polyphony/queue.c +3 -4
- data/ext/polyphony/ring_buffer.c +0 -1
- data/ext/polyphony/runqueue.c +102 -0
- data/ext/polyphony/runqueue_ring_buffer.c +85 -0
- data/ext/polyphony/runqueue_ring_buffer.h +31 -0
- data/ext/polyphony/thread.c +45 -92
- data/lib/polyphony.rb +2 -2
- data/lib/polyphony/adapters/fs.rb +1 -1
- data/lib/polyphony/adapters/process.rb +0 -3
- data/lib/polyphony/adapters/redis.rb +1 -1
- data/lib/polyphony/adapters/trace.rb +2 -2
- data/lib/polyphony/core/global_api.rb +9 -12
- data/lib/polyphony/core/sync.rb +6 -2
- data/lib/polyphony/extensions/core.rb +6 -24
- data/lib/polyphony/extensions/debug.rb +13 -0
- data/lib/polyphony/extensions/fiber.rb +21 -44
- data/lib/polyphony/extensions/io.rb +55 -10
- data/lib/polyphony/extensions/socket.rb +70 -12
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +3 -2
- data/test/helper.rb +36 -4
- data/test/io_uring_test.rb +55 -0
- data/test/stress.rb +5 -2
- data/test/test_backend.rb +4 -6
- data/test/test_ext.rb +1 -2
- data/test/test_fiber.rb +31 -24
- data/test/test_global_api.rb +58 -31
- data/test/test_io.rb +58 -0
- data/test/test_signal.rb +11 -8
- data/test/test_socket.rb +17 -0
- data/test/test_sync.rb +21 -0
- data/test/test_throttler.rb +3 -6
- data/test/test_trace.rb +7 -5
- metadata +86 -76
- data/examples/adapters/concurrent-ruby.rb +0 -9
- data/examples/core/04-handling-signals.rb +0 -19
- data/examples/core/xx-at_exit.rb +0 -29
- data/examples/core/xx-backend.rb +0 -102
- data/examples/core/xx-caller.rb +0 -12
- data/examples/core/xx-daemon.rb +0 -14
- data/examples/core/xx-deadlock.rb +0 -8
- data/examples/core/xx-deferring-an-operation.rb +0 -14
- data/examples/core/xx-exception-backtrace.rb +0 -40
- data/examples/core/xx-fork-cleanup.rb +0 -22
- data/examples/core/xx-fork-spin.rb +0 -42
- data/examples/core/xx-fork-terminate.rb +0 -27
- data/examples/core/xx-move_on.rb +0 -23
- data/examples/core/xx-queue-async.rb +0 -120
- data/examples/core/xx-readpartial.rb +0 -18
- data/examples/core/xx-signals.rb +0 -16
- data/examples/core/xx-sleep-forever.rb +0 -9
- data/examples/core/xx-sleeping.rb +0 -25
- data/examples/core/xx-snooze-starve.rb +0 -16
- data/examples/core/xx-spin-fork.rb +0 -49
- data/examples/core/xx-state-machine.rb +0 -51
- data/examples/core/xx-stop.rb +0 -20
- data/examples/core/xx-supervisors.rb +0 -21
- data/examples/core/xx-thread-selector-sleep.rb +0 -51
- data/examples/core/xx-thread-selector-snooze.rb +0 -46
- data/examples/core/xx-thread-snooze.rb +0 -34
- data/examples/core/xx-timer-gc.rb +0 -17
- data/examples/core/xx-trace.rb +0 -79
- data/examples/performance/xx-array.rb +0 -11
- data/examples/performance/xx-fiber-switch.rb +0 -9
- data/examples/performance/xx-snooze.rb +0 -15
- data/examples/xx-spin.rb +0 -32
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#include <stdlib.h>
|
|
2
|
+
#include "ruby.h"
|
|
3
|
+
#include "polyphony.h"
|
|
4
|
+
#include "backend_io_uring_context.h"
|
|
5
|
+
|
|
6
|
+
const char *op_type_to_str(enum op_type type) {
|
|
7
|
+
switch (type) {
|
|
8
|
+
case OP_READ: return "READ";
|
|
9
|
+
case OP_WRITEV: return "WRITEV";
|
|
10
|
+
case OP_WRITE: return "WRITE";
|
|
11
|
+
case OP_RECV: return "RECV";
|
|
12
|
+
case OP_SEND: return "SEND";
|
|
13
|
+
case OP_TIMEOUT: return "TIMEOUT";
|
|
14
|
+
case OP_POLL: return "POLL";
|
|
15
|
+
case OP_ACCEPT: return "ACCEPT";
|
|
16
|
+
case OP_CONNECT: return "CONNECT";
|
|
17
|
+
default: return "";
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
void context_store_initialize(op_context_store_t *store) {
|
|
22
|
+
store->last_id = 0;
|
|
23
|
+
store->available = NULL;
|
|
24
|
+
store->taken = NULL;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_type type) {
|
|
28
|
+
op_context_t *ctx = store->available;
|
|
29
|
+
if (ctx) {
|
|
30
|
+
if (ctx->next) ctx->next->prev = NULL;
|
|
31
|
+
store->available = ctx->next;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
ctx = malloc(sizeof(op_context_t));
|
|
35
|
+
}
|
|
36
|
+
ctx->id = (++store->last_id);
|
|
37
|
+
|
|
38
|
+
ctx->prev = NULL;
|
|
39
|
+
ctx->next = store->taken;
|
|
40
|
+
if (store->taken) store->taken->prev = ctx;
|
|
41
|
+
store->taken = ctx;
|
|
42
|
+
|
|
43
|
+
ctx->type = type;
|
|
44
|
+
ctx->fiber = rb_fiber_current();
|
|
45
|
+
ctx->completed = 0;
|
|
46
|
+
ctx->result = 0;
|
|
47
|
+
|
|
48
|
+
return ctx;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
inline void context_store_release(op_context_store_t *store, op_context_t *ctx) {
|
|
52
|
+
if (ctx->next) ctx->next->prev = ctx->prev;
|
|
53
|
+
if (ctx->prev) ctx->prev->next = ctx->next;
|
|
54
|
+
if (store->taken == ctx) store->taken = ctx->next;
|
|
55
|
+
|
|
56
|
+
ctx->prev = NULL;
|
|
57
|
+
ctx->next = store->available;
|
|
58
|
+
if (ctx->next) ctx->next->prev = ctx;
|
|
59
|
+
store->available = ctx;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
void context_store_free(op_context_store_t *store) {
|
|
63
|
+
while (store->available) {
|
|
64
|
+
op_context_t *next = store->available->next;
|
|
65
|
+
free(store->available);
|
|
66
|
+
store->available = next;
|
|
67
|
+
}
|
|
68
|
+
while (store->taken) {
|
|
69
|
+
op_context_t *next = store->taken->next;
|
|
70
|
+
free(store->taken);
|
|
71
|
+
store->taken = next;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#ifndef BACKEND_IO_URING_CONTEXT_H
|
|
2
|
+
#define BACKEND_IO_URING_CONTEXT_H
|
|
3
|
+
|
|
4
|
+
#include "ruby.h"
|
|
5
|
+
|
|
6
|
+
enum op_type {
|
|
7
|
+
OP_NONE,
|
|
8
|
+
OP_READ,
|
|
9
|
+
OP_WRITEV,
|
|
10
|
+
OP_WRITE,
|
|
11
|
+
OP_RECV,
|
|
12
|
+
OP_SEND,
|
|
13
|
+
OP_TIMEOUT,
|
|
14
|
+
OP_POLL,
|
|
15
|
+
OP_ACCEPT,
|
|
16
|
+
OP_CONNECT
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
typedef struct op_context {
|
|
20
|
+
struct op_context *prev;
|
|
21
|
+
struct op_context *next;
|
|
22
|
+
enum op_type type: 16;
|
|
23
|
+
int completed : 16;
|
|
24
|
+
int id;
|
|
25
|
+
int result;
|
|
26
|
+
VALUE fiber;
|
|
27
|
+
} op_context_t;
|
|
28
|
+
|
|
29
|
+
typedef struct op_context_store {
|
|
30
|
+
int last_id;
|
|
31
|
+
op_context_t *available;
|
|
32
|
+
op_context_t *taken;
|
|
33
|
+
} op_context_store_t;
|
|
34
|
+
|
|
35
|
+
const char *op_type_to_str(enum op_type type);
|
|
36
|
+
|
|
37
|
+
void context_store_initialize(op_context_store_t *store);
|
|
38
|
+
op_context_t *context_store_acquire(op_context_store_t *store, enum op_type type);
|
|
39
|
+
void context_store_release(op_context_store_t *store, op_context_t *ctx);
|
|
40
|
+
void context_store_free(op_context_store_t *store);
|
|
41
|
+
|
|
42
|
+
#define OP_CONTEXT_ACQUIRE(store, op_type) context_store_acquire(store, op_type)
|
|
43
|
+
#define OP_CONTEXT_RELEASE(store, ctx) { \
|
|
44
|
+
if (ctx->completed) {\
|
|
45
|
+
context_store_release(store, ctx); \
|
|
46
|
+
} \
|
|
47
|
+
else { \
|
|
48
|
+
ctx->completed = 1; \
|
|
49
|
+
} \
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
#endif /* BACKEND_IO_URING_CONTEXT_H */
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
#ifdef POLYPHONY_BACKEND_LIBEV
|
|
2
|
+
|
|
1
3
|
#include <netdb.h>
|
|
2
4
|
#include <sys/socket.h>
|
|
3
5
|
#include <sys/uio.h>
|
|
@@ -8,47 +10,73 @@
|
|
|
8
10
|
|
|
9
11
|
#include "polyphony.h"
|
|
10
12
|
#include "../libev/ev.h"
|
|
13
|
+
#include "ruby/io.h"
|
|
11
14
|
|
|
12
15
|
VALUE cTCPSocket;
|
|
16
|
+
VALUE SYM_libev;
|
|
17
|
+
|
|
18
|
+
ID ID_ivar_is_nonblocking;
|
|
19
|
+
|
|
20
|
+
// Since we need to ensure that fd's are non-blocking before every I/O
|
|
21
|
+
// operation, here we improve upon Ruby's rb_io_set_nonblock by caching the
|
|
22
|
+
// "nonblock" state in an instance variable. Calling rb_ivar_get on every read
|
|
23
|
+
// is still much cheaper than doing a fcntl syscall on every read! Preliminary
|
|
24
|
+
// benchmarks (with a "hello world" HTTP server) show throughput is improved
|
|
25
|
+
// by 10-13%.
|
|
26
|
+
inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
|
|
27
|
+
VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
|
|
28
|
+
if (is_nonblocking == Qtrue) return;
|
|
29
|
+
|
|
30
|
+
rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
|
|
31
|
+
|
|
32
|
+
#ifdef _WIN32
|
|
33
|
+
rb_w32_set_nonblock(fptr->fd);
|
|
34
|
+
#elif defined(F_GETFL)
|
|
35
|
+
int oflags = fcntl(fptr->fd, F_GETFL);
|
|
36
|
+
if ((oflags == -1) && (oflags & O_NONBLOCK)) return;
|
|
37
|
+
oflags |= O_NONBLOCK;
|
|
38
|
+
fcntl(fptr->fd, F_SETFL, oflags);
|
|
39
|
+
#endif
|
|
40
|
+
}
|
|
13
41
|
|
|
14
|
-
typedef struct
|
|
42
|
+
typedef struct Backend_t {
|
|
15
43
|
struct ev_loop *ev_loop;
|
|
16
44
|
struct ev_async break_async;
|
|
17
45
|
int running;
|
|
18
46
|
int ref_count;
|
|
19
47
|
int run_no_wait_count;
|
|
20
|
-
}
|
|
48
|
+
} Backend_t;
|
|
21
49
|
|
|
22
|
-
static size_t
|
|
23
|
-
return sizeof(
|
|
50
|
+
static size_t Backend_size(const void *ptr) {
|
|
51
|
+
return sizeof(Backend_t);
|
|
24
52
|
}
|
|
25
53
|
|
|
26
|
-
static const rb_data_type_t
|
|
27
|
-
"
|
|
28
|
-
{0, 0,
|
|
54
|
+
static const rb_data_type_t Backend_type = {
|
|
55
|
+
"LibevBackend",
|
|
56
|
+
{0, 0, Backend_size,},
|
|
29
57
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
|
30
58
|
};
|
|
31
59
|
|
|
32
|
-
static VALUE
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return TypedData_Wrap_Struct(klass, &
|
|
60
|
+
static VALUE Backend_allocate(VALUE klass) {
|
|
61
|
+
Backend_t *backend = ALLOC(Backend_t);
|
|
62
|
+
|
|
63
|
+
return TypedData_Wrap_Struct(klass, &Backend_type, backend);
|
|
36
64
|
}
|
|
37
65
|
|
|
38
|
-
#define
|
|
39
|
-
TypedData_Get_Struct((obj),
|
|
66
|
+
#define GetBackend(obj, backend) \
|
|
67
|
+
TypedData_Get_Struct((obj), Backend_t, &Backend_type, (backend))
|
|
40
68
|
|
|
41
69
|
void break_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
|
|
42
70
|
// This callback does nothing, the break async is used solely for breaking out
|
|
43
71
|
// of a *blocking* event loop (waking it up) in a thread-safe, signal-safe manner
|
|
44
72
|
}
|
|
45
73
|
|
|
46
|
-
static VALUE
|
|
47
|
-
|
|
74
|
+
static VALUE Backend_initialize(VALUE self) {
|
|
75
|
+
Backend_t *backend;
|
|
48
76
|
VALUE thread = rb_thread_current();
|
|
49
77
|
int is_main_thread = (thread == rb_thread_main());
|
|
50
78
|
|
|
51
|
-
|
|
79
|
+
GetBackend(self, backend);
|
|
52
80
|
backend->ev_loop = is_main_thread ? EV_DEFAULT : ev_loop_new(EVFLAG_NOSIGMASK);
|
|
53
81
|
|
|
54
82
|
ev_async_init(&backend->break_async, break_async_callback);
|
|
@@ -62,9 +90,9 @@ static VALUE LibevBackend_initialize(VALUE self) {
|
|
|
62
90
|
return Qnil;
|
|
63
91
|
}
|
|
64
92
|
|
|
65
|
-
VALUE
|
|
66
|
-
|
|
67
|
-
|
|
93
|
+
VALUE Backend_finalize(VALUE self) {
|
|
94
|
+
Backend_t *backend;
|
|
95
|
+
GetBackend(self, backend);
|
|
68
96
|
|
|
69
97
|
ev_async_stop(backend->ev_loop, &backend->break_async);
|
|
70
98
|
|
|
@@ -73,9 +101,9 @@ VALUE LibevBackend_finalize(VALUE self) {
|
|
|
73
101
|
return self;
|
|
74
102
|
}
|
|
75
103
|
|
|
76
|
-
VALUE
|
|
77
|
-
|
|
78
|
-
|
|
104
|
+
VALUE Backend_post_fork(VALUE self) {
|
|
105
|
+
Backend_t *backend;
|
|
106
|
+
GetBackend(self, backend);
|
|
79
107
|
|
|
80
108
|
// After fork there may be some watchers still active left over from the
|
|
81
109
|
// parent, so we destroy the loop, even if it's the default one, then use the
|
|
@@ -88,70 +116,71 @@ VALUE LibevBackend_post_fork(VALUE self) {
|
|
|
88
116
|
return self;
|
|
89
117
|
}
|
|
90
118
|
|
|
91
|
-
VALUE
|
|
92
|
-
|
|
93
|
-
|
|
119
|
+
VALUE Backend_ref(VALUE self) {
|
|
120
|
+
Backend_t *backend;
|
|
121
|
+
GetBackend(self, backend);
|
|
94
122
|
|
|
95
123
|
backend->ref_count++;
|
|
96
|
-
return self;
|
|
124
|
+
return self;
|
|
97
125
|
}
|
|
98
126
|
|
|
99
|
-
VALUE
|
|
100
|
-
|
|
101
|
-
|
|
127
|
+
VALUE Backend_unref(VALUE self) {
|
|
128
|
+
Backend_t *backend;
|
|
129
|
+
GetBackend(self, backend);
|
|
102
130
|
|
|
103
131
|
backend->ref_count--;
|
|
104
|
-
return self;
|
|
132
|
+
return self;
|
|
105
133
|
}
|
|
106
134
|
|
|
107
|
-
int
|
|
108
|
-
|
|
109
|
-
|
|
135
|
+
int Backend_ref_count(VALUE self) {
|
|
136
|
+
Backend_t *backend;
|
|
137
|
+
GetBackend(self, backend);
|
|
110
138
|
|
|
111
139
|
return backend->ref_count;
|
|
112
140
|
}
|
|
113
141
|
|
|
114
|
-
void
|
|
115
|
-
|
|
116
|
-
|
|
142
|
+
void Backend_reset_ref_count(VALUE self) {
|
|
143
|
+
Backend_t *backend;
|
|
144
|
+
GetBackend(self, backend);
|
|
117
145
|
|
|
118
146
|
backend->ref_count = 0;
|
|
119
147
|
}
|
|
120
148
|
|
|
121
|
-
VALUE
|
|
149
|
+
VALUE Backend_pending_count(VALUE self) {
|
|
122
150
|
int count;
|
|
123
|
-
|
|
124
|
-
|
|
151
|
+
Backend_t *backend;
|
|
152
|
+
GetBackend(self, backend);
|
|
125
153
|
count = ev_pending_count(backend->ev_loop);
|
|
126
154
|
return INT2NUM(count);
|
|
127
155
|
}
|
|
128
156
|
|
|
129
|
-
VALUE
|
|
157
|
+
VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue) {
|
|
130
158
|
int is_nowait = nowait == Qtrue;
|
|
131
|
-
|
|
132
|
-
|
|
159
|
+
Backend_t *backend;
|
|
160
|
+
GetBackend(self, backend);
|
|
133
161
|
|
|
134
162
|
if (is_nowait) {
|
|
135
|
-
long runnable_count = Queue_len(queue);
|
|
136
163
|
backend->run_no_wait_count++;
|
|
137
|
-
if (backend->run_no_wait_count <
|
|
138
|
-
|
|
164
|
+
if (backend->run_no_wait_count < 10) return self;
|
|
165
|
+
|
|
166
|
+
long runnable_count = Runqueue_len(runqueue);
|
|
167
|
+
if (backend->run_no_wait_count < runnable_count) return self;
|
|
139
168
|
}
|
|
140
169
|
|
|
141
170
|
backend->run_no_wait_count = 0;
|
|
142
|
-
|
|
143
|
-
COND_TRACE(2,
|
|
171
|
+
|
|
172
|
+
COND_TRACE(2, SYM_fiber_event_poll_enter, current_fiber);
|
|
144
173
|
backend->running = 1;
|
|
145
174
|
ev_run(backend->ev_loop, is_nowait ? EVRUN_NOWAIT : EVRUN_ONCE);
|
|
146
175
|
backend->running = 0;
|
|
147
|
-
COND_TRACE(2,
|
|
176
|
+
COND_TRACE(2, SYM_fiber_event_poll_leave, current_fiber);
|
|
148
177
|
|
|
149
178
|
return self;
|
|
150
179
|
}
|
|
151
180
|
|
|
152
|
-
VALUE
|
|
153
|
-
|
|
154
|
-
|
|
181
|
+
VALUE Backend_wakeup(VALUE self) {
|
|
182
|
+
Backend_t *backend;
|
|
183
|
+
GetBackend(self, backend);
|
|
155
184
|
|
|
156
185
|
if (backend->running) {
|
|
157
186
|
// Since the loop will run until at least one event has occurred, we signal
|
|
@@ -166,146 +195,51 @@ VALUE LibevBackend_wakeup(VALUE self) {
|
|
|
166
195
|
return Qnil;
|
|
167
196
|
}
|
|
168
197
|
|
|
169
|
-
#include "polyphony.h"
|
|
170
198
|
#include "../libev/ev.h"
|
|
171
199
|
|
|
172
|
-
|
|
173
|
-
//////////////////////////////////////////////////////////////////////
|
|
174
|
-
// the following is copied verbatim from the Ruby source code (io.c)
|
|
175
|
-
struct io_internal_read_struct {
|
|
176
|
-
int fd;
|
|
177
|
-
int nonblock;
|
|
178
|
-
void *buf;
|
|
179
|
-
size_t capa;
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
#define StringValue(v) rb_string_value(&(v))
|
|
183
|
-
|
|
184
|
-
int io_setstrbuf(VALUE *str, long len) {
|
|
185
|
-
#ifdef _WIN32
|
|
186
|
-
len = (len + 1) & ~1L; /* round up for wide char */
|
|
187
|
-
#endif
|
|
188
|
-
if (NIL_P(*str)) {
|
|
189
|
-
*str = rb_str_new(0, len);
|
|
190
|
-
return 1;
|
|
191
|
-
}
|
|
192
|
-
else {
|
|
193
|
-
VALUE s = StringValue(*str);
|
|
194
|
-
long clen = RSTRING_LEN(s);
|
|
195
|
-
if (clen >= len) {
|
|
196
|
-
rb_str_modify(s);
|
|
197
|
-
return 0;
|
|
198
|
-
}
|
|
199
|
-
len -= clen;
|
|
200
|
-
}
|
|
201
|
-
rb_str_modify_expand(*str, len);
|
|
202
|
-
return 0;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
#define MAX_REALLOC_GAP 4096
|
|
206
|
-
static void io_shrink_read_string(VALUE str, long n) {
|
|
207
|
-
if (rb_str_capacity(str) - n > MAX_REALLOC_GAP) {
|
|
208
|
-
rb_str_resize(str, n);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
void io_set_read_length(VALUE str, long n, int shrinkable) {
|
|
213
|
-
if (RSTRING_LEN(str) != n) {
|
|
214
|
-
rb_str_modify(str);
|
|
215
|
-
rb_str_set_len(str, n);
|
|
216
|
-
if (shrinkable) io_shrink_read_string(str, n);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
static rb_encoding* io_read_encoding(rb_io_t *fptr) {
|
|
221
|
-
if (fptr->encs.enc) {
|
|
222
|
-
return fptr->encs.enc;
|
|
223
|
-
}
|
|
224
|
-
return rb_default_external_encoding();
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
|
|
228
|
-
OBJ_TAINT(str);
|
|
229
|
-
rb_enc_associate(str, io_read_encoding(fptr));
|
|
230
|
-
return str;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
//////////////////////////////////////////////////////////////////////
|
|
234
|
-
//////////////////////////////////////////////////////////////////////
|
|
200
|
+
#include "backend_common.h"
|
|
235
201
|
|
|
236
202
|
struct libev_io {
|
|
237
203
|
struct ev_io io;
|
|
238
204
|
VALUE fiber;
|
|
239
205
|
};
|
|
240
206
|
|
|
241
|
-
void
|
|
207
|
+
void Backend_io_callback(EV_P_ ev_io *w, int revents)
|
|
242
208
|
{
|
|
243
209
|
struct libev_io *watcher = (struct libev_io *)w;
|
|
244
210
|
Fiber_make_runnable(watcher->fiber, Qnil);
|
|
245
211
|
}
|
|
246
212
|
|
|
247
|
-
|
|
248
|
-
VALUE ret;
|
|
249
|
-
backend->ref_count++;
|
|
250
|
-
ret = Thread_switch_fiber(rb_thread_current());
|
|
251
|
-
backend->ref_count--;
|
|
252
|
-
RB_GC_GUARD(ret);
|
|
253
|
-
return ret;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
VALUE libev_backend_await(VALUE self) {
|
|
257
|
-
LibevBackend_t *backend;
|
|
258
|
-
GetLibevBackend(self, backend);
|
|
259
|
-
return libev_await(backend);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
VALUE libev_io_wait(LibevBackend_t *backend, struct libev_io *watcher, rb_io_t *fptr, int flags) {
|
|
213
|
+
VALUE libev_wait_fd_with_watcher(Backend_t *backend, int fd, struct libev_io *watcher, int events) {
|
|
263
214
|
VALUE switchpoint_result;
|
|
264
215
|
|
|
265
216
|
if (watcher->fiber == Qnil) {
|
|
266
217
|
watcher->fiber = rb_fiber_current();
|
|
267
|
-
ev_io_init(&watcher->io,
|
|
218
|
+
ev_io_init(&watcher->io, Backend_io_callback, fd, events);
|
|
268
219
|
}
|
|
269
220
|
ev_io_start(backend->ev_loop, &watcher->io);
|
|
270
|
-
switchpoint_result = libev_await(backend);
|
|
271
|
-
ev_io_stop(backend->ev_loop, &watcher->io);
|
|
272
221
|
|
|
222
|
+
switchpoint_result = backend_await(backend);
|
|
223
|
+
|
|
224
|
+
ev_io_stop(backend->ev_loop, &watcher->io);
|
|
273
225
|
RB_GC_GUARD(switchpoint_result);
|
|
274
|
-
return switchpoint_result;
|
|
226
|
+
return switchpoint_result;
|
|
275
227
|
}
|
|
276
228
|
|
|
277
|
-
VALUE
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
229
|
+
VALUE libev_wait_fd(Backend_t *backend, int fd, int events, int raise_exception) {
|
|
230
|
+
struct libev_io watcher;
|
|
231
|
+
VALUE switchpoint_result = Qnil;
|
|
232
|
+
watcher.fiber = Qnil;
|
|
281
233
|
|
|
282
|
-
|
|
234
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fd, &watcher, events);
|
|
283
235
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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;
|
|
236
|
+
if (raise_exception) RAISE_IF_EXCEPTION(switchpoint_result);
|
|
237
|
+
RB_GC_GUARD(switchpoint_result);
|
|
238
|
+
return switchpoint_result;
|
|
305
239
|
}
|
|
306
240
|
|
|
307
|
-
VALUE
|
|
308
|
-
|
|
241
|
+
VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
|
|
242
|
+
Backend_t *backend;
|
|
309
243
|
struct libev_io watcher;
|
|
310
244
|
rb_io_t *fptr;
|
|
311
245
|
long dynamic_len = length == Qnil;
|
|
@@ -315,37 +249,30 @@ VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_
|
|
|
315
249
|
long total = 0;
|
|
316
250
|
VALUE switchpoint_result = Qnil;
|
|
317
251
|
int read_to_eof = RTEST(to_eof);
|
|
318
|
-
VALUE underlying_io =
|
|
252
|
+
VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
|
|
319
253
|
|
|
320
|
-
|
|
254
|
+
GetBackend(self, backend);
|
|
321
255
|
if (underlying_io != Qnil) io = underlying_io;
|
|
322
256
|
GetOpenFile(io, fptr);
|
|
323
257
|
rb_io_check_byte_readable(fptr);
|
|
324
258
|
io_set_nonblock(fptr, io);
|
|
259
|
+
rectify_io_file_pos(fptr);
|
|
325
260
|
watcher.fiber = Qnil;
|
|
326
|
-
|
|
327
261
|
OBJ_TAINT(str);
|
|
328
262
|
|
|
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
|
-
|
|
338
263
|
while (1) {
|
|
339
264
|
ssize_t n = read(fptr->fd, buf, len - total);
|
|
340
265
|
if (n < 0) {
|
|
341
266
|
int e = errno;
|
|
342
267
|
if (e != EWOULDBLOCK && e != EAGAIN) rb_syserr_fail(e, strerror(e));
|
|
343
|
-
|
|
344
|
-
switchpoint_result =
|
|
268
|
+
|
|
269
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
|
|
270
|
+
|
|
345
271
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
346
272
|
}
|
|
347
273
|
else {
|
|
348
|
-
switchpoint_result =
|
|
274
|
+
switchpoint_result = backend_snooze();
|
|
275
|
+
|
|
349
276
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
350
277
|
|
|
351
278
|
if (n == 0) break; // EOF
|
|
@@ -354,7 +281,7 @@ VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_
|
|
|
354
281
|
|
|
355
282
|
if (total == len) {
|
|
356
283
|
if (!dynamic_len) break;
|
|
357
|
-
|
|
284
|
+
|
|
358
285
|
rb_str_resize(str, total);
|
|
359
286
|
rb_str_modify_expand(str, len);
|
|
360
287
|
buf = RSTRING_PTR(str) + total;
|
|
@@ -364,11 +291,12 @@ VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_
|
|
|
364
291
|
else buf += n;
|
|
365
292
|
}
|
|
366
293
|
}
|
|
367
|
-
if (total == 0) return Qnil;
|
|
368
294
|
|
|
369
295
|
io_set_read_length(str, total, shrinkable);
|
|
370
296
|
io_enc_str(str, fptr);
|
|
371
|
-
|
|
297
|
+
|
|
298
|
+
if (total == 0) return Qnil;
|
|
299
|
+
|
|
372
300
|
RB_GC_GUARD(watcher.fiber);
|
|
373
301
|
RB_GC_GUARD(switchpoint_result);
|
|
374
302
|
|
|
@@ -377,24 +305,12 @@ error:
|
|
|
377
305
|
return RAISE_EXCEPTION(switchpoint_result);
|
|
378
306
|
}
|
|
379
307
|
|
|
380
|
-
VALUE
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
str = Qnil; \
|
|
384
|
-
shrinkable = io_setstrbuf(&str, len); \
|
|
385
|
-
buf = RSTRING_PTR(str); \
|
|
386
|
-
total = 0; \
|
|
387
|
-
OBJ_TAINT(str); \
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
#define YIELD_STR() { \
|
|
391
|
-
io_set_read_length(str, total, shrinkable); \
|
|
392
|
-
io_enc_str(str, fptr); \
|
|
393
|
-
rb_yield(str); \
|
|
394
|
-
PREPARE_STR(); \
|
|
395
|
-
}
|
|
308
|
+
VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length) {
|
|
309
|
+
return Backend_read(self, io, str, length, Qnil);
|
|
310
|
+
}
|
|
396
311
|
|
|
397
|
-
|
|
312
|
+
VALUE Backend_read_loop(VALUE self, VALUE io) {
|
|
313
|
+
Backend_t *backend;
|
|
398
314
|
struct libev_io watcher;
|
|
399
315
|
rb_io_t *fptr;
|
|
400
316
|
VALUE str;
|
|
@@ -403,48 +319,35 @@ VALUE LibevBackend_read_loop(VALUE self, VALUE io) {
|
|
|
403
319
|
int shrinkable;
|
|
404
320
|
char *buf;
|
|
405
321
|
VALUE switchpoint_result = Qnil;
|
|
406
|
-
VALUE underlying_io =
|
|
322
|
+
VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
|
|
407
323
|
|
|
408
|
-
|
|
324
|
+
READ_LOOP_PREPARE_STR();
|
|
409
325
|
|
|
410
|
-
|
|
326
|
+
GetBackend(self, backend);
|
|
411
327
|
if (underlying_io != Qnil) io = underlying_io;
|
|
412
328
|
GetOpenFile(io, fptr);
|
|
413
329
|
rb_io_check_byte_readable(fptr);
|
|
414
330
|
io_set_nonblock(fptr, io);
|
|
331
|
+
rectify_io_file_pos(fptr);
|
|
415
332
|
watcher.fiber = Qnil;
|
|
416
333
|
|
|
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
|
-
}
|
|
425
|
-
|
|
426
334
|
while (1) {
|
|
427
335
|
ssize_t n = read(fptr->fd, buf, len);
|
|
428
336
|
if (n < 0) {
|
|
429
337
|
int e = errno;
|
|
430
338
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
431
339
|
|
|
432
|
-
switchpoint_result =
|
|
340
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
|
|
433
341
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
434
342
|
}
|
|
435
343
|
else {
|
|
436
|
-
switchpoint_result =
|
|
437
|
-
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
344
|
+
switchpoint_result = backend_snooze();
|
|
438
345
|
|
|
439
|
-
if (
|
|
346
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
440
347
|
|
|
348
|
+
if (n == 0) break; // EOF
|
|
441
349
|
total = n;
|
|
442
|
-
|
|
443
|
-
Fiber_make_runnable(rb_fiber_current(), Qnil);
|
|
444
|
-
switchpoint_result = Thread_switch_fiber(rb_thread_current());
|
|
445
|
-
if (TEST_EXCEPTION(switchpoint_result)) {
|
|
446
|
-
goto error;
|
|
447
|
-
}
|
|
350
|
+
READ_LOOP_YIELD_STR();
|
|
448
351
|
}
|
|
449
352
|
}
|
|
450
353
|
|
|
@@ -457,8 +360,8 @@ error:
|
|
|
457
360
|
return RAISE_EXCEPTION(switchpoint_result);
|
|
458
361
|
}
|
|
459
362
|
|
|
460
|
-
VALUE
|
|
461
|
-
|
|
363
|
+
VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
|
364
|
+
Backend_t *backend;
|
|
462
365
|
struct libev_io watcher;
|
|
463
366
|
rb_io_t *fptr;
|
|
464
367
|
VALUE switchpoint_result = Qnil;
|
|
@@ -467,11 +370,12 @@ VALUE LibevBackend_write(VALUE self, VALUE io, VALUE str) {
|
|
|
467
370
|
long len = RSTRING_LEN(str);
|
|
468
371
|
long left = len;
|
|
469
372
|
|
|
470
|
-
underlying_io =
|
|
373
|
+
underlying_io = rb_ivar_get(io, ID_ivar_io);
|
|
471
374
|
if (underlying_io != Qnil) io = underlying_io;
|
|
472
|
-
|
|
375
|
+
GetBackend(self, backend);
|
|
473
376
|
io = rb_io_get_write_io(io);
|
|
474
377
|
GetOpenFile(io, fptr);
|
|
378
|
+
io_set_nonblock(fptr, io);
|
|
475
379
|
watcher.fiber = Qnil;
|
|
476
380
|
|
|
477
381
|
while (left > 0) {
|
|
@@ -479,7 +383,9 @@ VALUE LibevBackend_write(VALUE self, VALUE io, VALUE str) {
|
|
|
479
383
|
if (n < 0) {
|
|
480
384
|
int e = errno;
|
|
481
385
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
482
|
-
|
|
386
|
+
|
|
387
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
|
|
388
|
+
|
|
483
389
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
484
390
|
}
|
|
485
391
|
else {
|
|
@@ -489,7 +395,8 @@ VALUE LibevBackend_write(VALUE self, VALUE io, VALUE str) {
|
|
|
489
395
|
}
|
|
490
396
|
|
|
491
397
|
if (watcher.fiber == Qnil) {
|
|
492
|
-
switchpoint_result =
|
|
398
|
+
switchpoint_result = backend_snooze();
|
|
399
|
+
|
|
493
400
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
494
401
|
}
|
|
495
402
|
|
|
@@ -501,8 +408,8 @@ error:
|
|
|
501
408
|
return RAISE_EXCEPTION(switchpoint_result);
|
|
502
409
|
}
|
|
503
410
|
|
|
504
|
-
VALUE
|
|
505
|
-
|
|
411
|
+
VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
412
|
+
Backend_t *backend;
|
|
506
413
|
struct libev_io watcher;
|
|
507
414
|
rb_io_t *fptr;
|
|
508
415
|
VALUE switchpoint_result = Qnil;
|
|
@@ -513,11 +420,12 @@ VALUE LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
|
513
420
|
struct iovec *iov_ptr = 0;
|
|
514
421
|
int iov_count = argc;
|
|
515
422
|
|
|
516
|
-
underlying_io =
|
|
423
|
+
underlying_io = rb_ivar_get(io, ID_ivar_io);
|
|
517
424
|
if (underlying_io != Qnil) io = underlying_io;
|
|
518
|
-
|
|
425
|
+
GetBackend(self, backend);
|
|
519
426
|
io = rb_io_get_write_io(io);
|
|
520
427
|
GetOpenFile(io, fptr);
|
|
428
|
+
io_set_nonblock(fptr, io);
|
|
521
429
|
watcher.fiber = Qnil;
|
|
522
430
|
|
|
523
431
|
iov = malloc(iov_count * sizeof(struct iovec));
|
|
@@ -533,9 +441,13 @@ VALUE LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
|
533
441
|
ssize_t n = writev(fptr->fd, iov_ptr, iov_count);
|
|
534
442
|
if (n < 0) {
|
|
535
443
|
int e = errno;
|
|
536
|
-
if ((e != EWOULDBLOCK && e != EAGAIN))
|
|
444
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) {
|
|
445
|
+
free(iov);
|
|
446
|
+
rb_syserr_fail(e, strerror(e));
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
|
|
537
450
|
|
|
538
|
-
switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_WRITE);
|
|
539
451
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
540
452
|
}
|
|
541
453
|
else {
|
|
@@ -557,7 +469,7 @@ VALUE LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
|
557
469
|
}
|
|
558
470
|
}
|
|
559
471
|
if (watcher.fiber == Qnil) {
|
|
560
|
-
switchpoint_result =
|
|
472
|
+
switchpoint_result = backend_snooze();
|
|
561
473
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
562
474
|
}
|
|
563
475
|
|
|
@@ -571,30 +483,28 @@ error:
|
|
|
571
483
|
return RAISE_EXCEPTION(switchpoint_result);
|
|
572
484
|
}
|
|
573
485
|
|
|
574
|
-
VALUE
|
|
486
|
+
VALUE Backend_write_m(int argc, VALUE *argv, VALUE self) {
|
|
575
487
|
if (argc < 2)
|
|
576
488
|
// TODO: raise ArgumentError
|
|
577
489
|
rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
|
|
578
|
-
|
|
490
|
+
|
|
579
491
|
return (argc == 2) ?
|
|
580
|
-
|
|
581
|
-
|
|
492
|
+
Backend_write(self, argv[0], argv[1]) :
|
|
493
|
+
Backend_writev(self, argv[0], argc - 1, argv + 1);
|
|
582
494
|
}
|
|
583
495
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
VALUE LibevBackend_accept(VALUE self, VALUE sock) {
|
|
587
|
-
LibevBackend_t *backend;
|
|
496
|
+
VALUE Backend_accept(VALUE self, VALUE sock) {
|
|
497
|
+
Backend_t *backend;
|
|
588
498
|
struct libev_io watcher;
|
|
589
499
|
rb_io_t *fptr;
|
|
590
500
|
int fd;
|
|
591
501
|
struct sockaddr addr;
|
|
592
502
|
socklen_t len = (socklen_t)sizeof addr;
|
|
593
503
|
VALUE switchpoint_result = Qnil;
|
|
594
|
-
VALUE underlying_sock =
|
|
504
|
+
VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
|
|
595
505
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
|
596
506
|
|
|
597
|
-
|
|
507
|
+
GetBackend(self, backend);
|
|
598
508
|
GetOpenFile(sock, fptr);
|
|
599
509
|
io_set_nonblock(fptr, sock);
|
|
600
510
|
watcher.fiber = Qnil;
|
|
@@ -604,13 +514,15 @@ VALUE LibevBackend_accept(VALUE self, VALUE sock) {
|
|
|
604
514
|
int e = errno;
|
|
605
515
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
606
516
|
|
|
607
|
-
switchpoint_result =
|
|
517
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
|
|
518
|
+
|
|
608
519
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
609
520
|
}
|
|
610
521
|
else {
|
|
611
522
|
VALUE socket;
|
|
612
523
|
rb_io_t *fp;
|
|
613
|
-
switchpoint_result =
|
|
524
|
+
switchpoint_result = backend_snooze();
|
|
525
|
+
|
|
614
526
|
if (TEST_EXCEPTION(switchpoint_result)) {
|
|
615
527
|
close(fd); // close fd since we're raising an exception
|
|
616
528
|
goto error;
|
|
@@ -624,7 +536,7 @@ VALUE LibevBackend_accept(VALUE self, VALUE sock) {
|
|
|
624
536
|
rb_io_ascii8bit_binmode(socket);
|
|
625
537
|
io_set_nonblock(fp, socket);
|
|
626
538
|
rb_io_synchronized(fp);
|
|
627
|
-
|
|
539
|
+
|
|
628
540
|
// if (rsock_do_not_reverse_lookup) {
|
|
629
541
|
// fp->mode |= FMODE_NOREVLOOKUP;
|
|
630
542
|
// }
|
|
@@ -637,8 +549,8 @@ error:
|
|
|
637
549
|
return RAISE_EXCEPTION(switchpoint_result);
|
|
638
550
|
}
|
|
639
551
|
|
|
640
|
-
VALUE
|
|
641
|
-
|
|
552
|
+
VALUE Backend_accept_loop(VALUE self, VALUE sock) {
|
|
553
|
+
Backend_t *backend;
|
|
642
554
|
struct libev_io watcher;
|
|
643
555
|
rb_io_t *fptr;
|
|
644
556
|
int fd;
|
|
@@ -646,10 +558,10 @@ VALUE LibevBackend_accept_loop(VALUE self, VALUE sock) {
|
|
|
646
558
|
socklen_t len = (socklen_t)sizeof addr;
|
|
647
559
|
VALUE switchpoint_result = Qnil;
|
|
648
560
|
VALUE socket = Qnil;
|
|
649
|
-
VALUE underlying_sock =
|
|
561
|
+
VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
|
|
650
562
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
|
651
563
|
|
|
652
|
-
|
|
564
|
+
GetBackend(self, backend);
|
|
653
565
|
GetOpenFile(sock, fptr);
|
|
654
566
|
io_set_nonblock(fptr, sock);
|
|
655
567
|
watcher.fiber = Qnil;
|
|
@@ -660,17 +572,19 @@ VALUE LibevBackend_accept_loop(VALUE self, VALUE sock) {
|
|
|
660
572
|
int e = errno;
|
|
661
573
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
662
574
|
|
|
663
|
-
switchpoint_result =
|
|
575
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
|
|
576
|
+
|
|
664
577
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
665
578
|
}
|
|
666
579
|
else {
|
|
667
580
|
rb_io_t *fp;
|
|
668
|
-
switchpoint_result =
|
|
581
|
+
switchpoint_result = backend_snooze();
|
|
582
|
+
|
|
669
583
|
if (TEST_EXCEPTION(switchpoint_result)) {
|
|
670
584
|
close(fd); // close fd since we're raising an exception
|
|
671
585
|
goto error;
|
|
672
586
|
}
|
|
673
|
-
|
|
587
|
+
|
|
674
588
|
socket = rb_obj_alloc(cTCPSocket);
|
|
675
589
|
MakeOpenFile(socket, fp);
|
|
676
590
|
rb_update_max_fd(fd);
|
|
@@ -693,22 +607,22 @@ error:
|
|
|
693
607
|
return RAISE_EXCEPTION(switchpoint_result);
|
|
694
608
|
}
|
|
695
609
|
|
|
696
|
-
VALUE
|
|
697
|
-
|
|
610
|
+
VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
611
|
+
Backend_t *backend;
|
|
698
612
|
struct libev_io watcher;
|
|
699
613
|
rb_io_t *fptr;
|
|
700
614
|
struct sockaddr_in addr;
|
|
701
615
|
char *host_buf = StringValueCStr(host);
|
|
702
616
|
VALUE switchpoint_result = Qnil;
|
|
703
|
-
VALUE underlying_sock =
|
|
617
|
+
VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
|
|
704
618
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
|
705
619
|
|
|
706
|
-
|
|
620
|
+
GetBackend(self, backend);
|
|
707
621
|
GetOpenFile(sock, fptr);
|
|
708
622
|
io_set_nonblock(fptr, sock);
|
|
709
623
|
watcher.fiber = Qnil;
|
|
710
624
|
|
|
711
|
-
addr.sin_family = AF_INET;
|
|
625
|
+
addr.sin_family = AF_INET;
|
|
712
626
|
addr.sin_addr.s_addr = inet_addr(host_buf);
|
|
713
627
|
addr.sin_port = htons(NUM2INT(port));
|
|
714
628
|
|
|
@@ -716,11 +630,14 @@ VALUE LibevBackend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
|
716
630
|
if (result < 0) {
|
|
717
631
|
int e = errno;
|
|
718
632
|
if (e != EINPROGRESS) rb_syserr_fail(e, strerror(e));
|
|
719
|
-
|
|
633
|
+
|
|
634
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
|
|
635
|
+
|
|
720
636
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
721
637
|
}
|
|
722
638
|
else {
|
|
723
|
-
switchpoint_result =
|
|
639
|
+
switchpoint_result = backend_snooze();
|
|
640
|
+
|
|
724
641
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
725
642
|
}
|
|
726
643
|
RB_GC_GUARD(switchpoint_result);
|
|
@@ -729,30 +646,15 @@ error:
|
|
|
729
646
|
return RAISE_EXCEPTION(switchpoint_result);
|
|
730
647
|
}
|
|
731
648
|
|
|
732
|
-
VALUE
|
|
733
|
-
|
|
734
|
-
VALUE switchpoint_result = Qnil;
|
|
735
|
-
|
|
736
|
-
watcher.fiber = rb_fiber_current();
|
|
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);
|
|
741
|
-
|
|
742
|
-
if (raise_exception) TEST_RESUME_EXCEPTION(switchpoint_result);
|
|
743
|
-
RB_GC_GUARD(switchpoint_result);
|
|
744
|
-
return switchpoint_result;
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
VALUE LibevBackend_wait_io(VALUE self, VALUE io, VALUE write) {
|
|
748
|
-
LibevBackend_t *backend;
|
|
649
|
+
VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
|
|
650
|
+
Backend_t *backend;
|
|
749
651
|
rb_io_t *fptr;
|
|
750
652
|
int events = RTEST(write) ? EV_WRITE : EV_READ;
|
|
751
|
-
VALUE underlying_io =
|
|
653
|
+
VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
|
|
752
654
|
if (underlying_io != Qnil) io = underlying_io;
|
|
753
|
-
|
|
655
|
+
GetBackend(self, backend);
|
|
754
656
|
GetOpenFile(io, fptr);
|
|
755
|
-
|
|
657
|
+
|
|
756
658
|
return libev_wait_fd(backend, fptr->fd, events, 1);
|
|
757
659
|
}
|
|
758
660
|
|
|
@@ -761,27 +663,26 @@ struct libev_timer {
|
|
|
761
663
|
VALUE fiber;
|
|
762
664
|
};
|
|
763
665
|
|
|
764
|
-
void
|
|
666
|
+
void Backend_timer_callback(EV_P_ ev_timer *w, int revents)
|
|
765
667
|
{
|
|
766
668
|
struct libev_timer *watcher = (struct libev_timer *)w;
|
|
767
669
|
Fiber_make_runnable(watcher->fiber, Qnil);
|
|
768
670
|
}
|
|
769
671
|
|
|
770
|
-
VALUE
|
|
771
|
-
|
|
672
|
+
VALUE Backend_sleep(VALUE self, VALUE duration) {
|
|
673
|
+
Backend_t *backend;
|
|
772
674
|
struct libev_timer watcher;
|
|
773
675
|
VALUE switchpoint_result = Qnil;
|
|
774
676
|
|
|
775
|
-
|
|
677
|
+
GetBackend(self, backend);
|
|
776
678
|
watcher.fiber = rb_fiber_current();
|
|
777
|
-
ev_timer_init(&watcher.timer,
|
|
679
|
+
ev_timer_init(&watcher.timer, Backend_timer_callback, NUM2DBL(duration), 0.);
|
|
778
680
|
ev_timer_start(backend->ev_loop, &watcher.timer);
|
|
779
681
|
|
|
780
|
-
switchpoint_result =
|
|
682
|
+
switchpoint_result = backend_await(backend);
|
|
781
683
|
|
|
782
684
|
ev_timer_stop(backend->ev_loop, &watcher.timer);
|
|
783
|
-
|
|
784
|
-
TEST_RESUME_EXCEPTION(switchpoint_result);
|
|
685
|
+
RAISE_IF_EXCEPTION(switchpoint_result);
|
|
785
686
|
RB_GC_GUARD(watcher.fiber);
|
|
786
687
|
RB_GC_GUARD(switchpoint_result);
|
|
787
688
|
return switchpoint_result;
|
|
@@ -792,97 +693,106 @@ struct libev_child {
|
|
|
792
693
|
VALUE fiber;
|
|
793
694
|
};
|
|
794
695
|
|
|
795
|
-
void
|
|
696
|
+
void Backend_child_callback(EV_P_ ev_child *w, int revents)
|
|
796
697
|
{
|
|
797
698
|
struct libev_child *watcher = (struct libev_child *)w;
|
|
798
|
-
int exit_status = w->rstatus
|
|
699
|
+
int exit_status = WEXITSTATUS(w->rstatus);
|
|
799
700
|
VALUE status;
|
|
800
701
|
|
|
801
702
|
status = rb_ary_new_from_args(2, INT2NUM(w->rpid), INT2NUM(exit_status));
|
|
802
703
|
Fiber_make_runnable(watcher->fiber, status);
|
|
803
704
|
}
|
|
804
705
|
|
|
805
|
-
VALUE
|
|
806
|
-
|
|
706
|
+
VALUE Backend_waitpid(VALUE self, VALUE pid) {
|
|
707
|
+
Backend_t *backend;
|
|
807
708
|
struct libev_child watcher;
|
|
808
709
|
VALUE switchpoint_result = Qnil;
|
|
809
|
-
|
|
710
|
+
GetBackend(self, backend);
|
|
810
711
|
|
|
811
712
|
watcher.fiber = rb_fiber_current();
|
|
812
|
-
ev_child_init(&watcher.child,
|
|
713
|
+
ev_child_init(&watcher.child, Backend_child_callback, NUM2INT(pid), 0);
|
|
813
714
|
ev_child_start(backend->ev_loop, &watcher.child);
|
|
814
|
-
|
|
815
|
-
switchpoint_result = libev_await(backend);
|
|
816
|
-
ev_child_stop(backend->ev_loop, &watcher.child);
|
|
817
715
|
|
|
818
|
-
|
|
716
|
+
switchpoint_result = backend_await(backend);
|
|
717
|
+
|
|
718
|
+
ev_child_stop(backend->ev_loop, &watcher.child);
|
|
719
|
+
RAISE_IF_EXCEPTION(switchpoint_result);
|
|
819
720
|
RB_GC_GUARD(watcher.fiber);
|
|
820
721
|
RB_GC_GUARD(switchpoint_result);
|
|
821
722
|
return switchpoint_result;
|
|
822
723
|
}
|
|
823
724
|
|
|
824
|
-
|
|
825
|
-
LibevBackend_t *backend;
|
|
826
|
-
GetLibevBackend(self, backend);
|
|
827
|
-
return backend->ev_loop;
|
|
828
|
-
}
|
|
725
|
+
void Backend_async_callback(EV_P_ ev_async *w, int revents) { }
|
|
829
726
|
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
VALUE LibevBackend_wait_event(VALUE self, VALUE raise) {
|
|
833
|
-
LibevBackend_t *backend;
|
|
727
|
+
VALUE Backend_wait_event(VALUE self, VALUE raise) {
|
|
728
|
+
Backend_t *backend;
|
|
834
729
|
VALUE switchpoint_result = Qnil;
|
|
835
|
-
|
|
730
|
+
GetBackend(self, backend);
|
|
836
731
|
|
|
837
732
|
struct ev_async async;
|
|
838
733
|
|
|
839
|
-
ev_async_init(&async,
|
|
734
|
+
ev_async_init(&async, Backend_async_callback);
|
|
840
735
|
ev_async_start(backend->ev_loop, &async);
|
|
841
|
-
switchpoint_result = libev_await(backend);
|
|
842
|
-
ev_async_stop(backend->ev_loop, &async);
|
|
843
736
|
|
|
844
|
-
|
|
737
|
+
switchpoint_result = backend_await(backend);
|
|
738
|
+
|
|
739
|
+
ev_async_stop(backend->ev_loop, &async);
|
|
740
|
+
if (RTEST(raise)) RAISE_IF_EXCEPTION(switchpoint_result);
|
|
845
741
|
RB_GC_GUARD(switchpoint_result);
|
|
846
742
|
return switchpoint_result;
|
|
847
743
|
}
|
|
848
744
|
|
|
849
|
-
|
|
745
|
+
VALUE Backend_kind(VALUE self) {
|
|
746
|
+
return SYM_libev;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
void Init_Backend() {
|
|
750
|
+
ev_set_allocator(xrealloc);
|
|
751
|
+
|
|
850
752
|
rb_require("socket");
|
|
851
753
|
cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
|
|
852
754
|
|
|
853
755
|
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cData);
|
|
854
|
-
rb_define_alloc_func(cBackend,
|
|
855
|
-
|
|
856
|
-
rb_define_method(cBackend, "initialize",
|
|
857
|
-
rb_define_method(cBackend, "finalize",
|
|
858
|
-
rb_define_method(cBackend, "post_fork",
|
|
859
|
-
rb_define_method(cBackend, "pending_count",
|
|
860
|
-
|
|
861
|
-
rb_define_method(cBackend, "ref",
|
|
862
|
-
rb_define_method(cBackend, "unref",
|
|
863
|
-
|
|
864
|
-
rb_define_method(cBackend, "poll",
|
|
865
|
-
rb_define_method(cBackend, "break",
|
|
866
|
-
|
|
867
|
-
rb_define_method(cBackend, "read",
|
|
868
|
-
rb_define_method(cBackend, "read_loop",
|
|
869
|
-
rb_define_method(cBackend, "write",
|
|
870
|
-
rb_define_method(cBackend, "accept",
|
|
871
|
-
rb_define_method(cBackend, "accept_loop",
|
|
872
|
-
rb_define_method(cBackend, "connect",
|
|
873
|
-
rb_define_method(cBackend, "
|
|
874
|
-
rb_define_method(cBackend, "
|
|
875
|
-
rb_define_method(cBackend, "
|
|
876
|
-
rb_define_method(cBackend, "
|
|
756
|
+
rb_define_alloc_func(cBackend, Backend_allocate);
|
|
757
|
+
|
|
758
|
+
rb_define_method(cBackend, "initialize", Backend_initialize, 0);
|
|
759
|
+
rb_define_method(cBackend, "finalize", Backend_finalize, 0);
|
|
760
|
+
rb_define_method(cBackend, "post_fork", Backend_post_fork, 0);
|
|
761
|
+
rb_define_method(cBackend, "pending_count", Backend_pending_count, 0);
|
|
762
|
+
|
|
763
|
+
rb_define_method(cBackend, "ref", Backend_ref, 0);
|
|
764
|
+
rb_define_method(cBackend, "unref", Backend_unref, 0);
|
|
765
|
+
|
|
766
|
+
rb_define_method(cBackend, "poll", Backend_poll, 3);
|
|
767
|
+
rb_define_method(cBackend, "break", Backend_wakeup, 0);
|
|
768
|
+
|
|
769
|
+
rb_define_method(cBackend, "read", Backend_read, 4);
|
|
770
|
+
rb_define_method(cBackend, "read_loop", Backend_read_loop, 1);
|
|
771
|
+
rb_define_method(cBackend, "write", Backend_write_m, -1);
|
|
772
|
+
rb_define_method(cBackend, "accept", Backend_accept, 1);
|
|
773
|
+
rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 1);
|
|
774
|
+
rb_define_method(cBackend, "connect", Backend_connect, 3);
|
|
775
|
+
rb_define_method(cBackend, "recv", Backend_recv, 3);
|
|
776
|
+
rb_define_method(cBackend, "recv_loop", Backend_read_loop, 1);
|
|
777
|
+
rb_define_method(cBackend, "send", Backend_write, 2);
|
|
778
|
+
rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
|
|
779
|
+
rb_define_method(cBackend, "sleep", Backend_sleep, 1);
|
|
780
|
+
rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
|
|
781
|
+
rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
|
|
782
|
+
|
|
783
|
+
rb_define_method(cBackend, "kind", Backend_kind, 0);
|
|
877
784
|
|
|
878
785
|
ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
|
|
786
|
+
SYM_libev = ID2SYM(rb_intern("libev"));
|
|
879
787
|
|
|
880
|
-
__BACKEND__.pending_count =
|
|
881
|
-
__BACKEND__.poll =
|
|
882
|
-
__BACKEND__.ref =
|
|
883
|
-
__BACKEND__.ref_count =
|
|
884
|
-
__BACKEND__.reset_ref_count =
|
|
885
|
-
__BACKEND__.unref =
|
|
886
|
-
__BACKEND__.wait_event =
|
|
887
|
-
__BACKEND__.wakeup =
|
|
788
|
+
__BACKEND__.pending_count = Backend_pending_count;
|
|
789
|
+
__BACKEND__.poll = Backend_poll;
|
|
790
|
+
__BACKEND__.ref = Backend_ref;
|
|
791
|
+
__BACKEND__.ref_count = Backend_ref_count;
|
|
792
|
+
__BACKEND__.reset_ref_count = Backend_reset_ref_count;
|
|
793
|
+
__BACKEND__.unref = Backend_unref;
|
|
794
|
+
__BACKEND__.wait_event = Backend_wait_event;
|
|
795
|
+
__BACKEND__.wakeup = Backend_wakeup;
|
|
888
796
|
}
|
|
797
|
+
|
|
798
|
+
#endif // POLYPHONY_BACKEND_LIBEV
|