polyphony 0.99 → 0.99.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -1
  3. data/.rubocop.yml +3 -3
  4. data/.yardopts +30 -0
  5. data/CHANGELOG.md +4 -0
  6. data/LICENSE +1 -1
  7. data/README.md +63 -29
  8. data/Rakefile +1 -5
  9. data/TODO.md +0 -4
  10. data/docs/{main-concepts/concurrency.md → concurrency.md} +2 -9
  11. data/docs/{main-concepts/design-principles.md → design-principles.md} +3 -9
  12. data/docs/{main-concepts/exception-handling.md → exception-handling.md} +2 -9
  13. data/docs/{main-concepts/extending.md → extending.md} +2 -9
  14. data/docs/faq.md +3 -16
  15. data/docs/{main-concepts/fiber-scheduling.md → fiber-scheduling.md} +1 -9
  16. data/docs/link_rewriter.rb +16 -0
  17. data/docs/{getting-started/overview.md → overview.md} +1 -30
  18. data/docs/{getting-started/tutorial.md → tutorial.md} +3 -28
  19. data/docs/{_posts/2020-07-26-polyphony-0.44.md → whats-new.md} +3 -1
  20. data/examples/adapters/redis_client.rb +3 -2
  21. data/examples/io/echo_server.rb +1 -1
  22. data/examples/io/echo_server_plain_ruby.rb +26 -0
  23. data/ext/polyphony/backend_io_uring.c +154 -9
  24. data/ext/polyphony/backend_io_uring_context.c +21 -12
  25. data/ext/polyphony/backend_io_uring_context.h +12 -7
  26. data/ext/polyphony/backend_libev.c +1 -1
  27. data/ext/polyphony/extconf.rb +24 -8
  28. data/ext/polyphony/fiber.c +79 -2
  29. data/ext/polyphony/io_extensions.c +53 -0
  30. data/ext/polyphony/pipe.c +42 -2
  31. data/ext/polyphony/polyphony.c +345 -31
  32. data/ext/polyphony/polyphony.h +9 -2
  33. data/ext/polyphony/queue.c +181 -0
  34. data/ext/polyphony/ring_buffer.c +0 -1
  35. data/ext/polyphony/runqueue.c +8 -1
  36. data/ext/polyphony/runqueue_ring_buffer.c +13 -0
  37. data/ext/polyphony/runqueue_ring_buffer.h +2 -1
  38. data/ext/polyphony/socket_extensions.c +6 -0
  39. data/ext/polyphony/thread.c +34 -2
  40. data/lib/polyphony/adapters/process.rb +11 -1
  41. data/lib/polyphony/adapters/sequel.rb +1 -1
  42. data/lib/polyphony/core/channel.rb +2 -0
  43. data/lib/polyphony/core/debug.rb +1 -1
  44. data/lib/polyphony/core/global_api.rb +25 -24
  45. data/lib/polyphony/core/resource_pool.rb +7 -6
  46. data/lib/polyphony/core/sync.rb +2 -2
  47. data/lib/polyphony/core/thread_pool.rb +3 -3
  48. data/lib/polyphony/core/timer.rb +8 -8
  49. data/lib/polyphony/extensions/exception.rb +2 -0
  50. data/lib/polyphony/extensions/fiber.rb +15 -13
  51. data/lib/polyphony/extensions/io.rb +127 -5
  52. data/lib/polyphony/extensions/kernel.rb +20 -2
  53. data/lib/polyphony/extensions/openssl.rb +100 -11
  54. data/lib/polyphony/extensions/pipe.rb +103 -7
  55. data/lib/polyphony/extensions/process.rb +13 -1
  56. data/lib/polyphony/extensions/socket.rb +93 -27
  57. data/lib/polyphony/extensions/thread.rb +9 -1
  58. data/lib/polyphony/extensions/timeout.rb +1 -1
  59. data/lib/polyphony/version.rb +2 -1
  60. data/lib/polyphony.rb +27 -7
  61. data/polyphony.gemspec +1 -8
  62. data/test/stress.rb +1 -1
  63. data/test/test_global_api.rb +45 -7
  64. data/test/test_socket.rb +96 -0
  65. data/test/test_timer.rb +5 -5
  66. metadata +17 -40
  67. data/docs/_config.yml +0 -64
  68. data/docs/_includes/head.html +0 -40
  69. data/docs/_includes/title.html +0 -1
  70. data/docs/_sass/custom/custom.scss +0 -10
  71. data/docs/_sass/overrides.scss +0 -0
  72. data/docs/api-reference/exception.md +0 -31
  73. data/docs/api-reference/fiber.md +0 -425
  74. data/docs/api-reference/index.md +0 -9
  75. data/docs/api-reference/io.md +0 -36
  76. data/docs/api-reference/object.md +0 -99
  77. data/docs/api-reference/polyphony-baseexception.md +0 -33
  78. data/docs/api-reference/polyphony-cancel.md +0 -26
  79. data/docs/api-reference/polyphony-moveon.md +0 -24
  80. data/docs/api-reference/polyphony-net.md +0 -20
  81. data/docs/api-reference/polyphony-process.md +0 -28
  82. data/docs/api-reference/polyphony-resourcepool.md +0 -59
  83. data/docs/api-reference/polyphony-restart.md +0 -18
  84. data/docs/api-reference/polyphony-terminate.md +0 -18
  85. data/docs/api-reference/polyphony-threadpool.md +0 -67
  86. data/docs/api-reference/polyphony-throttler.md +0 -77
  87. data/docs/api-reference/polyphony.md +0 -36
  88. data/docs/api-reference/thread.md +0 -88
  89. data/docs/favicon.ico +0 -0
  90. data/docs/getting-started/index.md +0 -10
  91. data/docs/getting-started/installing.md +0 -34
  92. /data/{docs/assets/img → assets}/echo-fibers.svg +0 -0
  93. /data/{docs → assets}/polyphony-logo.png +0 -0
  94. /data/{docs/assets/img → assets}/sleeping-fiber.svg +0 -0
