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.
Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +2 -0
  3. data/.gitmodules +0 -0
  4. data/.rubocop.yml +1 -0
  5. data/CHANGELOG.md +38 -0
  6. data/Gemfile.lock +11 -3
  7. data/README.md +3 -3
  8. data/Rakefile +1 -1
  9. data/TODO.md +10 -18
  10. data/examples/adapters/redis_client.rb +3 -1
  11. data/examples/adapters/redis_pubsub_perf.rb +11 -8
  12. data/examples/adapters/sequel_mysql.rb +1 -1
  13. data/examples/adapters/sequel_pg.rb +24 -0
  14. data/examples/core/{02-awaiting-fibers.rb → await.rb} +0 -0
  15. data/examples/core/{xx-channels.rb → channels.rb} +0 -0
  16. data/examples/core/deferring-an-operation.rb +16 -0
  17. data/examples/core/{xx-erlang-style-genserver.rb → erlang-style-genserver.rb} +16 -9
  18. data/examples/core/{xx-forking.rb → forking.rb} +1 -1
  19. data/examples/core/handling-signals.rb +11 -0
  20. data/examples/core/{03-interrupting.rb → interrupt.rb} +0 -0
  21. data/examples/core/{xx-pingpong.rb → pingpong.rb} +7 -5
  22. data/examples/core/{xx-recurrent-timer.rb → recurrent-timer.rb} +1 -1
  23. data/examples/core/{xx-resource_delegate.rb → resource_delegate.rb} +3 -4
  24. data/examples/core/{01-spinning-up-fibers.rb → spin.rb} +1 -1
  25. data/examples/core/{xx-spin_error_backtrace.rb → spin_error_backtrace.rb} +1 -1
  26. data/examples/core/{xx-supervise-process.rb → supervise-process.rb} +8 -5
  27. data/examples/core/supervisor.rb +20 -0
  28. data/examples/core/{xx-thread-sleep.rb → thread-sleep.rb} +0 -0
  29. data/examples/core/{xx-thread_pool.rb → thread_pool.rb} +0 -0
  30. data/examples/core/{xx-throttling.rb → throttling.rb} +0 -0
  31. data/examples/core/{xx-timeout.rb → timeout.rb} +0 -0
  32. data/examples/core/{xx-using-a-mutex.rb → using-a-mutex.rb} +0 -0
  33. data/examples/core/{xx-worker-thread.rb → worker-thread.rb} +2 -2
  34. data/examples/io/{xx-backticks.rb → backticks.rb} +0 -0
  35. data/examples/io/{xx-echo_client.rb → echo_client.rb} +1 -1
  36. data/examples/io/{xx-echo_client_from_stdin.rb → echo_client_from_stdin.rb} +2 -2
  37. data/examples/io/{xx-echo_pipe.rb → echo_pipe.rb} +1 -1
  38. data/examples/io/{xx-echo_server.rb → echo_server.rb} +0 -0
  39. data/examples/io/{xx-echo_server_with_timeout.rb → echo_server_with_timeout.rb} +1 -1
  40. data/examples/io/{xx-echo_stdin.rb → echo_stdin.rb} +0 -0
  41. data/examples/io/{xx-happy-eyeballs.rb → happy-eyeballs.rb} +0 -0
  42. data/examples/io/{xx-httparty.rb → httparty.rb} +4 -13
  43. data/examples/io/{xx-irb.rb → irb.rb} +0 -0
  44. data/examples/io/{xx-net-http.rb → net-http.rb} +0 -0
  45. data/examples/io/{xx-open.rb → open.rb} +0 -0
  46. data/examples/io/{xx-pry.rb → pry.rb} +0 -0
  47. data/examples/io/{xx-rack_server.rb → rack_server.rb} +0 -0
  48. data/examples/io/raw.rb +14 -0
  49. data/examples/io/reline.rb +18 -0
  50. data/examples/io/{xx-system.rb → system.rb} +1 -1
  51. data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
  52. data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
  53. data/examples/io/tunnel.rb +6 -1
  54. data/examples/io/{xx-zip.rb → zip.rb} +0 -0
  55. data/examples/performance/fiber_transfer.rb +2 -1
  56. data/examples/performance/fs_read.rb +5 -6
  57. data/examples/performance/multi_snooze.rb +0 -1
  58. data/examples/{io/xx-switch.rb → performance/switch.rb} +2 -1
  59. data/examples/performance/thread-vs-fiber/{xx-httparty_multi.rb → httparty_multi.rb} +3 -4
  60. data/examples/performance/thread-vs-fiber/{xx-httparty_threaded.rb → httparty_threaded.rb} +0 -0
  61. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +1 -1
  62. data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -2
  63. data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
  64. data/examples/performance/thread_pool_perf.rb +6 -7
  65. data/ext/liburing/liburing.h +585 -0
  66. data/ext/liburing/liburing/README.md +4 -0
  67. data/ext/liburing/liburing/barrier.h +73 -0
  68. data/ext/liburing/liburing/compat.h +15 -0
  69. data/ext/liburing/liburing/io_uring.h +343 -0
  70. data/ext/liburing/queue.c +333 -0
  71. data/ext/liburing/register.c +187 -0
  72. data/ext/liburing/setup.c +210 -0
  73. data/ext/liburing/syscall.c +54 -0
  74. data/ext/liburing/syscall.h +18 -0
  75. data/ext/polyphony/backend.h +1 -16
  76. data/ext/polyphony/backend_common.h +109 -0
  77. data/ext/polyphony/backend_io_uring.c +884 -0
  78. data/ext/polyphony/backend_io_uring_context.c +73 -0
  79. data/ext/polyphony/backend_io_uring_context.h +52 -0
  80. data/ext/polyphony/{libev_backend.c → backend_libev.c} +255 -345
  81. data/ext/polyphony/event.c +1 -1
  82. data/ext/polyphony/extconf.rb +31 -13
  83. data/ext/polyphony/fiber.c +111 -27
  84. data/ext/polyphony/libev.c +4 -0
  85. data/ext/polyphony/libev.h +8 -2
  86. data/ext/polyphony/liburing.c +8 -0
  87. data/ext/polyphony/playground.c +51 -0
  88. data/ext/polyphony/polyphony.c +6 -8
  89. data/ext/polyphony/polyphony.h +29 -25
  90. data/ext/polyphony/polyphony_ext.c +13 -6
  91. data/ext/polyphony/queue.c +3 -4
  92. data/ext/polyphony/ring_buffer.c +0 -1
  93. data/ext/polyphony/runqueue.c +102 -0
  94. data/ext/polyphony/runqueue_ring_buffer.c +85 -0
  95. data/ext/polyphony/runqueue_ring_buffer.h +31 -0
  96. data/ext/polyphony/thread.c +45 -92
  97. data/lib/polyphony.rb +2 -2
  98. data/lib/polyphony/adapters/fs.rb +1 -1
  99. data/lib/polyphony/adapters/process.rb +0 -3
  100. data/lib/polyphony/adapters/redis.rb +1 -1
  101. data/lib/polyphony/adapters/trace.rb +2 -2
  102. data/lib/polyphony/core/global_api.rb +9 -12
  103. data/lib/polyphony/core/sync.rb +6 -2
  104. data/lib/polyphony/extensions/core.rb +6 -24
  105. data/lib/polyphony/extensions/debug.rb +13 -0
  106. data/lib/polyphony/extensions/fiber.rb +21 -44
  107. data/lib/polyphony/extensions/io.rb +55 -10
  108. data/lib/polyphony/extensions/socket.rb +70 -12
  109. data/lib/polyphony/version.rb +1 -1
  110. data/polyphony.gemspec +3 -2
  111. data/test/helper.rb +36 -4
  112. data/test/io_uring_test.rb +55 -0
  113. data/test/stress.rb +5 -2
  114. data/test/test_backend.rb +4 -6
  115. data/test/test_ext.rb +1 -2
  116. data/test/test_fiber.rb +31 -24
  117. data/test/test_global_api.rb +58 -31
  118. data/test/test_io.rb +58 -0
  119. data/test/test_signal.rb +11 -8
  120. data/test/test_socket.rb +17 -0
  121. data/test/test_sync.rb +21 -0
  122. data/test/test_throttler.rb +3 -6
  123. data/test/test_trace.rb +7 -5
  124. metadata +86 -76
  125. data/examples/adapters/concurrent-ruby.rb +0 -9
  126. data/examples/core/04-handling-signals.rb +0 -19
  127. data/examples/core/xx-at_exit.rb +0 -29
  128. data/examples/core/xx-backend.rb +0 -102
  129. data/examples/core/xx-caller.rb +0 -12
  130. data/examples/core/xx-daemon.rb +0 -14
  131. data/examples/core/xx-deadlock.rb +0 -8
  132. data/examples/core/xx-deferring-an-operation.rb +0 -14
  133. data/examples/core/xx-exception-backtrace.rb +0 -40
  134. data/examples/core/xx-fork-cleanup.rb +0 -22
  135. data/examples/core/xx-fork-spin.rb +0 -42
  136. data/examples/core/xx-fork-terminate.rb +0 -27
  137. data/examples/core/xx-move_on.rb +0 -23
  138. data/examples/core/xx-queue-async.rb +0 -120
  139. data/examples/core/xx-readpartial.rb +0 -18
  140. data/examples/core/xx-signals.rb +0 -16
  141. data/examples/core/xx-sleep-forever.rb +0 -9
  142. data/examples/core/xx-sleeping.rb +0 -25
  143. data/examples/core/xx-snooze-starve.rb +0 -16
  144. data/examples/core/xx-spin-fork.rb +0 -49
  145. data/examples/core/xx-state-machine.rb +0 -51
  146. data/examples/core/xx-stop.rb +0 -20
  147. data/examples/core/xx-supervisors.rb +0 -21
  148. data/examples/core/xx-thread-selector-sleep.rb +0 -51
  149. data/examples/core/xx-thread-selector-snooze.rb +0 -46
  150. data/examples/core/xx-thread-snooze.rb +0 -34
  151. data/examples/core/xx-timer-gc.rb +0 -17
  152. data/examples/core/xx-trace.rb +0 -79
  153. data/examples/performance/xx-array.rb +0 -11
  154. data/examples/performance/xx-fiber-switch.rb +0 -9
  155. data/examples/performance/xx-snooze.rb +0 -15
  156. 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 LibevBackend_t {
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
- } LibevBackend_t;
48
+ } Backend_t;
21
49
 
