polyphony 0.43.1 → 0.43.6
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 +1 -1
- data/CHANGELOG.md +37 -0
- data/Gemfile.lock +2 -2
- data/README.md +2 -1
- data/docs/_includes/head.html +40 -0
- data/docs/_includes/title.html +1 -0
- data/docs/_user-guide/web-server.md +11 -11
- data/docs/getting-started/overview.md +2 -2
- data/docs/index.md +4 -3
- data/docs/main-concepts/design-principles.md +23 -34
- data/docs/main-concepts/fiber-scheduling.md +1 -1
- data/docs/polyphony-logo.png +0 -0
- data/examples/adapters/concurrent-ruby.rb +9 -0
- data/examples/adapters/redis_blpop.rb +12 -0
- data/examples/core/xx-daemon.rb +14 -0
- data/examples/io/xx-happy-eyeballs.rb +21 -22
- data/examples/io/xx-zip.rb +19 -0
- data/examples/performance/mem-usage.rb +34 -28
- data/examples/performance/messaging.rb +29 -0
- data/examples/performance/multi_snooze.rb +11 -9
- data/examples/xx-spin.rb +32 -0
- data/ext/polyphony/libev_agent.c +242 -145
- data/ext/polyphony/libev_queue.c +129 -57
- data/ext/polyphony/polyphony.h +12 -5
- data/ext/polyphony/ring_buffer.c +120 -0
- data/ext/polyphony/ring_buffer.h +28 -0
- data/ext/polyphony/thread.c +13 -7
- data/lib/polyphony.rb +29 -10
- data/lib/polyphony/adapters/redis.rb +3 -2
- data/lib/polyphony/core/global_api.rb +5 -3
- data/lib/polyphony/core/resource_pool.rb +19 -9
- data/lib/polyphony/core/thread_pool.rb +1 -1
- data/lib/polyphony/extensions/core.rb +40 -0
- data/lib/polyphony/extensions/fiber.rb +9 -14
- data/lib/polyphony/extensions/io.rb +17 -16
- data/lib/polyphony/extensions/openssl.rb +8 -0
- data/lib/polyphony/extensions/socket.rb +12 -0
- data/lib/polyphony/version.rb +1 -1
- data/test/q.rb +24 -0
- data/test/test_agent.rb +13 -7
- data/test/test_fiber.rb +3 -3
- data/test/test_global_api.rb +50 -17
- data/test/test_io.rb +10 -2
- data/test/test_queue.rb +26 -1
- data/test/test_resource_pool.rb +12 -0
- data/test/test_socket.rb +43 -0
- data/test/test_throttler.rb +6 -5
- metadata +13 -2
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
require 'zlib'
|
6
|
+
|
7
|
+
i, o = IO.pipe
|
8
|
+
|
9
|
+
w = Zlib::GzipWriter.new(o)
|
10
|
+
|
11
|
+
s = (1..1000).map { (65 + rand(26)).chr }.join
|
12
|
+
puts "full length: #{s.bytesize}"
|
13
|
+
w << s
|
14
|
+
w.close
|
15
|
+
o.close
|
16
|
+
|
17
|
+
|
18
|
+
z = i.read
|
19
|
+
puts "zipped length: #{z.bytesize}"
|
@@ -4,47 +4,53 @@ def mem_usage
|
|
4
4
|
`ps -o rss #{$$}`.split.last.to_i
|
5
5
|
end
|
6
6
|
|
7
|
-
def
|
7
|
+
def calculate_memory_cost(name, count, &block)
|
8
|
+
GC.enable
|
9
|
+
ObjectSpace.garbage_collect
|
10
|
+
sleep 0.5
|
8
11
|
GC.disable
|
9
12
|
rss0 = mem_usage
|
10
|
-
|
13
|
+
count0 = ObjectSpace.count_objects[:TOTAL] - ObjectSpace.count_objects[:FREE]
|
14
|
+
a = []
|
15
|
+
count.times { a << block.call }
|
11
16
|
rss1 = mem_usage
|
12
|
-
|
17
|
+
count1 = ObjectSpace.count_objects[:TOTAL] - ObjectSpace.count_objects[:FREE]
|
18
|
+
p [count0, count1]
|
19
|
+
# sleep 0.5
|
13
20
|
cost = (rss1 - rss0).to_f / count
|
21
|
+
count_delta = (count1 - count0) / count
|
14
22
|
|
15
|
-
puts "
|
23
|
+
puts "#{name} rss cost: #{cost}KB object count: #{count_delta}"
|
16
24
|
end
|
17
25
|
|
18
|
-
|
19
|
-
|
20
|
-
def calculate_thread_memory_cost(count)
|
21
|
-
GC.disable
|
22
|
-
rss0 = mem_usage
|
23
|
-
count.times { Thread.new { sleep 1 } }
|
24
|
-
sleep 0.5
|
25
|
-
rss1 = mem_usage
|
26
|
-
sleep 0.5
|
27
|
-
GC.start
|
28
|
-
cost = (rss1 - rss0).to_f / count
|
26
|
+
f = Fiber.new { |f| f.transfer }
|
27
|
+
f.transfer Fiber.current
|
29
28
|
|
30
|
-
|
29
|
+
calculate_memory_cost('fiber', 10000) do
|
30
|
+
f = Fiber.new { |f| f.transfer :foo }
|
31
|
+
f.transfer Fiber.current
|
32
|
+
f
|
31
33
|
end
|
32
34
|
|
33
|
-
|
35
|
+
t = Thread.new { sleep 1}
|
36
|
+
t.kill
|
37
|
+
t.join
|
38
|
+
|
39
|
+
calculate_memory_cost('thread', 500) do
|
40
|
+
t = Thread.new { sleep 1 }
|
41
|
+
sleep 0.001
|
42
|
+
t
|
43
|
+
end
|
44
|
+
(Thread.list - [Thread.current]).each(&:kill).each(&:join)
|
34
45
|
|
35
46
|
require 'bundler/setup'
|
36
47
|
require 'polyphony'
|
37
48
|
|
38
|
-
|
39
|
-
|
40
|
-
rss0 = mem_usage
|
41
|
-
count.times { spin { :foo } }
|
42
|
-
snooze
|
43
|
-
rss1 = mem_usage
|
44
|
-
GC.start
|
45
|
-
cost = (rss1 - rss0).to_f / count
|
49
|
+
f = spin { sleep 0.1 }
|
50
|
+
f.await
|
46
51
|
|
47
|
-
|
52
|
+
calculate_memory_cost('polyphony fiber', 10000) do
|
53
|
+
f = spin { :foo }
|
54
|
+
f.await
|
55
|
+
f
|
48
56
|
end
|
49
|
-
|
50
|
-
calculate_extended_fiber_memory_cost(10000)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
X = 1_000_000
|
7
|
+
|
8
|
+
GC.disable
|
9
|
+
|
10
|
+
count = 0
|
11
|
+
|
12
|
+
pong = spin_loop do
|
13
|
+
msg, ping = receive
|
14
|
+
count += 1
|
15
|
+
ping << 'pong'
|
16
|
+
end
|
17
|
+
|
18
|
+
ping = spin do
|
19
|
+
X.times do
|
20
|
+
pong << ['ping', Fiber.current]
|
21
|
+
msg = receive
|
22
|
+
count += 1
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
t0 = Time.now
|
27
|
+
ping.await
|
28
|
+
dt = Time.now - t0
|
29
|
+
puts format('message rate: %d/s', (X / dt))
|
@@ -5,19 +5,20 @@ require 'polyphony'
|
|
5
5
|
|
6
6
|
def bm(fibers, iterations)
|
7
7
|
count = 0
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
count += 1
|
15
|
-
end
|
8
|
+
t_pre = Time.now
|
9
|
+
fibers.times do
|
10
|
+
spin do
|
11
|
+
iterations.times do
|
12
|
+
snooze
|
13
|
+
count += 1
|
16
14
|
end
|
17
15
|
end
|
18
16
|
end
|
17
|
+
t0 = Time.now
|
18
|
+
Fiber.current.await_all_children
|
19
19
|
dt = Time.now - t0
|
20
|
-
puts "#{[fibers, iterations].inspect} count: #{count} #{count / dt.to_f}/s"
|
20
|
+
puts "#{[fibers, iterations].inspect} setup: #{t0 - t_pre}s count: #{count} #{count / dt.to_f}/s"
|
21
|
+
Thread.current.run_queue_trace
|
21
22
|
end
|
22
23
|
|
23
24
|
GC.disable
|
@@ -27,5 +28,6 @@ bm(10, 100_000)
|
|
27
28
|
bm(100, 10_000)
|
28
29
|
bm(1_000, 1_000)
|
29
30
|
bm(10_000, 100)
|
31
|
+
|
30
32
|
# bm(100_000, 10)
|
31
33
|
# bm(1_000_000, 1)
|
data/examples/xx-spin.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
puts "pid: #{Process.pid}"
|
7
|
+
GC.disable
|
8
|
+
|
9
|
+
def mem_usage
|
10
|
+
# orig_backtick('ps -o rss #{$$}').split.last.to_i
|
11
|
+
`ps -o rss #{$$}`.split.last.to_i
|
12
|
+
end
|
13
|
+
|
14
|
+
f = File.open('spin.log', 'w+')
|
15
|
+
|
16
|
+
m0 = mem_usage
|
17
|
+
|
18
|
+
X = ARGV[0] ? ARGV[0].to_i : 10
|
19
|
+
STDOUT.orig_write "Starting #{X} fibers...\n"
|
20
|
+
t0 = Time.now
|
21
|
+
x = nil
|
22
|
+
X.times do |i|
|
23
|
+
spin { p i; suspend }
|
24
|
+
end
|
25
|
+
|
26
|
+
suspend
|
27
|
+
f.close
|
28
|
+
t1 = Time.now
|
29
|
+
m1 = mem_usage
|
30
|
+
rate = X / (t1 - t0)
|
31
|
+
mem_cost = (m1 - m0) / X.to_f
|
32
|
+
STDOUT.orig_write("#{ { time: t1 - t0, spin_rate: rate, fiber_mem_cost: mem_cost }.inspect }\n")
|
data/ext/polyphony/libev_agent.c
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
#include <netdb.h>
|
2
|
+
#include <sys/socket.h>
|
3
|
+
#include <sys/uio.h>
|
4
|
+
|
1
5
|
#include "polyphony.h"
|
2
6
|
#include "../libev/ev.h"
|
3
|
-
#include <sys/socket.h>
|
4
7
|
|
5
8
|
VALUE cLibevAgent = Qnil;
|
6
9
|
VALUE cTCPSocket;
|
@@ -128,7 +131,7 @@ VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue
|
|
128
131
|
GetLibevAgent(self, agent);
|
129
132
|
|
130
133
|
if (is_nowait) {
|
131
|
-
|
134
|
+
long runnable_count = LibevQueue_len(queue);
|
132
135
|
agent->run_no_wait_count++;
|
133
136
|
if (agent->run_no_wait_count < runnable_count || agent->run_no_wait_count < 10)
|
134
137
|
return self;
|
@@ -234,7 +237,7 @@ struct libev_io {
|
|
234
237
|
VALUE fiber;
|
235
238
|
};
|
236
239
|
|
237
|
-
|
240
|
+
void LibevAgent_io_callback(EV_P_ ev_io *w, int revents)
|
238
241
|
{
|
239
242
|
struct libev_io *watcher = (struct libev_io *)w;
|
240
243
|
Fiber_make_runnable(watcher->fiber, Qnil);
|
@@ -255,11 +258,32 @@ VALUE libev_agent_await(VALUE self) {
|
|
255
258
|
return libev_await(agent);
|
256
259
|
}
|
257
260
|
|
261
|
+
VALUE libev_io_wait(struct LibevAgent_t *agent, struct libev_io *watcher, rb_io_t *fptr, int flags) {
|
262
|
+
VALUE switchpoint_result;
|
263
|
+
|
264
|
+
if (watcher->fiber == Qnil) {
|
265
|
+
watcher->fiber = rb_fiber_current();
|
266
|
+
ev_io_init(&watcher->io, LibevAgent_io_callback, fptr->fd, flags);
|
267
|
+
}
|
268
|
+
ev_io_start(agent->ev_loop, &watcher->io);
|
269
|
+
switchpoint_result = libev_await(agent);
|
270
|
+
ev_io_stop(agent->ev_loop, &watcher->io);
|
271
|
+
|
272
|
+
RB_GC_GUARD(switchpoint_result);
|
273
|
+
return switchpoint_result;
|
274
|
+
}
|
275
|
+
|
276
|
+
VALUE libev_snooze() {
|
277
|
+
Fiber_make_runnable(rb_fiber_current(), Qnil);
|
278
|
+
return Thread_switch_fiber(rb_thread_current());
|
279
|
+
}
|
280
|
+
|
258
281
|
VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
|
259
282
|
struct LibevAgent_t *agent;
|
260
283
|
struct libev_io watcher;
|
261
284
|
rb_io_t *fptr;
|
262
|
-
|
285
|
+
long dynamic_len = length == Qnil;
|
286
|
+
long len = dynamic_len ? 4096 : NUM2INT(length);
|
263
287
|
int shrinkable = io_setstrbuf(&str, len);
|
264
288
|
char *buf = RSTRING_PTR(str);
|
265
289
|
long total = 0;
|
@@ -276,44 +300,35 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
276
300
|
|
277
301
|
OBJ_TAINT(str);
|
278
302
|
|
279
|
-
while (
|
280
|
-
|
281
|
-
if (n
|
282
|
-
break;
|
283
|
-
if (n > 0) {
|
284
|
-
total = total + n;
|
285
|
-
buf += n;
|
286
|
-
len -= n;
|
287
|
-
if (!read_to_eof || (len == 0)) break;
|
288
|
-
}
|
289
|
-
else {
|
303
|
+
while (1) {
|
304
|
+
ssize_t n = read(fptr->fd, buf, len - total);
|
305
|
+
if (n < 0) {
|
290
306
|
int e = errno;
|
291
|
-
if (
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
}
|
296
|
-
ev_io_start(agent->ev_loop, &watcher.io);
|
297
|
-
switchpoint_result = libev_await(agent);
|
298
|
-
ev_io_stop(agent->ev_loop, &watcher.io);
|
299
|
-
if (TEST_EXCEPTION(switchpoint_result)) {
|
300
|
-
goto error;
|
301
|
-
}
|
302
|
-
}
|
303
|
-
else
|
304
|
-
rb_syserr_fail(e, strerror(e));
|
305
|
-
// rb_syserr_fail_path(e, fptr->pathv);
|
307
|
+
if (e != EWOULDBLOCK && e != EAGAIN) rb_syserr_fail(e, strerror(e));
|
308
|
+
|
309
|
+
switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
|
310
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
306
311
|
}
|
307
|
-
|
312
|
+
else {
|
313
|
+
switchpoint_result = libev_snooze();
|
314
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
308
315
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
316
|
+
if (n == 0) break; // EOF
|
317
|
+
total = total + n;
|
318
|
+
if (!read_to_eof) break;
|
319
|
+
|
320
|
+
if (total == len) {
|
321
|
+
if (!dynamic_len) break;
|
322
|
+
|
323
|
+
rb_str_resize(str, total);
|
324
|
+
rb_str_modify_expand(str, len);
|
325
|
+
buf = RSTRING_PTR(str) + total;
|
326
|
+
shrinkable = 0;
|
327
|
+
len += len;
|
328
|
+
}
|
329
|
+
else buf += n;
|
314
330
|
}
|
315
331
|
}
|
316
|
-
|
317
332
|
if (total == 0) return Qnil;
|
318
333
|
|
319
334
|
io_set_read_length(str, total, shrinkable);
|
@@ -347,8 +362,8 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
347
362
|
struct libev_io watcher;
|
348
363
|
rb_io_t *fptr;
|
349
364
|
VALUE str;
|
350
|
-
|
351
|
-
|
365
|
+
long total;
|
366
|
+
long len = 8192;
|
352
367
|
int shrinkable;
|
353
368
|
char *buf;
|
354
369
|
VALUE switchpoint_result = Qnil;
|
@@ -366,10 +381,20 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
366
381
|
OBJ_TAINT(str);
|
367
382
|
|
368
383
|
while (1) {
|
369
|
-
|
370
|
-
if (n
|
371
|
-
|
372
|
-
|
384
|
+
ssize_t n = read(fptr->fd, buf, len);
|
385
|
+
if (n < 0) {
|
386
|
+
int e = errno;
|
387
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
388
|
+
|
389
|
+
switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
|
390
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
391
|
+
}
|
392
|
+
else {
|
393
|
+
switchpoint_result = libev_snooze();
|
394
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
395
|
+
|
396
|
+
if (n == 0) break; // EOF
|
397
|
+
|
373
398
|
total = n;
|
374
399
|
YIELD_STR();
|
375
400
|
Fiber_make_runnable(rb_fiber_current(), Qnil);
|
@@ -378,24 +403,6 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
378
403
|
goto error;
|
379
404
|
}
|
380
405
|
}
|
381
|
-
else {
|
382
|
-
int e = errno;
|
383
|
-
if ((e == EWOULDBLOCK || e == EAGAIN)) {
|
384
|
-
if (watcher.fiber == Qnil) {
|
385
|
-
watcher.fiber = rb_fiber_current();
|
386
|
-
ev_io_init(&watcher.io, LibevAgent_io_callback, fptr->fd, EV_READ);
|
387
|
-
}
|
388
|
-
ev_io_start(agent->ev_loop, &watcher.io);
|
389
|
-
switchpoint_result = libev_await(agent);
|
390
|
-
ev_io_stop(agent->ev_loop, &watcher.io);
|
391
|
-
if (TEST_EXCEPTION(switchpoint_result)) {
|
392
|
-
goto error;
|
393
|
-
}
|
394
|
-
}
|
395
|
-
else
|
396
|
-
rb_syserr_fail(e, strerror(e));
|
397
|
-
// rb_syserr_fail_path(e, fptr->pathv);
|
398
|
-
}
|
399
406
|
}
|
400
407
|
|
401
408
|
RB_GC_GUARD(str);
|
@@ -412,12 +419,12 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
|
412
419
|
struct libev_io watcher;
|
413
420
|
rb_io_t *fptr;
|
414
421
|
VALUE switchpoint_result = Qnil;
|
415
|
-
|
422
|
+
VALUE underlying_io;
|
416
423
|
char *buf = StringValuePtr(str);
|
417
|
-
|
418
|
-
|
424
|
+
long len = RSTRING_LEN(str);
|
425
|
+
long left = len;
|
419
426
|
|
420
|
-
|
427
|
+
underlying_io = rb_iv_get(io, "@io");
|
421
428
|
if (underlying_io != Qnil) io = underlying_io;
|
422
429
|
GetLibevAgent(self, agent);
|
423
430
|
io = rb_io_get_write_io(io);
|
@@ -425,38 +432,22 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
|
425
432
|
watcher.fiber = Qnil;
|
426
433
|
|
427
434
|
while (left > 0) {
|
428
|
-
|
429
|
-
if (
|
435
|
+
ssize_t n = write(fptr->fd, buf, left);
|
436
|
+
if (n < 0) {
|
430
437
|
int e = errno;
|
431
|
-
if (e
|
432
|
-
|
433
|
-
|
434
|
-
ev_io_init(&watcher.io, LibevAgent_io_callback, fptr->fd, EV_WRITE);
|
435
|
-
}
|
436
|
-
ev_io_start(agent->ev_loop, &watcher.io);
|
437
|
-
switchpoint_result = libev_await(agent);
|
438
|
-
ev_io_stop(agent->ev_loop, &watcher.io);
|
439
|
-
if (TEST_EXCEPTION(switchpoint_result))
|
440
|
-
goto error;
|
441
|
-
}
|
442
|
-
else {
|
443
|
-
rb_syserr_fail(e, strerror(e));
|
444
|
-
// rb_syserr_fail_path(e, fptr->pathv);
|
445
|
-
|
446
|
-
}
|
438
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
439
|
+
switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
|
440
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
447
441
|
}
|
448
442
|
else {
|
449
|
-
buf +=
|
450
|
-
left -=
|
443
|
+
buf += n;
|
444
|
+
left -= n;
|
451
445
|
}
|
452
446
|
}
|
453
447
|
|
454
448
|
if (watcher.fiber == Qnil) {
|
455
|
-
|
456
|
-
|
457
|
-
if (TEST_EXCEPTION(switchpoint_result)) {
|
458
|
-
goto error;
|
459
|
-
}
|
449
|
+
switchpoint_result = libev_snooze();
|
450
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
460
451
|
}
|
461
452
|
|
462
453
|
RB_GC_GUARD(watcher.fiber);
|
@@ -467,6 +458,86 @@ error:
|
|
467
458
|
return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
468
459
|
}
|
469
460
|
|
461
|
+
VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
462
|
+
struct LibevAgent_t *agent;
|
463
|
+
struct libev_io watcher;
|
464
|
+
rb_io_t *fptr;
|
465
|
+
VALUE switchpoint_result = Qnil;
|
466
|
+
VALUE underlying_io;
|
467
|
+
long total_length = 0;
|
468
|
+
long total_written = 0;
|
469
|
+
struct iovec *iov = 0;
|
470
|
+
struct iovec *iov_ptr = 0;
|
471
|
+
int iov_count = argc;
|
472
|
+
|
473
|
+
underlying_io = rb_iv_get(io, "@io");
|
474
|
+
if (underlying_io != Qnil) io = underlying_io;
|
475
|
+
GetLibevAgent(self, agent);
|
476
|
+
io = rb_io_get_write_io(io);
|
477
|
+
GetOpenFile(io, fptr);
|
478
|
+
watcher.fiber = Qnil;
|
479
|
+
|
480
|
+
iov = malloc(iov_count * sizeof(struct iovec));
|
481
|
+
for (int i = 0; i < argc; i++) {
|
482
|
+
VALUE str = argv[i];
|
483
|
+
iov[i].iov_base = StringValuePtr(str);
|
484
|
+
iov[i].iov_len = RSTRING_LEN(str);
|
485
|
+
total_length += iov[i].iov_len;
|
486
|
+
}
|
487
|
+
iov_ptr = iov;
|
488
|
+
|
489
|
+
while (1) {
|
490
|
+
ssize_t n = writev(fptr->fd, iov_ptr, iov_count);
|
491
|
+
if (n < 0) {
|
492
|
+
int e = errno;
|
493
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
494
|
+
|
495
|
+
switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
|
496
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
497
|
+
}
|
498
|
+
else {
|
499
|
+
total_written += n;
|
500
|
+
if (total_written == total_length) break;
|
501
|
+
|
502
|
+
while (n > 0) {
|
503
|
+
if ((size_t) n < iov_ptr[0].iov_len) {
|
504
|
+
iov_ptr[0].iov_base = (char *) iov_ptr[0].iov_base + n;
|
505
|
+
iov_ptr[0].iov_len -= n;
|
506
|
+
n = 0;
|
507
|
+
}
|
508
|
+
else {
|
509
|
+
n -= iov_ptr[0].iov_len;
|
510
|
+
iov_ptr += 1;
|
511
|
+
iov_count -= 1;
|
512
|
+
}
|
513
|
+
}
|
514
|
+
}
|
515
|
+
}
|
516
|
+
if (watcher.fiber == Qnil) {
|
517
|
+
switchpoint_result = libev_snooze();
|
518
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
519
|
+
}
|
520
|
+
|
521
|
+
RB_GC_GUARD(watcher.fiber);
|
522
|
+
RB_GC_GUARD(switchpoint_result);
|
523
|
+
|
524
|
+
free(iov);
|
525
|
+
return INT2NUM(total_written);
|
526
|
+
error:
|
527
|
+
free(iov);
|
528
|
+
return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
529
|
+
}
|
530
|
+
|
531
|
+
VALUE LibevAgent_write_m(int argc, VALUE *argv, VALUE self) {
|
532
|
+
if (argc < 2)
|
533
|
+
// TODO: raise ArgumentError
|
534
|
+
rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
|
535
|
+
|
536
|
+
return (argc == 2) ?
|
537
|
+
LibevAgent_write(self, argv[0], argv[1]) :
|
538
|
+
LibevAgent_writev(self, argv[0], argc - 1, argv + 1);
|
539
|
+
}
|
540
|
+
|
470
541
|
///////////////////////////////////////////////////////////////////////////
|
471
542
|
|
472
543
|
VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
@@ -488,50 +559,39 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
488
559
|
fd = accept(fptr->fd, &addr, &len);
|
489
560
|
if (fd < 0) {
|
490
561
|
int e = errno;
|
491
|
-
if (e
|
492
|
-
if (watcher.fiber == Qnil) {
|
493
|
-
watcher.fiber = rb_fiber_current();
|
494
|
-
ev_io_init(&watcher.io, LibevAgent_io_callback, fptr->fd, EV_READ);
|
495
|
-
}
|
496
|
-
ev_io_start(agent->ev_loop, &watcher.io);
|
497
|
-
switchpoint_result = libev_await(agent);
|
498
|
-
ev_io_stop(agent->ev_loop, &watcher.io);
|
562
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
499
563
|
|
500
|
-
|
501
|
-
|
502
|
-
RB_GC_GUARD(switchpoint_result);
|
503
|
-
}
|
504
|
-
else
|
505
|
-
rb_syserr_fail(e, strerror(e));
|
506
|
-
// rb_syserr_fail_path(e, fptr->pathv);
|
564
|
+
switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
|
565
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
507
566
|
}
|
508
567
|
else {
|
509
|
-
VALUE
|
568
|
+
VALUE socket;
|
510
569
|
rb_io_t *fp;
|
511
|
-
|
570
|
+
switchpoint_result = libev_snooze();
|
571
|
+
if (TEST_EXCEPTION(switchpoint_result)) {
|
572
|
+
close(fd); // close fd since we're raising an exception
|
573
|
+
goto error;
|
574
|
+
}
|
575
|
+
|
576
|
+
socket = rb_obj_alloc(cTCPSocket);
|
577
|
+
MakeOpenFile(socket, fp);
|
512
578
|
rb_update_max_fd(fd);
|
513
579
|
fp->fd = fd;
|
514
580
|
fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
|
515
|
-
rb_io_ascii8bit_binmode(
|
581
|
+
rb_io_ascii8bit_binmode(socket);
|
516
582
|
rb_io_set_nonblock(fp);
|
517
583
|
rb_io_synchronized(fp);
|
518
584
|
|
519
585
|
// if (rsock_do_not_reverse_lookup) {
|
520
586
|
// fp->mode |= FMODE_NOREVLOOKUP;
|
521
587
|
// }
|
522
|
-
|
523
|
-
if (watcher.fiber == Qnil) {
|
524
|
-
Fiber_make_runnable(rb_fiber_current(), Qnil);
|
525
|
-
switchpoint_result = Thread_switch_fiber(rb_thread_current());
|
526
|
-
if (TEST_EXCEPTION(switchpoint_result)) {
|
527
|
-
return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
528
|
-
}
|
529
|
-
}
|
530
|
-
|
531
|
-
return connection;
|
588
|
+
return socket;
|
532
589
|
}
|
533
590
|
}
|
591
|
+
RB_GC_GUARD(switchpoint_result);
|
534
592
|
return Qnil;
|
593
|
+
error:
|
594
|
+
return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
535
595
|
}
|
536
596
|
|
537
597
|
VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
@@ -542,7 +602,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
542
602
|
struct sockaddr addr;
|
543
603
|
socklen_t len = (socklen_t)sizeof addr;
|
544
604
|
VALUE switchpoint_result = Qnil;
|
545
|
-
VALUE
|
605
|
+
VALUE socket = Qnil;
|
546
606
|
VALUE underlying_sock = rb_iv_get(sock, "@io");
|
547
607
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
548
608
|
|
@@ -555,46 +615,82 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
555
615
|
fd = accept(fptr->fd, &addr, &len);
|
556
616
|
if (fd < 0) {
|
557
617
|
int e = errno;
|
558
|
-
if (e
|
559
|
-
if (watcher.fiber == Qnil) {
|
560
|
-
watcher.fiber = rb_fiber_current();
|
561
|
-
ev_io_init(&watcher.io, LibevAgent_io_callback, fptr->fd, EV_READ);
|
562
|
-
}
|
563
|
-
ev_io_start(agent->ev_loop, &watcher.io);
|
564
|
-
switchpoint_result = libev_await(agent);
|
565
|
-
ev_io_stop(agent->ev_loop, &watcher.io);
|
618
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
566
619
|
|
567
|
-
|
568
|
-
|
569
|
-
else
|
570
|
-
rb_syserr_fail(e, strerror(e));
|
571
|
-
// rb_syserr_fail_path(e, fptr->pathv);
|
620
|
+
switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
|
621
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
572
622
|
}
|
573
623
|
else {
|
574
624
|
rb_io_t *fp;
|
575
|
-
|
576
|
-
|
625
|
+
switchpoint_result = libev_snooze();
|
626
|
+
if (TEST_EXCEPTION(switchpoint_result)) {
|
627
|
+
close(fd); // close fd since we're raising an exception
|
628
|
+
goto error;
|
629
|
+
}
|
630
|
+
|
631
|
+
socket = rb_obj_alloc(cTCPSocket);
|
632
|
+
MakeOpenFile(socket, fp);
|
577
633
|
rb_update_max_fd(fd);
|
578
634
|
fp->fd = fd;
|
579
635
|
fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
|
580
|
-
rb_io_ascii8bit_binmode(
|
636
|
+
rb_io_ascii8bit_binmode(socket);
|
581
637
|
rb_io_set_nonblock(fp);
|
582
638
|
rb_io_synchronized(fp);
|
583
639
|
|
584
|
-
rb_yield(
|
585
|
-
|
586
|
-
|
587
|
-
Fiber_make_runnable(rb_fiber_current(), Qnil);
|
588
|
-
switchpoint_result = Thread_switch_fiber(rb_thread_current());
|
589
|
-
TEST_RESUME_EXCEPTION(switchpoint_result);
|
640
|
+
rb_yield(socket);
|
641
|
+
socket = Qnil;
|
590
642
|
}
|
591
643
|
}
|
592
644
|
|
593
|
-
RB_GC_GUARD(
|
645
|
+
RB_GC_GUARD(socket);
|
594
646
|
RB_GC_GUARD(watcher.fiber);
|
595
647
|
RB_GC_GUARD(switchpoint_result);
|
648
|
+
return Qnil;
|
649
|
+
error:
|
650
|
+
return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
596
651
|
}
|
597
652
|
|
653
|
+
// VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
654
|
+
// struct LibevAgent_t *agent;
|
655
|
+
// struct libev_io watcher;
|
656
|
+
// rb_io_t *fptr;
|
657
|
+
// struct sockaddr_in addr;
|
658
|
+
// char *host_buf = StringValueCStr(host);
|
659
|
+
// VALUE switchpoint_result = Qnil;
|
660
|
+
// VALUE underlying_sock = rb_iv_get(sock, "@io");
|
661
|
+
// if (underlying_sock != Qnil) sock = underlying_sock;
|
662
|
+
|
663
|
+
// GetLibevAgent(self, agent);
|
664
|
+
// GetOpenFile(sock, fptr);
|
665
|
+
// rb_io_set_nonblock(fptr);
|
666
|
+
// watcher.fiber = Qnil;
|
667
|
+
|
668
|
+
// addr.sin_family = AF_INET;
|
669
|
+
// addr.sin_addr.s_addr = inet_addr(host_buf);
|
670
|
+
// addr.sin_port = htons(NUM2INT(port));
|
671
|
+
|
672
|
+
// while (1) {
|
673
|
+
// int result = connect(fptr->fd, &addr, sizeof(addr));
|
674
|
+
// if (result < 0) {
|
675
|
+
// int e = errno;
|
676
|
+
// if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
677
|
+
|
678
|
+
// switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
|
679
|
+
// if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
680
|
+
// }
|
681
|
+
// else {
|
682
|
+
// switchpoint_result = libev_snooze();
|
683
|
+
// if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
684
|
+
|
685
|
+
// return sock;
|
686
|
+
// }
|
687
|
+
// }
|
688
|
+
// RB_GC_GUARD(switchpoint_result);
|
689
|
+
// return Qnil;
|
690
|
+
// error:
|
691
|
+
// return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
692
|
+
// }
|
693
|
+
|
598
694
|
VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write) {
|
599
695
|
struct LibevAgent_t *agent;
|
600
696
|
struct libev_io watcher;
|
@@ -624,7 +720,7 @@ struct libev_timer {
|
|
624
720
|
VALUE fiber;
|
625
721
|
};
|
626
722
|
|
627
|
-
|
723
|
+
void LibevAgent_timer_callback(EV_P_ ev_timer *w, int revents)
|
628
724
|
{
|
629
725
|
struct libev_timer *watcher = (struct libev_timer *)w;
|
630
726
|
Fiber_make_runnable(watcher->fiber, Qnil);
|
@@ -654,7 +750,7 @@ struct libev_child {
|
|
654
750
|
VALUE fiber;
|
655
751
|
};
|
656
752
|
|
657
|
-
|
753
|
+
void LibevAgent_child_callback(EV_P_ ev_child *w, int revents)
|
658
754
|
{
|
659
755
|
struct libev_child *watcher = (struct libev_child *)w;
|
660
756
|
int exit_status = w->rstatus >> 8; // weird, why should we do this?
|
@@ -709,9 +805,10 @@ void Init_LibevAgent() {
|
|
709
805
|
|
710
806
|
rb_define_method(cLibevAgent, "read", LibevAgent_read, 4);
|
711
807
|
rb_define_method(cLibevAgent, "read_loop", LibevAgent_read_loop, 1);
|
712
|
-
rb_define_method(cLibevAgent, "write",
|
808
|
+
rb_define_method(cLibevAgent, "write", LibevAgent_write_m, -1);
|
713
809
|
rb_define_method(cLibevAgent, "accept", LibevAgent_accept, 1);
|
714
810
|
rb_define_method(cLibevAgent, "accept_loop", LibevAgent_accept_loop, 1);
|
811
|
+
// rb_define_method(cLibevAgent, "connect", LibevAgent_accept, 3);
|
715
812
|
rb_define_method(cLibevAgent, "wait_io", LibevAgent_wait_io, 2);
|
716
813
|
rb_define_method(cLibevAgent, "sleep", LibevAgent_sleep, 1);
|
717
814
|
rb_define_method(cLibevAgent, "waitpid", LibevAgent_waitpid, 1);
|