@@ -6,7 +6,7 @@ require_relative './io'
6
6
  require_relative '../core/thread_pool'
7
7
 
8
8
  # BasicSocket extensions
9
- class BasicSocket
9
+ class BasicSocket < ::IO
10
10
  # Returns `:backend_recv`. This method is used to tell parsers which read
11
11
  # method to use for this object.
12
12
  #
@@ -15,13 +15,17 @@ class BasicSocket
15
15
  :backend_recv
16
16
  end
17
17
 
18
+ # Returns `:backend_send`. This method is used to tell various libraries which
19
+ # write method to use for this object.
20
+ #
21
+ # @return [:backend_send] use Polyphony.backend_send to send DATA
18
22
  def __write_method__
19
23
  :backend_send
20
24
  end
21
25
  end
22
26
 
23
27
  # Socket extensions # TODO: rewrite in C
24
- class ::Socket
28
+ class ::Socket < ::BasicSocket
25
29
 
26
30
  # Accepts an incoming connection.
27
31
 
@@ -35,12 +39,13 @@ class ::Socket
35
39
  #
36
40
  # Accepts incoming connections in an infinite loop.
37
41
  #
38
- # @param &block [Proc] handler block
42
+ # @yield [Socket] block receiving accepted sockets
39
43
  # @return [void]
40
44
  def accept_loop(&block)
41
45
  Polyphony.backend_accept_loop(self, TCPSocket, &block)
42
46
  end
43
47
 
48
+ # @!visibility private
44
49
  NO_EXCEPTION = { exception: false }.freeze
45
50
 
46
51
  # Connects to the given address
@@ -53,6 +58,7 @@ class ::Socket
53
58
  self
54
59
  end
55
60
 
61
+ # @!visibility private
56
62
  alias_method :orig_read, :read
57
63
 
58
64
  # call-seq:
@@ -122,7 +128,7 @@ class ::Socket
122
128
  # will be passed to the given block.
123
129
  #
124
130
  # @param maxlen [Integer] maximum bytes to receive
125
- # @param &block [Proc] handler block
131
+ # @yield [String] handler block
126
132
  # @return [void]
127
133
  def recv_loop(maxlen = 8192, &block)
128
134
  Polyphony.backend_recv_loop(self, maxlen, &block)
@@ -149,13 +155,17 @@ class ::Socket
149
155
  #
150
156
  # @param receiver [any] receiver object
151
157
  # @param method [Symbol] method to call
