polyphony 0.99 → 0.99.1
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/FUNDING.yml +1 -1
- data/.rubocop.yml +3 -3
- data/.yardopts +30 -0
- data/CHANGELOG.md +4 -0
- data/LICENSE +1 -1
- data/README.md +63 -29
- data/Rakefile +1 -5
- data/TODO.md +0 -4
- data/docs/{main-concepts/concurrency.md → concurrency.md} +2 -9
- data/docs/{main-concepts/design-principles.md → design-principles.md} +3 -9
- data/docs/{main-concepts/exception-handling.md → exception-handling.md} +2 -9
- data/docs/{main-concepts/extending.md → extending.md} +2 -9
- data/docs/faq.md +3 -16
- data/docs/{main-concepts/fiber-scheduling.md → fiber-scheduling.md} +1 -9
- data/docs/link_rewriter.rb +16 -0
- data/docs/{getting-started/overview.md → overview.md} +1 -30
- data/docs/{getting-started/tutorial.md → tutorial.md} +3 -28
- data/docs/{_posts/2020-07-26-polyphony-0.44.md → whats-new.md} +3 -1
- data/examples/adapters/redis_client.rb +3 -2
- data/examples/io/echo_server.rb +1 -1
- data/examples/io/echo_server_plain_ruby.rb +26 -0
- data/ext/polyphony/backend_io_uring.c +154 -9
- data/ext/polyphony/backend_io_uring_context.c +21 -12
- data/ext/polyphony/backend_io_uring_context.h +12 -7
- data/ext/polyphony/backend_libev.c +1 -1
- data/ext/polyphony/extconf.rb +24 -8
- data/ext/polyphony/fiber.c +79 -2
- data/ext/polyphony/io_extensions.c +53 -0
- data/ext/polyphony/pipe.c +42 -2
- data/ext/polyphony/polyphony.c +345 -31
- data/ext/polyphony/polyphony.h +9 -2
- data/ext/polyphony/queue.c +181 -0
- data/ext/polyphony/ring_buffer.c +0 -1
- data/ext/polyphony/runqueue.c +8 -1
- data/ext/polyphony/runqueue_ring_buffer.c +13 -0
- data/ext/polyphony/runqueue_ring_buffer.h +2 -1
- data/ext/polyphony/socket_extensions.c +6 -0
- data/ext/polyphony/thread.c +34 -2
- data/lib/polyphony/adapters/process.rb +11 -1
- data/lib/polyphony/adapters/sequel.rb +1 -1
- data/lib/polyphony/core/channel.rb +2 -0
- data/lib/polyphony/core/debug.rb +1 -1
- data/lib/polyphony/core/global_api.rb +25 -24
- data/lib/polyphony/core/resource_pool.rb +7 -6
- data/lib/polyphony/core/sync.rb +2 -2
- data/lib/polyphony/core/thread_pool.rb +3 -3
- data/lib/polyphony/core/timer.rb +8 -8
- data/lib/polyphony/extensions/exception.rb +2 -0
- data/lib/polyphony/extensions/fiber.rb +15 -13
- data/lib/polyphony/extensions/io.rb +127 -5
- data/lib/polyphony/extensions/kernel.rb +20 -2
- data/lib/polyphony/extensions/openssl.rb +100 -11
- data/lib/polyphony/extensions/pipe.rb +103 -7
- data/lib/polyphony/extensions/process.rb +13 -1
- data/lib/polyphony/extensions/socket.rb +93 -27
- data/lib/polyphony/extensions/thread.rb +9 -1
- data/lib/polyphony/extensions/timeout.rb +1 -1
- data/lib/polyphony/version.rb +2 -1
- data/lib/polyphony.rb +27 -7
- data/polyphony.gemspec +1 -8
- data/test/stress.rb +1 -1
- data/test/test_global_api.rb +45 -7
- data/test/test_socket.rb +96 -0
- data/test/test_timer.rb +5 -5
- metadata +17 -40
- data/docs/_config.yml +0 -64
- data/docs/_includes/head.html +0 -40
- data/docs/_includes/title.html +0 -1
- data/docs/_sass/custom/custom.scss +0 -10
- data/docs/_sass/overrides.scss +0 -0
- data/docs/api-reference/exception.md +0 -31
- data/docs/api-reference/fiber.md +0 -425
- data/docs/api-reference/index.md +0 -9
- data/docs/api-reference/io.md +0 -36
- data/docs/api-reference/object.md +0 -99
- data/docs/api-reference/polyphony-baseexception.md +0 -33
- data/docs/api-reference/polyphony-cancel.md +0 -26
- data/docs/api-reference/polyphony-moveon.md +0 -24
- data/docs/api-reference/polyphony-net.md +0 -20
- data/docs/api-reference/polyphony-process.md +0 -28
- data/docs/api-reference/polyphony-resourcepool.md +0 -59
- data/docs/api-reference/polyphony-restart.md +0 -18
- data/docs/api-reference/polyphony-terminate.md +0 -18
- data/docs/api-reference/polyphony-threadpool.md +0 -67
- data/docs/api-reference/polyphony-throttler.md +0 -77
- data/docs/api-reference/polyphony.md +0 -36
- data/docs/api-reference/thread.md +0 -88
- data/docs/favicon.ico +0 -0
- data/docs/getting-started/index.md +0 -10
- data/docs/getting-started/installing.md +0 -34
- /data/{docs/assets/img → assets}/echo-fibers.svg +0 -0
- /data/{docs → assets}/polyphony-logo.png +0 -0
- /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
|
-
# @
|
|
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
|
-
# @
|
|
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
|
-
# @
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
# @
|
|
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
|
-
# @
|
|
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,
|
|
464
|
-
result = Polyphony.backend_recv(self,
|
|
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(
|
|
480
|
-
@io.read_nonblock(
|
|
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
|
-
# @
|
|
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
|
-
|
|
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
|
-
# @
|
|
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
|
-
|
|
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
|
-
# @
|
|
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
|
-
# @
|
|
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
|
|
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,
|
|
715
|
-
result = Polyphony.backend_recv(self,
|
|
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(
|
|
731
|
-
@io.read_nonblock(
|
|
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
|
-
|
|
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
|
-
# @
|
|
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
|
-
# @
|
|
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)
|
data/lib/polyphony/version.rb
CHANGED
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
data/test/test_global_api.rb
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
187
|
+
sleep 0.05
|
|
188
188
|
f.reset
|
|
189
|
-
sleep 0.
|
|
189
|
+
sleep 0.05
|
|
190
|
+
f.reset
|
|
191
|
+
sleep 0.05
|
|
190
192
|
end
|
|
191
193
|
t1 = monotonic_clock
|
|
192
|
-
assert_in_range 0.
|
|
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
|