22
- static size_t LibevBackend_size(const void *ptr) {
23
- return sizeof(LibevBackend_t);
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 LibevBackend_type = {
27
- "Libev",
28
- {0, 0, LibevBackend_size,},
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 LibevBackend_allocate(VALUE klass) {
33
- LibevBackend_t *backend = ALLOC(LibevBackend_t);
34
-
35
- return TypedData_Wrap_Struct(klass, &LibevBackend_type, backend);
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 GetLibevBackend(obj, backend) \
39
- TypedData_Get_Struct((obj), LibevBackend_t, &LibevBackend_type, (backend))
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 LibevBackend_initialize(VALUE self) {
47
- LibevBackend_t *backend;
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
- GetLibevBackend(self, backend);
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 LibevBackend_finalize(VALUE self) {
66
- LibevBackend_t *backend;
67
- GetLibevBackend(self, backend);
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 LibevBackend_post_fork(VALUE self) {
77
- LibevBackend_t *backend;
78
- GetLibevBackend(self, backend);
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 LibevBackend_ref(VALUE self) {
92
- LibevBackend_t *backend;
93
- GetLibevBackend(self, backend);
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 LibevBackend_unref(VALUE self) {
100
- LibevBackend_t *backend;
101
- GetLibevBackend(self, backend);
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 LibevBackend_ref_count(VALUE self) {
108
- LibevBackend_t *backend;
109
- GetLibevBackend(self, backend);
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 LibevBackend_reset_ref_count(VALUE self) {
115
- LibevBackend_t *backend;
116
- GetLibevBackend(self, backend);
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 LibevBackend_pending_count(VALUE self) {
149
+ VALUE Backend_pending_count(VALUE self) {
122
150
  int count;
123
- LibevBackend_t *backend;
124
- GetLibevBackend(self, backend);
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 LibevBackend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue) {
157
+ VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue) {
130
158
  int is_nowait = nowait == Qtrue;
131
- LibevBackend_t *backend;
132
- GetLibevBackend(self, backend);
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 < runnable_count || backend->run_no_wait_count < 10)
138
- return self;
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, SYM_fiber_ev_loop_enter, current_fiber);
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, SYM_fiber_ev_loop_leave, current_fiber);
176
+ COND_TRACE(2, SYM_fiber_event_poll_leave, current_fiber);
148
177
 
149
178
  return self;
150
179
  }
151
180
 
152
- VALUE LibevBackend_wakeup(VALUE self) {
153
- LibevBackend_t *backend;
154
- GetLibevBackend(self, backend);
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 LibevBackend_io_callback(EV_P_ ev_io *w, int revents)
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
- inline VALUE libev_await(LibevBackend_t *backend) {
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, LibevBackend_io_callback, fptr->fd, flags);
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 libev_snooze() {
278
- Fiber_make_runnable(rb_fiber_current(), Qnil);
279
- return Thread_switch_fiber(rb_thread_current());
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
- ID ID_ivar_is_nonblocking;
234
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fd, &watcher, events);
283
235
 
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;
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 LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
308
- LibevBackend_t *backend;
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 = rb_iv_get(io, "@io");
252
+ VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
319
253
 
320
- GetLibevBackend(self, backend);
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 = libev_io_wait(backend, &watcher, fptr, EV_READ);
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 = libev_snooze();
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 LibevBackend_read_loop(VALUE self, VALUE io) {
381
-
382
- #define PREPARE_STR() { \
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
- LibevBackend_t *backend;
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 = rb_iv_get(io, "@io");
322
+ VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
407
323
 
408
- PREPARE_STR();
324
+ READ_LOOP_PREPARE_STR();
409
325
 
410
- GetLibevBackend(self, backend);
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 = libev_io_wait(backend, &watcher, fptr, EV_READ);
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 = libev_snooze();
437
- if (TEST_EXCEPTION(switchpoint_result)) goto error;
344
+ switchpoint_result = backend_snooze();
438
345
 
439
- if (n == 0) break; // EOF
346
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
440
347
 
348
+ if (n == 0) break; // EOF
441
349
  total = n;
442
- YIELD_STR();
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 LibevBackend_write(VALUE self, VALUE io, VALUE str) {
461
- LibevBackend_t *backend;
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 = rb_iv_get(io, "@io");
373
+ underlying_io = rb_ivar_get(io, ID_ivar_io);
471
374
  if (underlying_io != Qnil) io = underlying_io;
472
- GetLibevBackend(self, backend);
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
- switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_WRITE);
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 = libev_snooze();
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 LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
505
- LibevBackend_t *backend;
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 = rb_iv_get(io, "@io");
423
+ underlying_io = rb_ivar_get(io, ID_ivar_io);
517
424
  if (underlying_io != Qnil) io = underlying_io;
518
- GetLibevBackend(self, backend);
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)) rb_syserr_fail(e, strerror(e));
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 = libev_snooze();
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 LibevBackend_write_m(int argc, VALUE *argv, VALUE self) {
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
- LibevBackend_write(self, argv[0], argv[1]) :
581
- LibevBackend_writev(self, argv[0], argc - 1, argv + 1);
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 = rb_iv_get(sock, "@io");
504
+ VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
595
505
  if (underlying_sock != Qnil) sock = underlying_sock;
596
506
 
597
- GetLibevBackend(self, backend);
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 = libev_io_wait(backend, &watcher, fptr, EV_READ);
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 = libev_snooze();
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 LibevBackend_accept_loop(VALUE self, VALUE sock) {
641
- LibevBackend_t *backend;
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 = rb_iv_get(sock, "@io");
561
+ VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
650
562
  if (underlying_sock != Qnil) sock = underlying_sock;
651
563
 
652
- GetLibevBackend(self, backend);
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 = libev_io_wait(backend, &watcher, fptr, EV_READ);
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 = libev_snooze();
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 LibevBackend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
697
- LibevBackend_t *backend;
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 = rb_iv_get(sock, "@io");
617
+ VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
704
618
  if (underlying_sock != Qnil) sock = underlying_sock;
705
619
 
706
- GetLibevBackend(self, backend);
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
- switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_WRITE);
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 = libev_snooze();
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 libev_wait_fd(LibevBackend_t *backend, int fd, int events, int raise_exception) {
733
- struct libev_io watcher;
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 = rb_iv_get(io, "@io");
653
+ VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
752
654
  if (underlying_io != Qnil) io = underlying_io;
753
- GetLibevBackend(self, backend);
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 LibevBackend_timer_callback(EV_P_ ev_timer *w, int revents)
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 LibevBackend_sleep(VALUE self, VALUE duration) {
771
- LibevBackend_t *backend;
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
- GetLibevBackend(self, backend);
677
+ GetBackend(self, backend);
776
678
  watcher.fiber = rb_fiber_current();
777
- ev_timer_init(&watcher.timer, LibevBackend_timer_callback, NUM2DBL(duration), 0.);
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 = libev_await(backend);
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 LibevBackend_child_callback(EV_P_ ev_child *w, int revents)
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 >> 8; // weird, why should we do this?
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 LibevBackend_waitpid(VALUE self, VALUE pid) {
806
- LibevBackend_t *backend;
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
- GetLibevBackend(self, backend);
710
+ GetBackend(self, backend);
810
711
 
811
712
  watcher.fiber = rb_fiber_current();
812
- ev_child_init(&watcher.child, LibevBackend_child_callback, NUM2INT(pid), 0);
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
- TEST_RESUME_EXCEPTION(switchpoint_result);
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
- struct ev_loop *LibevBackend_ev_loop(VALUE self) {
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
- void LibevBackend_async_callback(EV_P_ ev_async *w, int revents) { }
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
- GetLibevBackend(self, backend);
730
+ GetBackend(self, backend);
836
731
 
837
732
  struct ev_async async;
838
733
 
839
- ev_async_init(&async, LibevBackend_async_callback);
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
- if (RTEST(raise)) TEST_RESUME_EXCEPTION(switchpoint_result);
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
- void Init_LibevBackend() {
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, LibevBackend_allocate);
855
-
856
- rb_define_method(cBackend, "initialize", LibevBackend_initialize, 0);
857
- rb_define_method(cBackend, "finalize", LibevBackend_finalize, 0);
858
- rb_define_method(cBackend, "post_fork", LibevBackend_post_fork, 0);
859
- rb_define_method(cBackend, "pending_count", LibevBackend_pending_count, 0);
860
-
861
- rb_define_method(cBackend, "ref", LibevBackend_ref, 0);
862
- rb_define_method(cBackend, "unref", LibevBackend_unref, 0);
863
-
864
- rb_define_method(cBackend, "poll", LibevBackend_poll, 3);
865
- rb_define_method(cBackend, "break", LibevBackend_wakeup, 0);
866
-
867
- rb_define_method(cBackend, "read", LibevBackend_read, 4);
868
- rb_define_method(cBackend, "read_loop", LibevBackend_read_loop, 1);
869
- rb_define_method(cBackend, "write", LibevBackend_write_m, -1);
870
- rb_define_method(cBackend, "accept", LibevBackend_accept, 1);
871
- rb_define_method(cBackend, "accept_loop", LibevBackend_accept_loop, 1);
872
- rb_define_method(cBackend, "connect", LibevBackend_connect, 3);
873
- rb_define_method(cBackend, "wait_io", LibevBackend_wait_io, 2);
874
- rb_define_method(cBackend, "sleep", LibevBackend_sleep, 1);
875
- rb_define_method(cBackend, "waitpid", LibevBackend_waitpid, 1);
876
- rb_define_method(cBackend, "wait_event", LibevBackend_wait_event, 1);
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 = LibevBackend_pending_count;
881
- __BACKEND__.poll = LibevBackend_poll;
882
- __BACKEND__.ref = LibevBackend_ref;
883
- __BACKEND__.ref_count = LibevBackend_ref_count;
884
- __BACKEND__.reset_ref_count = LibevBackend_reset_ref_count;
885
- __BACKEND__.unref = LibevBackend_unref;
886
- __BACKEND__.wait_event = LibevBackend_wait_event;
887
- __BACKEND__.wakeup = LibevBackend_wakeup;
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