152
- # @param &block [Proc] block to handle result of method call to receiver
158
+ # @yield [any] block to handle result of method call to receiver
153
159
  # @return [void]
154
160
  def feed_loop(receiver, method = :call, &block)
155
161
  Polyphony.backend_recv_feed_loop(self, receiver, method, &block)
156
162
  end
157
163
 
158
- # :no-doc:
164
+ # Reimplements #recvfrom.
165
+ #
166
+ # @param maxlen [Integer] maximum bytes to receive
167
+ # @param flags [Integer] optional flags
168
+ # @return [String] received data
159
169
  def recvfrom(maxlen, flags = 0)
160
170
  buf = +''
161
171
  while true
@@ -198,6 +208,7 @@ class ::Socket
198
208
  result
199
209
  end
200
210
 
211
+ # @!visibility private
201
212
  ZERO_LINGER = [0, 0].pack('ii').freeze
202
213
 
203
214
  # Sets the linger option to 0.
@@ -233,7 +244,8 @@ class ::Socket
233
244
  end
234
245
 
235
246
  class << self
236
- alias_method :orig_getaddrinfo, :getaddrinfo
247
+ # @!visibility private
248
+ alias_method :orig_getaddrinfo, :getaddrinfo
237
249
 
238
250
  # Resolves the given addr using a worker thread from the default thread
239
251
  # pool.
@@ -246,9 +258,11 @@ class ::Socket
246
258
  end
247
259
 
248
260
  # Overide stock TCPSocket code by encapsulating a Socket instance
249
- class ::TCPSocket
261
+ class ::TCPSocket < ::IPSocket
262
+ # @!visibility private
250
263
  NO_EXCEPTION = { exception: false }.freeze
251
264
 
265
+ # @!visibility private
252
266
  attr_reader :io
253
267
 
254
268
  class << self
@@ -275,6 +289,7 @@ class ::TCPSocket
275
289
  @io.connect(addr)
276
290
  end
277
291
 
292
+ # @!visibility private
278
293
  alias_method :orig_close, :close
279
294
 
280
295
  # Closes the socket.
@@ -285,6 +300,7 @@ class ::TCPSocket
285
300
  self
286
301
  end
287
302
 
303
+ # @!visibility private
288
304
  alias_method :orig_setsockopt, :setsockopt
289
305
 
290
306
  # Calls `setsockopt` with the given arguments.
@@ -295,6 +311,7 @@ class ::TCPSocket
295
311
  self
296
312
  end
297
313
 
314
+ # @!visibility private
298
315
  alias_method :orig_closed?, :closed?
299
316
 
300
317
  # Returns true if the socket is closed.
@@ -336,6 +353,7 @@ class ::TCPSocket
336
353
  self
337
354
  end
338
355
 
356
+ # @!visibility private
339
357
  alias_method :orig_read, :read
340
358
 
341
359
  # call-seq:
@@ -405,7 +423,7 @@ class ::TCPSocket
405
423
  # will be passed to the given block.
406
424
  #
407
425
  # @param maxlen [Integer] maximum bytes to receive
408
- # @param &block [Proc] handler block
426
+ # @yield [String] handler block
409
427
  # @return [void]
410
428
  def recv_loop(maxlen = 8192, &block)
411
429
  Polyphony.backend_recv_loop(self, maxlen, &block)
@@ -432,7 +450,7 @@ class ::TCPSocket
432
450
  #
433
451
  # @param receiver [any] receiver object
434
452
  # @param method [Symbol] method to call
435
- # @param &block [Proc] block to handle result of method call to receiver
453
+ # @yield [any] block to handle result of method call to receiver
436
454
  # @return [void]
437
455
  def feed_loop(receiver, method = :call, &block)
438
456
  Polyphony.backend_recv_feed_loop(self, receiver, method, &block)
@@ -460,8 +478,8 @@ class ::TCPSocket
460
478
  # @param buf_pos [Number] buffer position to read into
461
479
  # @param raise_on_eof [bool] whether to raise an exception on `EOF`
462
480
  # @return [String, nil] buffer used for reading or nil on `EOF`
