polyphony 1.4 → 1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -42,21 +42,24 @@ thread.
42
42
  #define _GNU_SOURCE 1
43
43
  #endif
44
44
 
45
- #include <netdb.h>
46
- #include <sys/socket.h>
47
- #include <sys/uio.h>
48
45
  #include <unistd.h>
49
- #include <netinet/in.h>
50
- #include <arpa/inet.h>
51
46
  #include <stdnoreturn.h>
52
47
  #include <sys/types.h>
48
+
49
+ #ifdef POLYPHONY_USE_PIDFD_OPEN
53
50
  #include <sys/wait.h>
51
+ #endif
52
+
54
53
  #include <fcntl.h>
55
54
 
56
55
  #include "polyphony.h"
57
56
  #include "../libev/ev.h"
58
57
  #include "ruby/io.h"
59
58
 
59
+ #ifndef POLYPHONY_WINDOWS
60
+ #include <sys/uio.h>
61
+ #endif
62
+
60
63
  #include "../libev/ev.h"
61
64
  #include "backend_common.h"
62
65
 
@@ -267,21 +270,25 @@ VALUE libev_wait_fd(Backend_t *backend, int fd, int events, int raise_exception)
267
270
  }
268
271
 
269
272
  static inline int fd_from_io(VALUE io, rb_io_t **fptr, int write_mode, int rectify_file_pos) {
273
+ if (TYPE(io) == T_FIXNUM) {
274
+ *fptr = NULL;
275
+ return FIX2INT(io);
276
+ }
277
+
270
278
  if (rb_obj_class(io) == cPipe) {
271
279
  *fptr = NULL;
272
280
  Pipe_verify_blocking_mode(io, Qfalse);
273
281
  return Pipe_get_fd(io, write_mode);
274
282
  }
275
- else {
276
- VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
277
- if (underlying_io != Qnil) io = underlying_io;
278
-
279
- GetOpenFile(io, *fptr);
280
- int fd = rb_io_descriptor(io);
281
- io_verify_blocking_mode(io, fd, Qfalse);
282
- if (rectify_file_pos) rectify_io_file_pos(*fptr);
283
- return fd;
284
- }
283
+
284
+ VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
285
+ if (underlying_io != Qnil) io = underlying_io;
286
+
287
+ GetOpenFile(io, *fptr);
288
+ int fd = rb_io_descriptor(io);
289
+ io_verify_blocking_mode(io, fd, Qfalse);
290
+ if (rectify_file_pos) rectify_io_file_pos(*fptr);
291
+ return fd;
285
292
  }
286
293
 
287
294
  VALUE Backend_read(VALUE self, VALUE io, VALUE buffer, VALUE length, VALUE to_eof, VALUE pos) {
@@ -558,6 +565,7 @@ error:
558
565
  return RAISE_EXCEPTION(switchpoint_result);
559
566
  }
560
567
 
568
+ #ifndef POLYPHONY_WINDOWS
561
569
  VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
562
570
  Backend_t *backend;
563
571
  struct libev_io watcher;
@@ -629,15 +637,23 @@ error:
629
637
  free(iov);
630
638
  return RAISE_EXCEPTION(switchpoint_result);
631
639
  }
640
+ #endif
632
641
 
633
642
  VALUE Backend_write_m(int argc, VALUE *argv, VALUE self) {
634
643
  if (argc < 2)
635
644
  // TODO: raise ArgumentError
636
645
  rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
637
646
 
647
+ #ifdef POLYPHONY_WINDOWS
648
+ int total = 0;
649
+ for (int i = 1; i < argc; i++)
650
+ total += FIX2INT(Backend_write(self, argv[0], argv[i]));
651
+ return INT2FIX(total);
652
+ #else
638
653
  return (argc == 2) ?
639
654
  Backend_write(self, argv[0], argv[1]) :
640
655
  Backend_writev(self, argv[0], argc - 1, argv + 1);
656
+ #endif
641
657
  }
642
658
 
643
659
  VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
@@ -1155,7 +1171,7 @@ VALUE Backend_close(VALUE self, VALUE io) {
1155
1171
  RAISE_IF_EXCEPTION(resume_value);
1156
1172
  RB_GC_GUARD(resume_value);
1157
1173
 
1158
- fptr_finalize(fptr);
1174
+ if (fptr) fptr_finalize(fptr);
1159
1175
  // fd = -1;
1160
1176
  return io;
1161
1177
  }