463
- def readpartial(maxlen, str = +'', buffer_pos = 0, raise_on_eof = true)
464
- result = Polyphony.backend_recv(self, str, maxlen, buffer_pos)
481
+ def readpartial(maxlen, buf = +'', buf_pos = 0, raise_on_eof = true)
482
+ result = Polyphony.backend_recv(self, buf, maxlen, buf_pos)
465
483
  raise EOFError if !result && raise_on_eof
466
484
  result
467
485
  end
@@ -476,8 +494,8 @@ class ::TCPSocket
476
494
  # @param buf [String, nil] read buffer
477
495
  # @param exception [bool] whether to raise an exception if not ready for reading
478
496
  # @return [String, :wait_readable] read buffer
479
- def read_nonblock(len, buf = nil, exception: true)
480
- @io.read_nonblock(len, buf, exception: exception)
497
+ def read_nonblock(maxlen, buf = nil, exception: true)
498
+ @io.read_nonblock(maxlen, buf, exception: exception)
481
499
  end
482
500
 
483
501
  # Performs a non-blocking to the socket. If the socket is not ready for
@@ -494,7 +512,7 @@ class ::TCPSocket
494
512
  end
495
513
 
496
514
  # TCPServer extensions
497
- class ::TCPServer
515
+ class ::TCPServer < ::TCPSocket
498
516
 
499
517
  # Initializes the TCP server socket.
500
518
  #
@@ -503,10 +521,12 @@ class ::TCPServer
503
521
  def initialize(hostname = nil, port = 0)
504
522
  addr = Addrinfo.tcp(hostname, port)
505
523
  @io = Socket.new addr.afamily, Socket::SOCK_STREAM
524
+ @io.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)
506
525
  @io.bind(addr)
507
526
  @io.listen(0)
508
527
  end
509
528
 
529
+ # @!visibility private
510
530
  alias_method :orig_accept, :accept
511
531
 
512
532
  # Accepts an incoming connection.
@@ -516,17 +536,33 @@ class ::TCPServer
516
536
  Polyphony.backend_accept(@io, TCPSocket)
517
537
  end
518
538
 
539
+ if Polyphony.instance_methods(false).include?(:backend_multishot_accept)
540
+ # Starts a multishot accept operation (only available with io_uring
541
+ # backend). Example usage:
542
+ #
543
+ # server.multishot_accept do
544
+ # server.accept_loop { |c| handle_connection(c) }
545
+ # end
546
+ #
547
+ # @yield [TCPSocket] code block
548
+ # @return [any] return value of code block
549
+ def multishot_accept(&block)
550
+ Polyphony.backend_multishot_accept(@io, &block)
551
+ end
552
+ end
553
+
519
554
  # call-seq:
520
555
  # socket.accept_loop { |conn| ... }
521
556
  #
522
557
  # Accepts incoming connections in an infinite loop.
523
558
  #
524
- # @param &block [Proc] handler block
559
+ # @yield [TCPSocket] handler block
525
560
  # @return [void]
526
561
  def accept_loop(&block)
527
562
  Polyphony.backend_accept_loop(@io, TCPSocket, &block)
528
563
  end
529
564
 
565
+ # @!visibility private
530
566
  alias_method :orig_close, :close
531
567
 
532
568
  # Closes the server socket.
@@ -538,7 +574,9 @@ class ::TCPServer
538
574
  end
539
575
  end
540
576
 
541
- class ::UNIXServer
577
+ # UNIXServer extensions
578
+ class ::UNIXServer < ::UNIXSocket
579
+ # @!visibility private
542
580
  alias_method :orig_accept, :accept
543
581
 
544
582
  # Accepts an incoming connection.
@@ -553,14 +591,16 @@ class ::UNIXServer
553
591
  #
554
592
  # Accepts incoming connections in an infinite loop.
555
593
  #
556
- # @param &block [Proc] handler block
594
+ # @yield [UNIXSocket] handler block
557
595
  # @return [void]
558
596
  def accept_loop(&block)
559
597
  Polyphony.backend_accept_loop(self, UNIXSocket, &block)
560
598
  end
561
599
  end
562
600
 
563
- class ::UNIXSocket
601
+ # UNIXSocket extensions
602
+ class ::UNIXSocket < ::BasicSocket
603
+ # @!visibility private
564
604
  alias_method :orig_read, :read
565
605
 
566
606
  # call-seq:
@@ -630,7 +670,7 @@ class ::UNIXSocket
630
670
  # will be passed to the given block.
631
671
  #
632
672
  # @param maxlen [Integer] maximum bytes to receive
633
- # @param &block [Proc] handler block
673
+ # @yield [String] handler block
634
674
  # @return [void]
635
675
  def recv_loop(maxlen = 8192, &block)
636
676
  Polyphony.backend_recv_loop(self, maxlen, &block)
@@ -657,7 +697,7 @@ class ::UNIXSocket
657
697
  #
658
698
  # @param receiver [any] receiver object
659
699
  # @param method [Symbol] method to call
660
- # @param &block [Proc] block to handle result of method call to receiver
700
+ # @yield [any] block to handle result of method call to receiver
661
701
  # @return [void]
662
702
  def feed_loop(receiver, method = :call, &block)
663
703
  Polyphony.backend_recv_feed_loop(self, receiver, method, &block)
@@ -675,7 +715,7 @@ class ::UNIXSocket
675
715
  # Sends one or more strings on the socket. The strings are guaranteed to be
676
716
  # written as a single blocking operation.
677
717
  #
678
- # @param *args [Array<String>] string buffers to write
718
+ # @param args [Array<String>] string buffers to write
679
719
  # @return [Integer] number of bytes written
680
720
  def write(*args)
681
721
  Polyphony.backend_sendv(self, args, 0)
@@ -711,8 +751,8 @@ class ::UNIXSocket
711
751
  # @param buf_pos [Number] buffer position to read into
712
752
  # @param raise_on_eof [bool] whether to raise an exception on `EOF`
713
753
  # @return [String, nil] buffer used for reading or nil on `EOF`
714
- def readpartial(maxlen, str = +'', buffer_pos = 0, raise_on_eof = true)
715
- result = Polyphony.backend_recv(self, str, maxlen, buffer_pos)
754
+ def readpartial(maxlen, buf = +'', buf_pos = 0, raise_on_eof = true)
755
+ result = Polyphony.backend_recv(self, buf, maxlen, buf_pos)
716
756
  raise EOFError if !result && raise_on_eof
717
757
  result
718
758
  end
@@ -727,8 +767,8 @@ class ::UNIXSocket
727
767
  # @param buf [String, nil] read buffer
728
768
  # @param exception [bool] whether to raise an exception if not ready for reading
729
769
  # @return [String, :wait_readable] read buffer
730
- def read_nonblock(len, str = nil, exception: true)
731
- @io.read_nonblock(len, str, exception: exception)
770
+ def read_nonblock(maxlen, buf = nil, exception: true)
771
+ @io.read_nonblock(maxlen, buf, exception: exception)
732
772
  end
733
773
 
734
774
  # Performs a non-blocking to the socket. If the socket is not ready for
@@ -744,21 +784,47 @@ class ::UNIXSocket
744
784
  end
745
785
  end
746
786
 
747
- class ::UDPSocket
787
+ # UDPSocket extensions
788
+ class ::UDPSocket < ::IPSocket
789
+ # Reimplements #recvfrom.
790
+ #
791
+ # @param maxlen [Integer] maximum bytes to receive
792
+ # @param flags [Integer] optional flags
793
+ # @return [String] received data
748
794
  def recvfrom(maxlen, flags = 0)
749
795
  buf = +''
750
796
  Polyphony.backend_recvmsg(self, buf, maxlen, 0, flags, 0, nil)
751
797
  end
752
798
 
799
+ # Reimplements #recvmsg.
800
+ #
801
+ # @param maxlen [Integer] maximum bytes to receive
802
+ # @param flags [Integer] optional flags
803
+ # @param maxcontrollen [Integer] maximum control bytes to receive
804
+ # @param opts [Hash] options
805
+ # @return [String] received data
753
806
  def recvmsg(maxlen = nil, flags = 0, maxcontrollen = nil, opts = {})
754
807
  buf = +''
755
808
  Polyphony.backend_recvmsg(self, buf, maxlen || 4096, 0, flags, maxcontrollen, opts)
756
809
  end
757
810
 