@@ -1317,6 +1333,7 @@ struct libev_child {
1317
1333
  VALUE fiber;
1318
1334
  };
1319
1335
 
1336
+ #ifndef POLYPHONY_WINDOWS
1320
1337
  void Backend_child_callback(EV_P_ ev_child *w, int revents) {
1321
1338
  struct libev_child *watcher = (struct libev_child *)w;
1322
1339
  int exit_status = WEXITSTATUS(w->rstatus);
@@ -1325,8 +1342,12 @@ void Backend_child_callback(EV_P_ ev_child *w, int revents) {
1325
1342
  status = rb_ary_new_from_args(2, INT2FIX(w->rpid), INT2FIX(exit_status));
1326
1343
  Fiber_make_runnable(watcher->fiber, status);
1327
1344
  }
1345
+ #endif
1328
1346
 
1329
1347
  VALUE Backend_waitpid(VALUE self, VALUE pid) {
1348
+ #ifdef POLYPHONY_WINDOWS
1349
+ rb_raise(rb_eStandardError, "Not implemented");
1350
+ #else
1330
1351
  Backend_t *backend;
1331
1352
  struct libev_child watcher;
1332
1353
  VALUE switchpoint_result = Qnil;
@@ -1344,6 +1365,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1344
1365
  RB_GC_GUARD(watcher.fiber);
1345
1366
  RB_GC_GUARD(switchpoint_result);
1346
1367
  return switchpoint_result;
1368
+ #endif
1347
1369
  }
1348
1370
  #endif
1349
1371
 
@@ -1497,6 +1519,7 @@ done:
1497
1519
  #endif
1498
1520
  }
1499
1521
 
1522
+ #ifdef POLYPHONY_LINUX
1500
1523
  VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VALUE postfix, VALUE chunk_prefix, VALUE chunk_postfix, VALUE chunk_size) {
1501
1524
  Backend_t *backend;
1502
1525
  GetBackend(self, backend);
@@ -1586,6 +1609,7 @@ error:
1586
1609
  if (pipefd[1] != -1) close(pipefd[1]);
1587
1610
  return RAISE_EXCEPTION(result);
1588
1611
  }
1612
+ #endif
1589
1613
 