811
+ # Reimplements #sendmsg.
812
+ #
813
+ # @param msg [String] data to send
814
+ # @param flags [Integer] optional flags
815
+ # @param dest_sockaddr [Sockaddr, nil] optional destination address
816
+ # @param controls [Array] optional control data
817
+ # @return [Integer] bytes sent
758
818
  def sendmsg(msg, flags = 0, dest_sockaddr = nil, *controls)
759
819
  Polyphony.backend_sendmsg(self, msg, flags, dest_sockaddr, controls)
760
820
  end
761
821
 
822
+ # Sends data.
823
+ #
824
+ # @param msg [String] data to send
825
+ # @param flags [Integer] flags
826
+ # @param addr [Array] addresses to send to
827
+ # @return [Integer] bytes sent
762
828
  def send(msg, flags, *addr)
763
829
  sockaddr = case addr.size
764
830
  when 2
@@ -7,9 +7,13 @@ class ::Thread
7
7
  attr_reader :main_fiber, :result
8
8
  attr_accessor :backend
9
9
 
10
+ # @!visibility private
10
11
  alias_method :orig_initialize, :initialize
11
12
 
12
13
  # Initializes the thread.
14
+ # @param args [Array] arguments to pass to thread block
15
+ # @yield [any] thread block
16
+ # @return [void]
13
17
  def initialize(*args, &block)
14
18
  @join_wait_queue = []
15
19
  @finalization_mutex = Mutex.new
@@ -27,6 +31,7 @@ class ::Thread
27
31
  setup_fiber_scheduling
28
32
  end
29
33
 
34
+ # @!visibility private
30
35
  alias_method :orig_join, :join
31
36
 
32
37
  # call-seq:
@@ -56,6 +61,7 @@ class ::Thread
56
61
  end
57
62
  alias_method :await, :join
58
63
 
64
+ # @!visibility private
59
65
  alias_method :orig_raise, :raise
60
66
 
61
67
  # call-seq:
@@ -77,6 +83,7 @@ class ::Thread
77
83
  main_fiber&.raise(error)
78
84
  end
79
85
 
86
+ # @!visibility private
80
87
  alias_method :orig_kill, :kill
81
88
 
82
89
  # Terminates the thread.
@@ -89,6 +96,7 @@ class ::Thread
89
96
  self
90
97
  end
91
98
 
99
+ # @!visibility private
92
100
  alias_method :orig_inspect, :inspect
93
101
 
94
102
  # Returns a string representation of the thread for debugging purposes.
@@ -128,7 +136,7 @@ class ::Thread
128
136
 
129
137
  # Sets the idle handler for the thread's backend.
130
138
  #
131
- # @param &block [Proc] idle handler
139
+ # @yield [] idle handler
132
140
  # @return [Proc] idle handler
133
141
  def on_idle(&block)
134
142
  backend.idle_proc = block
@@ -13,7 +13,7 @@ module ::Timeout
13
13
  # @param sec [Number] timeout period in seconds
14
14
  # @param klass [Class] exception class
15
15
  # @param message [String] exception message
16
- # @param &block [Proc] code to run
16
+ # @yield [] code to run
17
17
  # @return [any] block's return value
18
18
  def self.timeout(sec, klass = Timeout::Error, message = 'execution expired', &block)
19
19
  cancel_after(sec, with_exception: [klass, message], &block)
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.99'
4
+ # @!visibility private
5
+ VERSION = '0.99.1'
5
6
  end
data/lib/polyphony.rb CHANGED
@@ -18,10 +18,14 @@ require_relative './polyphony/adapters/process'
18
18
  # Polyphony API
19
19
  module Polyphony
20
20
  class << self
21
+ # Creates a new Polyphony::Pipe instance.
22
+ #
23
+ # @return [Polyphony::Pipe] created pipe
21
24
  def pipe
22
25
  Pipe.new
23
26
  end
24
27
 
28
+ # @!visibility private
25
29
  def fork(&block)
26
30
  Kernel.fork do
27
31
  # A race condition can arise if a TERM or INT signal is received before
@@ -39,6 +43,19 @@ module Polyphony
39
43
  end
40
44
  end
41
45
 
46
+ # call-seq:
47
+ # Polyphony.watch_process(cmd)
48
+ # Polyphony.watch_process { sleep 1 }
49
+ #
50
+ # Lubnches a process using either a command or a block for a forked process,
51
+ # waiting for the child process to terminate.
52
+ def watch_process(cmd = nil, &block)
53
+ Polyphony::Process.watch(cmd, &block)
54
+ end
55
+
56
+ private
57
+
58
+ # @!visibility private
42
59
  def spin_forked_block(&block)
43
60
  Fiber.new do
44
61
  run_forked_block(&block)
@@ -54,6 +71,7 @@ module Polyphony
54
71
  end
55
72
  end
56
73
 
74
+ # @!visibility private
57
75
  def run_forked_block(&block)
58
76
  Thread.current.setup
59
77
  Thread.current.backend.post_fork
@@ -63,6 +81,7 @@ module Polyphony
63
81
  block.()
64
82
  end
65
83
 
84
+ # @!visibility private
66
85
  def exit_forked_process
67
86
  terminate_threads
68
87
  Fiber.current.shutdown_all_children
@@ -74,10 +93,7 @@ module Polyphony
74
93
  exit
75
94
  end
76
95
 
77
- def watch_process(cmd = nil, &block)
78
- Polyphony::Process.watch(cmd, &block)
79
- end
80
-
96
+ # @!visibility private
81
97
  def install_terminating_signal_handlers
82
98
  trap('SIGTERM') { raise SystemExit }
83
99
  orig_trap('SIGINT') do
@@ -86,6 +102,7 @@ module Polyphony
86
102
  end
87
103
  end
88
104
 
105
+ # @!visibility private
89
106
  def terminate_threads
90
107
  threads = Thread.list - [Thread.current]
91
108
  return if threads.empty?
@@ -94,8 +111,10 @@ module Polyphony
94
111
  threads.each(&:join)
95
112
  end
96
113
 
114
+ # @!visibility private
97
115
  attr_accessor :original_pid
98
116
 
117
+ # @!visibility private
99
118
  def install_at_exit_handler
100
119
  @original_pid = ::Process.pid
101
120
 
@@ -106,7 +125,7 @@ module Polyphony
106
125
  at_exit do
107
126
  next unless @original_pid == ::Process.pid
108
127
 
109
- Polyphony.terminate_threads
128
+ terminate_threads
110
129
  Fiber.current.shutdown_all_children
111
130
  end
112
131
  end
@@ -123,10 +142,11 @@ module Polyphony
123
142
 
124
143
  Object.const_set(:ConditionVariable, Polyphony::ConditionVariable)
125
144
  $VERBOSE = verbose
145
+
146
+ install_terminating_signal_handlers
147
+ install_at_exit_handler
126
148
  end
127
149
 
128
- Polyphony.install_terminating_signal_handlers
129
- Polyphony.install_at_exit_handler
130
150
 
131
151
  if (debug_socket_path = ENV['POLYPHONY_DEBUG_SOCKET_PATH'])
132
152
  puts "Starting debug server on #{debug_socket_path}"
data/polyphony.gemspec CHANGED
@@ -11,9 +11,7 @@ Gem::Specification.new do |s|
11
11
  s.homepage = 'https://digital-fabric.github.io/polyphony'
12
12
  s.metadata = {
13
13
  "source_code_uri" => "https://github.com/digital-fabric/polyphony",
14
- # "documentation_uri" => "https://www.rubydoc.info/gems/polyphony",
15
- "documentation_uri" => "https://digital-fabric.github.io/polyphony/",
16
- "homepage_uri" => "https://digital-fabric.github.io/polyphony/",
14
+ "documentation_uri" => "https://www.rubydoc.info/gems/polyphony",
17
15
  "changelog_uri" => "https://github.com/digital-fabric/polyphony/blob/master/CHANGELOG.md"
18
16
  }
19
17
  s.rdoc_options = ["--title", "polyphony", "--main", "README.md"]
@@ -31,9 +29,4 @@ Gem::Specification.new do |s|
31
29
  s.add_development_dependency 'msgpack', '1.6.0'
32
30
  s.add_development_dependency 'httparty', '0.21.0'
33
31
  s.add_development_dependency 'localhost', '1.1.10'
34
-
35
- # s.add_development_dependency 'jekyll', '~>3.8.6'
36
- # s.add_development_dependency 'jekyll-remote-theme', '~>0.4.1'
37
- # s.add_development_dependency 'jekyll-seo-tag', '~>2.6.1'
38
- # s.add_development_dependency 'just-the-docs', '~>0.3.0'
39
32
  end