1590
1614
  VALUE Backend_trace(int argc, VALUE *argv, VALUE self) {
1591
1615
  Backend_t *backend;
@@ -1651,7 +1675,10 @@ void Init_Backend(void) {
1651
1675
  rb_define_method(cBackend, "chain", Backend_chain, -1);
1652
1676
  rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
1653
1677
  rb_define_method(cBackend, "idle_proc=", Backend_idle_proc_set, 1);
1678
+
1679
+ #ifdef POLYPHONY_LINUX
1654
1680
  rb_define_method(cBackend, "splice_chunks", Backend_splice_chunks, 7);
1681
+ #endif
1655
1682
 
1656
1683
  rb_define_method(cBackend, "accept", Backend_accept, 2);
1657
1684
  rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
@@ -2,12 +2,14 @@
2
2
 
3
3
  require 'rubygems'
4
4
  require 'mkmf'
5
+ require 'rbconfig'
5
6
 
6
7
  dir_config 'polyphony_ext'
7
8
 
8
9
  KERNEL_INFO_RE = /Linux (\d)\.(\d+)(?:\.)?((?:\d+\.?)*)(?:\-)?([\w\-]+)?/
9
10
  def get_config
10
11
  config = { linux: !!(RUBY_PLATFORM =~ /linux/) }
12
+ config[:windows] = !!(RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/)
11
13
  return config if !config[:linux]
12
14
 
13
15
  kernel_info = `uname -sr`
@@ -17,7 +19,8 @@ def get_config
17
19
  version, major_revision, distribution = m[1].to_i, m[2].to_i, m[4]
18
20
 
19
21
  combined_version = version.to_i * 100 + major_revision.to_i
20
-
22
+
23
+ config[:kernel_version] = combined_version
21
24
  config[:pidfd_open] = combined_version > 503
22
25
  config[:multishot_recv] = combined_version >= 600
23
26
  config[:multishot_recvmsg] = combined_version >= 600
@@ -31,7 +34,7 @@ def get_config
31
34
  end
32
35
 
33
36
  config = get_config
34
- puts "Building Polyphony... (#{config.inspect})"
37
+ puts "Building Polyphony (\n#{config.map { |(k, v)| " #{k}: #{v}\n"}.join})"
35
38
 
36
39
  require_relative 'zlib_conf'
37
40
 
@@ -71,6 +74,7 @@ if config[:io_uring]
71
74
  else
72
75
  $defs << "-DPOLYPHONY_BACKEND_LIBEV"
73
76
  $defs << "-DPOLYPHONY_LINUX" if config[:linux]
77
+ $defs << "-DPOLYPHONY_WINDOWS" if config[:windows]
74
78
 
75
79
  $defs << "-DEV_STANDALONE" # prevent libev from assuming "config.h" exists
76
80
 
data/ext/polyphony/pipe.c CHANGED
@@ -97,8 +97,8 @@ VALUE Pipe_close(VALUE self) {
97
97
  if (pipe->w_closed)
98
98
  rb_raise(rb_eRuntimeError, "Pipe is already closed for writing");
99
99
 
100
+ Backend_close(BACKEND(), INT2FIX(pipe->fds[1]));
100
101
  pipe->w_closed = 1;
101
- close(pipe->fds[1]);
102
102
  return self;
103
103
  }
104
104
 
@@ -80,20 +80,6 @@ VALUE Polyphony_backend_accept_loop(VALUE self, VALUE server_socket, VALUE socke
80
80
  return Backend_accept_loop(BACKEND(), server_socket, socket_class);
81
81
  }
82
82
 
83
- #ifdef HAVE_IO_URING_PREP_MULTISHOT_ACCEPT
84
- /* Starts a multishot accept operation on the given server socket. This API is
85
- * available only for the io_uring backend.
86
- *
87
- * @param server_socket [Socket] socket to accept on
88
- * @return [any] block return value
89
- */
90
-
91
- VALUE Polyphony_backend_multishot_accept(VALUE self, VALUE server_socket) {
92
- return Backend_multishot_accept(BACKEND(), server_socket);
93
- }
94
- #endif
95
-
96
-
97
83
  /* Connects the given socket to the given address and port.
98
84
  *
99
85
  * @param io [Socket] socket to connect
@@ -433,12 +419,6 @@ void Init_Polyphony(void) {
433
419
  rb_define_singleton_method(mPolyphony, "backend_accept_loop", Polyphony_backend_accept_loop, 2);
434
420
  rb_define_singleton_method(mPolyphony, "backend_connect", Polyphony_backend_connect, 3);
435
421
  rb_define_singleton_method(mPolyphony, "backend_feed_loop", Polyphony_backend_feed_loop, 3);
436
-
437
- #ifdef HAVE_IO_URING_PREP_MULTISHOT_ACCEPT
438
- rb_define_singleton_method(mPolyphony, "backend_multishot_accept", Polyphony_backend_multishot_accept, 1);
439
- #endif
440
-
441
-
442
422
  rb_define_singleton_method(mPolyphony, "backend_read", Polyphony_backend_read, 5);
443
423
  rb_define_singleton_method(mPolyphony, "backend_read_loop", Polyphony_backend_read_loop, 2);
444
424
  rb_define_singleton_method(mPolyphony, "backend_recv", Polyphony_backend_recv, 4);
@@ -101,11 +101,6 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class);
101
101
  VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class);
102
102
  VALUE Backend_connect(VALUE self, VALUE io, VALUE addr, VALUE port);
103
103
  VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method);
104
-
105
- #ifdef HAVE_IO_URING_PREP_MULTISHOT_ACCEPT
106
- VALUE Backend_multishot_accept(VALUE self, VALUE io);
107
- #endif
108
-
109
104
  VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof, VALUE pos);
110
105
  VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen);
111
106
  VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos);
@@ -0,0 +1,18 @@
1
+ // source: https://stackoverflow.com/questions/57897314/fatal-error-when-compiling-include-sys-uio-h-on-project-windows
2
+
3
+ #ifndef SYS_UIO_H
4
+ #define SYS_UIO_H
5
+
6
+ #include <inttypes.h>
7
+ #include <unistd.h>
8
+
9
+ struct iovec
10
+ {
11
+ void *iov_base; /* Base address of a memory region for input or output */
12
+ size_t iov_len; /* The size of the memory pointed to by iov_base */
13
+ };
14
+
15
+ ssize_t readv(int fildes, const struct iovec *iov, int iovcnt);
16
+ ssize_t writev(int fildes, const struct iovec *iov, int iovcnt);
17
+
18
+ #endif /* SYS_UIO_H */
@@ -464,20 +464,6 @@ class ::TCPServer < ::TCPSocket
464
464
  Polyphony.backend_accept(@io, TCPSocket)
465
465
  end
466
466
 
467
- if Polyphony.instance_methods(false).include?(:backend_multishot_accept)
468
- # Starts a multishot accept operation (only available with io_uring
469
- # backend). Example usage:
470
- #
471
- # server.multishot_accept do
472
- # server.accept_loop { |c| handle_connection(c) }
473
- # end
474
- #
475
- # @return [any] return value of code block
476
- def multishot_accept(&block)
477
- Polyphony.backend_multishot_accept(@io, &block)
478
- end
479
- end
480
-
481
467
  # Accepts incoming connections in an infinite loop.
482
468
  #
483
469
  # @yield [TCPSocket] accepted socket
@@ -14,6 +14,10 @@ module ::Timeout
14
14
  # @param message [String] exception message
15
15
  # @return [any] block's return value
16
16
  def self.timeout(sec, klass = Timeout::Error, message = 'execution expired', &block)
17
- cancel_after(sec, with_exception: [klass, message], &block)
17
+ if sec.nil? || sec == 0
18
+ block.call
19
+ else
20
+ cancel_after(sec, with_exception: [klass, message], &block)
21
+ end
18
22
  end
19
23
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Polyphony
4
4
  # @!visibility private
5
- VERSION = '1.4'
5
+ VERSION = '1.5'
6
6
  end
data/test/test_backend.rb CHANGED
@@ -317,6 +317,8 @@ class BackendTest < MiniTest::Test
317
317
  end
318
318
 
319
319
  def test_splice_chunks
320
+ skip if !Thread.current.backend.respond_to?(:splice_chunks)
321
+
320
322
  body = 'abcd' * 4
321
323
  chunk_size = 12
322
324
 
@@ -346,8 +348,8 @@ class BackendTest < MiniTest::Test
346
348
  expected = "Content-Type: foo\r\n\r\n#{12.to_s(16)}\r\n#{body[0..11]}\r\n#{4.to_s(16)}\r\n#{body[12..15]}\r\n0\r\n\r\n"
347
349
  assert_equal expected, buf
348
350
  ensure
349
- o.close
350
- w.close
351
+ o&.close
352
+ w&.close
351
353
  end
352
354
 
353
355
  def test_idle_gc
data/test/test_ext.rb CHANGED
@@ -242,6 +242,20 @@ class TimeoutTest < MiniTest::Test
242
242
  assert_equal [0, 1, 2], buffer
243
243
  end
244
244
 
245
+ def test_timeout_with_nil_or_zero
246
+ v = Timeout.timeout(nil) do
247
+ sleep 0.01
248
+ :foo
249
+ end
250
+ assert_equal :foo, v
251
+
252
+ v = Timeout.timeout(0) do
253
+ sleep 0.01
254
+ :bar
255
+ end
256
+ assert_equal :bar, v
257
+ end
258
+
245
259
  class MyTimeout < Exception
246
260
  end
247
261
 
@@ -128,16 +128,16 @@ class MoveOnAfterTest < MiniTest::Test
128
128
 
129
129
  def test_move_on_after_with_reset
130
130
  t0 = monotonic_clock
131
- v = move_on_after(0.01, with_value: :moved_on) do |timeout|
132
- sleep 0.006
131
+ v = move_on_after(0.1, with_value: :moved_on) do |timeout|
132
+ sleep 0.06
133
133
  timeout.reset
134
- sleep 0.006
134
+ sleep 0.06
135
135
  nil
136
136
  end
137
137
  t1 = monotonic_clock
138
138
 
139
139
  assert_nil v
140
- assert_in_range 0.012..0.030, t1 - t0 if IS_LINUX
140
+ assert_in_range 0.12..0.30, t1 - t0 if IS_LINUX
141
141
  end
142
142
 
143
143
  def test_nested_move_on_after
data/test/test_socket.rb CHANGED
@@ -390,98 +390,3 @@ class SSLSocketTest < MiniTest::Test
390
390
  assert_kind_of OpenSSL::SSL::SSLError, errors.first
391
391
  end
392
392
  end
393
-
394
- class MultishotAcceptTest < MiniTest::Test
395
- def setup
396
- skip if !TCPServer.instance_methods(false).include?(:multishot_accept)
397
- end
398
-
399
- def start_tcp_server_on_random_port(host = '127.0.0.1')
400
- port = rand(1100..60000)
401
- server = TCPServer.new(host, port)
402
- [port, server]
403
- rescue Errno::EADDRINUSE
404
- retry
405
- end
406
-
407
- def test_multishot_accept_while_loop
408
- port, server = start_tcp_server_on_random_port
409
- server_fiber = spin do
410
- server.multishot_accept do
411
- while (socket = server.accept)
412
- spin do
413
- while (data = socket.gets(8192))
414
- socket << data
415
- end
416
- end
417
- end
418
- end
419
- end
420
-
421
- snooze
422
- client = TCPSocket.new('127.0.0.1', port)
423
- client.write("1234\n")
424
- assert_equal "1234\n", client.recv(8192)
425
- client.close
426
-
427
- client = TCPSocket.new('127.0.0.1', port)
428
- client.write("5678\n")
429
- assert_equal "5678\n", client.recv(8192)
430
- client.close
431
-
432
- ensure
433
- server_fiber&.stop
434
- server_fiber&.await
435
- server&.close
436
- end
437
-
438
- def spin_client(socket)
439
- spin do
440
- while (data = socket.gets(8192))
441
- socket << data
442
- end
443
- end
444
- end
445
-
446
- def test_multishot_accept_loop
447
- port, server = start_tcp_server_on_random_port
448
- server_fiber = spin do
449
- server.multishot_accept do
450
- server.accept_loop { |s| spin_client(s) }
451
- end
452
- end
453
-
454
- snooze
455
- client = TCPSocket.new('127.0.0.1', port)
456
- client.write("1234\n")
457
- assert_equal "1234\n", client.recv(8192)
458
- client.close
459
-
460
- client = TCPSocket.new('127.0.0.1', port)
461
- client.write("5678\n")
462
- assert_equal "5678\n", client.recv(8192)
463
- client.close
464
-
465
- ensure
466
- server_fiber&.stop
467
- server_fiber&.await
468
- server&.close
469
- end
470
-
471
- def test_multishot_accept_error
472
- port, server = start_tcp_server_on_random_port
473
- error = nil
474
- server_fiber = spin do
475
- server.multishot_accept do
476
- server.accept_loop { |s| spin_client(s) }
477
- rescue SystemCallError => e
478
- error = e
479
- end
480
- end
481
- snooze
482
- server.close
483
- snooze
484
- server_fiber.await
485
- assert_kind_of Errno::EBADF, error
486
- end
487
- end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: polyphony
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.4'
4
+ version: '1.5'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-01 00:00:00.000000000 Z
11
+ date: 2023-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -150,7 +150,7 @@ dependencies:
150
150
  - - '='
151
151
  - !ruby/object:Gem::Version
152
152
  version: 2.10.0
153
- description:
153
+ description:
154
154
  email: sharon@noteflakes.com
155
155
  executables: []
156
156
  extensions:
@@ -312,6 +312,7 @@ files:
312
312
  - examples/pipes/gzip.rb
313
313
  - examples/pipes/gzip_http_server.rb
314
314
  - examples/pipes/http_server.rb
315
+ - examples/pipes/http_server2.rb
315
316
  - examples/pipes/tcp_proxy.rb
316
317
  - examples/pipes/tee.rb
317
318
  - ext/libev/Changes
@@ -356,6 +357,7 @@ files:
356
357
  - ext/polyphony/runqueue_ring_buffer.h
357
358
  - ext/polyphony/socket_extensions.c
358
359
  - ext/polyphony/thread.c
360
+ - ext/polyphony/win_uio.h
359
361
  - ext/polyphony/zlib_conf.rb
360
362
  - ext/test_eintr.c
361
363
  - lib/polyphony.rb
@@ -658,7 +660,7 @@ metadata:
658
660
  source_code_uri: https://github.com/digital-fabric/polyphony
659
661
  documentation_uri: https://www.rubydoc.info/gems/polyphony
660
662
  changelog_uri: https://github.com/digital-fabric/polyphony/blob/master/CHANGELOG.md
661
- post_install_message:
663
+ post_install_message:
662
664
  rdoc_options:
663
665
  - "--title"
664
666
  - polyphony
@@ -678,7 +680,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
678
680
  version: '0'
679
681
  requirements: []
680
682
  rubygems_version: 3.4.1
681
- signing_key:
683
+ signing_key:
682
684
  specification_version: 4
683
685
  summary: Fine grained concurrency for Ruby
684
686
  test_files: []