data/test/stress.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  count = ARGV[0] ? ARGV[0].to_i : 100
4
4
  test_name = ARGV[1]
5
5
 
6
- $test_cmd = +'ruby test/run.rb --name test_signal_handler_trace'
6
+ $test_cmd = +'ruby test/run.rb'
7
7
  if test_name
8
8
  $test_cmd << " --name #{test_name}"
9
9
  end
@@ -145,13 +145,13 @@ class MoveOnAfterTest < MiniTest::Test
145
145
 
146
146
  t0 = monotonic_clock
147
147
  o = move_on_after(0.01, with_value: 1) do
148
- move_on_after(0.02, with_value: 2) do
148
+ move_on_after(0.03, with_value: 2) do
149
149
  sleep 1
150
150
  end
151
151
  end
152
152
  t1 = monotonic_clock
153
153
  assert_equal 1, o
154
- assert_in_range 0.008..0.015, t1 - t0 if IS_LINUX
154
+ assert_in_range 0.008..0.027, t1 - t0 if IS_LINUX
155
155
 
156
156
  t0 = monotonic_clock
157
157
  o = move_on_after(0.05, with_value: 1) do
@@ -161,7 +161,7 @@ class MoveOnAfterTest < MiniTest::Test
161
161
  end
162
162
  t1 = monotonic_clock
163
163
  assert_equal 2, o
164
- assert_in_range 0.008..0.013, t1 - t0 if IS_LINUX
164
+ assert_in_range 0.008..0.025, t1 - t0 if IS_LINUX
165
165
  end
166
166
  end
167
167
 
@@ -181,15 +181,17 @@ class CancelAfterTest < MiniTest::Test
181
181
 
182
182
  def test_cancel_after_with_reset
183
183
  t0 = monotonic_clock
184
- cancel_after(0.01) do |f|
184
+ cancel_after(0.1) do |f|
185
185
  assert_kind_of Fiber, f
186
186
  assert_equal Fiber.current, f.parent
187
- sleep 0.007
187
+ sleep 0.05
188
188
  f.reset
189
- sleep 0.007
189
+ sleep 0.05
190
+ f.reset
191
+ sleep 0.05
190
192
  end
191
193
  t1 = monotonic_clock
192
- assert_in_range 0.014..0.024, t1 - t0 if IS_LINUX
194
+ assert_in_range 0.14..0.24, t1 - t0 if IS_LINUX
193
195
  end
194
196
 
195
197
  class CustomException < Exception
@@ -228,6 +230,34 @@ class CancelAfterTest < MiniTest::Test
228
230
  assert_equal 'foo', e.message
229
231
  end
230
232
  end
233
+
234
+ def test_lots_of_cancel_after
235
+ cancels = 100
236
+
237
+ cancel_count = 0
238
+ cancels.times do
239
+ begin
240
+ cancel_after(0.001) { sleep 1 }
241
+ rescue Polyphony::Cancel
242
+ cancel_count += 1
243
+ end
244
+ end
245
+ assert_equal cancels, cancel_count
246
+ end
247
+
248
+ def test_cancel_after_with_lots_of_resets
249
+ resets = 100
250
+
251
+ t0 = monotonic_clock
252
+ cancel_after(0.1) do |f|
253
+ resets.times do
254
+ sleep 0.0001
255
+ f.reset
256
+ end
257
+ end
258
+ t1 = monotonic_clock
259
+ assert_in_range 0.01..0.2, t1 - t0 if IS_LINUX
260
+ end
231
261
  end
232
262
 
233
263
 
@@ -381,6 +411,14 @@ class ThrottledLoopTest < MiniTest::Test
381
411
  assert_in_range 0.075..0.15, t1 - t0 if IS_LINUX
382
412
  assert_equal [1, 2, 3, 4, 5], buffer
383
413
  end
414
+
415
+ def test_throttled_loop_inside_move_on_after
416
+ count = 0
417
+ move_on_after(0.1) do
418
+ throttled_loop(50) { count += 1 }
419
+ end
420
+ assert_in_range 3..7, count
421
+ end
384
422
  end
385
423
 
386
424
  class GlobalAPIEtcTest < MiniTest::Test