polyphony 0.98 → 0.99.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -1
- data/.rubocop.yml +3 -3
- data/.yardopts +30 -0
- data/CHANGELOG.md +11 -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/examples/io/https_server_sni_2.rb +14 -8
- 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 +25 -8
- data/ext/polyphony/fiber.c +79 -2
- data/ext/polyphony/io_extensions.c +53 -0
- data/ext/polyphony/libev.h +0 -2
- 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 +55 -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 +161 -16
- data/lib/polyphony/extensions/kernel.rb +20 -2
- data/lib/polyphony/extensions/openssl.rb +101 -12
- 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_io.rb +6 -7
- data/test/test_socket.rb +157 -0
- data/test/test_sync.rb +42 -1
- data/test/test_timer.rb +5 -5
- data/vendor/liburing/.github/workflows/build.yml +7 -16
- data/vendor/liburing/.gitignore +5 -0
- data/vendor/liburing/CHANGELOG +23 -1
- data/vendor/liburing/Makefile +4 -3
- data/vendor/liburing/Makefile.common +1 -0
- data/vendor/liburing/README +48 -0
- data/vendor/liburing/configure +76 -6
- data/vendor/liburing/debian/changelog +11 -0
- data/vendor/liburing/debian/control +7 -16
- data/vendor/liburing/debian/liburing-dev.manpages +3 -6
- data/vendor/liburing/debian/liburing2.install +1 -0
- data/vendor/liburing/debian/liburing2.symbols +56 -0
- data/vendor/liburing/debian/rules +15 -68
- data/vendor/liburing/examples/Makefile +4 -0
- data/vendor/liburing/examples/io_uring-close-test.c +123 -0
- data/vendor/liburing/examples/io_uring-udp.c +1 -1
- data/vendor/liburing/examples/send-zerocopy.c +315 -56
- data/vendor/liburing/examples/ucontext-cp.c +2 -17
- data/vendor/liburing/liburing-ffi.pc.in +12 -0
- data/vendor/liburing/liburing.pc.in +1 -1
- data/vendor/liburing/liburing.spec +1 -1
- data/vendor/liburing/make-debs.sh +3 -3
- data/vendor/liburing/man/IO_URING_CHECK_VERSION.3 +1 -0
- data/vendor/liburing/man/IO_URING_VERSION_MAJOR.3 +1 -0
- data/vendor/liburing/man/IO_URING_VERSION_MINOR.3 +1 -0
- data/vendor/liburing/man/io_uring_buf_ring_add.3 +6 -6
- data/vendor/liburing/man/io_uring_check_version.3 +72 -0
- data/vendor/liburing/man/io_uring_close_ring_fd.3 +43 -0
- data/vendor/liburing/man/io_uring_major_version.3 +1 -0
- data/vendor/liburing/man/io_uring_minor_version.3 +1 -0
- data/vendor/liburing/man/io_uring_prep_accept.3 +1 -1
- data/vendor/liburing/man/io_uring_prep_fgetxattr.3 +1 -0
- data/vendor/liburing/man/io_uring_prep_fsetxattr.3 +1 -0
- data/vendor/liburing/man/io_uring_prep_getxattr.3 +61 -0
- data/vendor/liburing/man/io_uring_prep_link_timeout.3 +94 -0
- data/vendor/liburing/man/io_uring_prep_msg_ring.3 +22 -2
- data/vendor/liburing/man/io_uring_prep_msg_ring_cqe_flags.3 +1 -0
- data/vendor/liburing/man/io_uring_prep_poll_add.3 +1 -1
- data/vendor/liburing/man/io_uring_prep_provide_buffers.3 +18 -9
- data/vendor/liburing/man/io_uring_prep_readv.3 +3 -3
- data/vendor/liburing/man/io_uring_prep_readv2.3 +3 -3
- data/vendor/liburing/man/io_uring_prep_recv.3 +5 -5
- data/vendor/liburing/man/io_uring_prep_recvmsg.3 +4 -4
- data/vendor/liburing/man/io_uring_prep_send.3 +9 -0
- data/vendor/liburing/man/io_uring_prep_send_set_addr.3 +38 -0
- data/vendor/liburing/man/io_uring_prep_send_zc.3 +39 -7
- data/vendor/liburing/man/io_uring_prep_send_zc_fixed.3 +1 -0
- data/vendor/liburing/man/io_uring_prep_sendmsg.3 +20 -0
- data/vendor/liburing/man/io_uring_prep_sendmsg_zc.3 +1 -0
- data/vendor/liburing/man/io_uring_prep_setxattr.3 +64 -0
- data/vendor/liburing/man/io_uring_prep_splice.3 +40 -0
- data/vendor/liburing/man/io_uring_prep_writev.3 +2 -2
- data/vendor/liburing/man/io_uring_prep_writev2.3 +2 -2
- data/vendor/liburing/man/io_uring_recvmsg_out.3 +13 -9
- data/vendor/liburing/man/io_uring_register.2 +15 -9
- data/vendor/liburing/man/io_uring_register_buf_ring.3 +4 -4
- data/vendor/liburing/man/io_uring_register_buffers.3 +49 -6
- data/vendor/liburing/man/io_uring_register_buffers_sparse.3 +1 -0
- data/vendor/liburing/man/io_uring_register_buffers_tags.3 +1 -0
- data/vendor/liburing/man/io_uring_register_buffers_update_tag.3 +1 -0
- data/vendor/liburing/man/io_uring_register_files.3 +60 -5
- data/vendor/liburing/man/io_uring_register_files_tags.3 +1 -0
- data/vendor/liburing/man/io_uring_register_files_update.3 +1 -0
- data/vendor/liburing/man/io_uring_register_files_update_tag.3 +1 -0
- data/vendor/liburing/man/io_uring_setup.2 +31 -2
- data/vendor/liburing/man/io_uring_wait_cqe_timeout.3 +1 -1
- data/vendor/liburing/src/Makefile +25 -3
- data/vendor/liburing/src/ffi.c +15 -0
- data/vendor/liburing/src/include/liburing/io_uring.h +30 -7
- data/vendor/liburing/src/include/liburing.h +190 -148
- data/vendor/liburing/src/int_flags.h +1 -0
- data/vendor/liburing/src/lib.h +5 -16
- data/vendor/liburing/src/liburing-ffi.map +172 -0
- data/vendor/liburing/src/liburing.map +11 -0
- data/vendor/liburing/src/nolibc.c +9 -2
- data/vendor/liburing/src/queue.c +2 -2
- data/vendor/liburing/src/register.c +66 -96
- data/vendor/liburing/src/setup.c +5 -4
- data/vendor/liburing/src/version.c +21 -0
- data/vendor/liburing/test/232c93d07b74.c +3 -3
- data/vendor/liburing/test/35fa71a030ca.c +3 -3
- data/vendor/liburing/test/500f9fbadef8.c +2 -0
- data/vendor/liburing/test/917257daa0fe.c +1 -1
- data/vendor/liburing/test/Makefile +27 -7
- data/vendor/liburing/test/a0908ae19763.c +2 -2
- data/vendor/liburing/test/a4c0b3decb33.c +2 -2
- data/vendor/liburing/test/accept-link.c +4 -4
- data/vendor/liburing/test/accept-reuse.c +5 -7
- data/vendor/liburing/test/accept.c +34 -31
- data/vendor/liburing/test/b19062a56726.c +1 -1
- data/vendor/liburing/test/buf-ring.c +58 -4
- data/vendor/liburing/test/ce593a6c480a.c +2 -2
- data/vendor/liburing/test/close-opath.c +2 -1
- data/vendor/liburing/test/connect.c +8 -0
- data/vendor/liburing/test/cq-overflow.c +14 -8
- data/vendor/liburing/test/d4ae271dfaae.c +1 -1
- data/vendor/liburing/test/defer-taskrun.c +64 -9
- data/vendor/liburing/test/defer.c +1 -1
- data/vendor/liburing/test/double-poll-crash.c +3 -3
- data/vendor/liburing/test/eeed8b54e0df.c +8 -3
- data/vendor/liburing/test/eploop.c +74 -0
- data/vendor/liburing/test/eventfd-ring.c +1 -1
- data/vendor/liburing/test/eventfd.c +1 -1
- data/vendor/liburing/test/evloop.c +73 -0
- data/vendor/liburing/test/exit-no-cleanup.c +1 -1
- data/vendor/liburing/test/fadvise.c +1 -1
- data/vendor/liburing/test/fc2a85cb02ef.c +3 -3
- data/vendor/liburing/test/fd-pass.c +35 -16
- data/vendor/liburing/test/file-register.c +61 -0
- data/vendor/liburing/test/file-verify.c +2 -2
- data/vendor/liburing/test/files-exit-hang-timeout.c +2 -2
- data/vendor/liburing/test/fixed-link.c +1 -1
- data/vendor/liburing/test/fsnotify.c +118 -0
- data/vendor/liburing/test/hardlink.c +1 -1
- data/vendor/liburing/test/helpers.c +54 -2
- data/vendor/liburing/test/helpers.h +4 -0
- data/vendor/liburing/test/io-cancel.c +3 -1
- data/vendor/liburing/test/io_uring_passthrough.c +39 -8
- data/vendor/liburing/test/io_uring_setup.c +3 -80
- data/vendor/liburing/test/iopoll-overflow.c +118 -0
- data/vendor/liburing/test/iopoll.c +90 -4
- data/vendor/liburing/test/lfs-openat-write.c +7 -9
- data/vendor/liburing/test/lfs-openat.c +6 -8
- data/vendor/liburing/test/link_drain.c +31 -5
- data/vendor/liburing/test/madvise.c +1 -1
- data/vendor/liburing/test/msg-ring-flags.c +192 -0
- data/vendor/liburing/test/msg-ring-overflow.c +159 -0
- data/vendor/liburing/test/msg-ring.c +173 -13
- data/vendor/liburing/test/multicqes_drain.c +22 -19
- data/vendor/liburing/test/nvme.h +4 -3
- data/vendor/liburing/test/pipe-bug.c +95 -0
- data/vendor/liburing/test/poll-link.c +3 -3
- data/vendor/liburing/test/poll-many.c +41 -19
- data/vendor/liburing/test/poll-mshot-overflow.c +105 -2
- data/vendor/liburing/test/poll-race-mshot.c +292 -0
- data/vendor/liburing/test/poll-race.c +105 -0
- data/vendor/liburing/test/poll.c +244 -26
- data/vendor/liburing/test/pollfree.c +5 -5
- data/vendor/liburing/test/read-before-exit.c +20 -3
- data/vendor/liburing/test/read-write.c +2 -0
- data/vendor/liburing/test/recv-multishot.c +96 -3
- data/vendor/liburing/test/reg-reg-ring.c +90 -0
- data/vendor/liburing/test/rename.c +1 -1
- data/vendor/liburing/test/ring-leak.c +0 -1
- data/vendor/liburing/test/ring-leak2.c +1 -1
- data/vendor/liburing/test/ringbuf-read.c +10 -6
- data/vendor/liburing/test/send-zerocopy.c +273 -103
- data/vendor/liburing/test/send_recv.c +7 -4
- data/vendor/liburing/test/sendmsg_fs_cve.c +2 -2
- data/vendor/liburing/test/single-issuer.c +7 -9
- data/vendor/liburing/test/skip-cqe.c +3 -4
- data/vendor/liburing/test/socket.c +0 -1
- data/vendor/liburing/test/sq-poll-dup.c +10 -3
- data/vendor/liburing/test/sq-poll-kthread.c +1 -1
- data/vendor/liburing/test/sq-poll-share.c +3 -2
- data/vendor/liburing/test/sqpoll-cancel-hang.c +17 -6
- data/vendor/liburing/test/sqpoll-disable-exit.c +4 -4
- data/vendor/liburing/test/symlink.c +2 -1
- data/vendor/liburing/test/test.h +2 -1
- data/vendor/liburing/test/timeout-new.c +11 -7
- data/vendor/liburing/test/timeout.c +1 -2
- data/vendor/liburing/test/unlink.c +1 -1
- data/vendor/liburing/test/version.c +25 -0
- data/vendor/liburing/test/wakeup-hang.c +1 -1
- data/vendor/liburing/test/xattr.c +8 -4
- metadata +57 -44
- 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/vendor/liburing/debian/compat +0 -1
- data/vendor/liburing/debian/liburing1-udeb.install +0 -1
- data/vendor/liburing/debian/liburing1.install +0 -1
- data/vendor/liburing/debian/liburing1.symbols +0 -32
- /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
@@ -8,7 +8,7 @@ module Polyphony
|
|
8
8
|
# Initializes a new resource pool.
|
9
9
|
#
|
10
10
|
# @param opts [Hash] options
|
11
|
-
# @
|
11
|
+
# @yield [] allocator block
|
12
12
|
def initialize(opts, &block)
|
13
13
|
@allocator = block
|
14
14
|
@limit = opts[:limit] || 4
|
@@ -36,7 +36,7 @@ module Polyphony
|
|
36
36
|
# db.query(sql).to_a
|
37
37
|
# end
|
38
38
|
#
|
39
|
-
# @
|
39
|
+
# @yield [any] code to run
|
40
40
|
# @return [any] return value of block
|
41
41
|
def acquire(&block)
|
42
42
|
fiber = Fiber.current
|
@@ -54,13 +54,14 @@ module Polyphony
|
|
54
54
|
# }
|
55
55
|
#
|
56
56
|
# @param sym [Symbol] method name
|
57
|
-
# @param
|
58
|
-
# @
|
57
|
+
# @param args [Array<any>] method arguments
|
58
|
+
# @yield [any] block passed to method
|
59
|
+
# @return [any] result of method call
|
59
60
|
def method_missing(sym, *args, &block)
|
60
61
|
acquire { |r| r.send(sym, *args, &block) }
|
61
62
|
end
|
62
63
|
|
63
|
-
#
|
64
|
+
# @!visibility private
|
64
65
|
def respond_to_missing?(*_args)
|
65
66
|
true
|
66
67
|
end
|
@@ -87,7 +88,7 @@ module Polyphony
|
|
87
88
|
# Acquires a resource from stock, yielding it to the given block.
|
88
89
|
#
|
89
90
|
# @param fiber [Fiber] the fiber the resource will be associated with
|
90
|
-
# @
|
91
|
+
# @yield [any] given block
|
91
92
|
# @return [any] return value of block
|
92
93
|
def acquire_from_stock(fiber)
|
93
94
|
add_to_stock if (@stock.empty? || @stock.pending?) && @size < @limit
|
data/lib/polyphony/core/sync.rb
CHANGED
@@ -17,7 +17,7 @@ module Polyphony
|
|
17
17
|
# This method is re-entrant. Recursive calls from the given block will not
|
18
18
|
# block.
|
19
19
|
#
|
20
|
-
# @
|
20
|
+
# @yield [] code to run
|
21
21
|
# @return [any] return value of block
|
22
22
|
def synchronize(&block)
|
23
23
|
return yield if @holding_fiber == Fiber.current
|
@@ -58,6 +58,58 @@ module Polyphony
|
|
58
58
|
@holding_fiber
|
59
59
|
end
|
60
60
|
|
61
|
+
# Obtains a lock. Raises `ThreadError` if mutex is locked by the current
|
62
|
+
# thread.
|
63
|
+
#
|
64
|
+
# @return [Mutex] self
|
65
|
+
def lock
|
66
|
+
raise ThreadError if owned?
|
67
|
+
|
68
|
+
@token = @store.shift
|
69
|
+
@holding_fiber = Fiber.current
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
# Releases the lock. Raises `ThreadError` if mutex is not locked by the
|
74
|
+
# current thread.
|
75
|
+
#
|
76
|
+
# @return [Mutex] self
|
77
|
+
def unlock
|
78
|
+
raise ThreadError if !owned?
|
79
|
+
|
80
|
+
@holding_fiber = nil
|
81
|
+
@store << @token if @token
|
82
|
+
@token = nil
|
83
|
+
end
|
84
|
+
|
85
|
+
# Attempts to obtain the lock and returns immediately. Returns `true` if the
|
86
|
+
# lock was granted.
|
87
|
+
#
|
88
|
+
# @return [true, false]
|
89
|
+
def try_lock
|
90
|
+
return false if @holding_fiber
|
91
|
+
|
92
|
+
@token = @store.shift
|
93
|
+
@holding_fiber = Fiber.current
|
94
|
+
true
|
95
|
+
end
|
96
|
+
|
97
|
+
# Releases the lock and sleeps timeout seconds if it is given and non-nil or
|
98
|
+
# forever. Raises `ThreadError` if mutex wasn’t locked by the current
|
99
|
+
# thread.
|
100
|
+
#
|
101
|
+
# @param timeout [nil, Number] sleep timeout
|
102
|
+
# @return [Number] slept time in seconds
|
103
|
+
def sleep(timeout = nil)
|
104
|
+
unlock
|
105
|
+
t0 = Time.now
|
106
|
+
Kernel.sleep(timeout)
|
107
|
+
t1 = Time.now
|
108
|
+
lock
|
109
|
+
|
110
|
+
return t1 - t0
|
111
|
+
end
|
112
|
+
|
61
113
|
private
|
62
114
|
|
63
115
|
# Helper method for performing a `#synchronize` when not currently holding
|
@@ -72,6 +124,7 @@ module Polyphony
|
|
72
124
|
ensure
|
73
125
|
@holding_fiber = nil
|
74
126
|
@store << @token if @token
|
127
|
+
@token = nil
|
75
128
|
end
|
76
129
|
end
|
77
130
|
end
|
@@ -87,7 +140,7 @@ module Polyphony
|
|
87
140
|
# Waits for the condition variable to be signalled.
|
88
141
|
#
|
89
142
|
# @param mutex [Polyphony::Mutex] mutex to release while waiting for signal
|
90
|
-
# @param
|
143
|
+
# @param _timeout [Number, nil] timeout in seconds (currently not implemented)
|
91
144
|
# @return [void]
|
92
145
|
def wait(mutex, _timeout = nil)
|
93
146
|
mutex.conditional_release
|
@@ -12,7 +12,7 @@ module Polyphony
|
|
12
12
|
|
13
13
|
# Runs the given block on an available thread from the default thread pool.
|
14
14
|
#
|
15
|
-
# @
|
15
|
+
# @yield [] given block
|
16
16
|
# @return [any] return value of given block
|
17
17
|
def self.process(&block)
|
18
18
|
@default_pool ||= new
|
@@ -41,7 +41,7 @@ module Polyphony
|
|
41
41
|
|
42
42
|
# Runs the given block on an available thread from the pool.
|
43
43
|
#
|
44
|
-
# @
|
44
|
+
# @yield [] given block
|
45
45
|
# @return [any] return value of block
|
46
46
|
def process(&block)
|
47
47
|
setup unless @task_queue
|
@@ -55,7 +55,7 @@ module Polyphony
|
|
55
55
|
# method does not block. The task will be performed once a thread becomes
|
56
56
|
# available.
|
57
57
|
#
|
58
|
-
# @
|
58
|
+
# @yield [] given block
|
59
59
|
# @return [Polyphony::ThreadPool] self
|
60
60
|
def cast(&block)
|
61
61
|
setup unless @task_queue
|
data/lib/polyphony/core/timer.rb
CHANGED
@@ -11,7 +11,7 @@ module Polyphony
|
|
11
11
|
# Initializes a new timer with the given resolution.
|
12
12
|
#
|
13
13
|
# @param tag [any] tag to use for the timer's fiber
|
14
|
-
# @param resolution
|
14
|
+
# @param resolution [Number] timer granularity in seconds or fractions thereof
|
15
15
|
def initialize(tag = nil, resolution:)
|
16
16
|
@fiber = spin_loop(tag, interval: resolution) { update }
|
17
17
|
@timeouts = {}
|
@@ -43,8 +43,8 @@ module Polyphony
|
|
43
43
|
# Spins up a fiber that will run the given block after sleeping for the
|
44
44
|
# given delay.
|
45
45
|
#
|
46
|
-
# @param
|
47
|
-
# @
|
46
|
+
# @param interval [Number] delay in seconds before running the given block
|
47
|
+
# @yield [] block to run
|
48
48
|
# @return [Fiber] spun fiber
|
49
49
|
def after(interval, &block)
|
50
50
|
spin do
|
@@ -57,7 +57,7 @@ module Polyphony
|
|
57
57
|
# consecutive iterations.
|
58
58
|
#
|
59
59
|
# @param interval [Number] interval between consecutive iterations in seconds
|
60
|
-
# @
|
60
|
+
# @yield [] block to run
|
61
61
|
# @return [void]
|
62
62
|
def every(interval)
|
63
63
|
fiber = Fiber.current
|
@@ -109,8 +109,8 @@ module Polyphony
|
|
109
109
|
# end
|
110
110
|
#
|
111
111
|
# @param interval [Number] timout in seconds
|
112
|
-
# @param with_exception
|
113
|
-
# @
|
112
|
+
# @param with_exception [Class, Exception] exception or exception class
|
113
|
+
# @yield [Canceller] block to execute
|
114
114
|
# @return [any] block's return value
|
115
115
|
def cancel_after(interval, with_exception: Polyphony::Cancel)
|
116
116
|
fiber = Fiber.current
|
@@ -163,8 +163,8 @@ module Polyphony
|
|
163
163
|
# end
|
164
164
|
#
|
165
165
|
# @param interval [Number] timout in seconds
|
166
|
-
# @param with_value
|
167
|
-
# @
|
166
|
+
# @param with_value [any] return value in case of timeout
|
167
|
+
# @yield [Fiber] block to execute
|
168
168
|
# @return [any] block's return value
|
169
169
|
def move_on_after(interval, with_value: nil)
|
170
170
|
fiber = Fiber.current
|
@@ -18,6 +18,7 @@ class ::Exception
|
|
18
18
|
# Set to the fiber from which the exception was raised.
|
19
19
|
attr_accessor :raising_fiber
|
20
20
|
|
21
|
+
# @!visibility private
|
21
22
|
alias_method :orig_initialize, :initialize
|
22
23
|
|
23
24
|
# Initializes the exception with the given arguments.
|
@@ -26,6 +27,7 @@ class ::Exception
|
|
26
27
|
orig_initialize(*args)
|
27
28
|
end
|
28
29
|
|
30
|
+
# @!visibility private
|
29
31
|
alias_method :orig_backtrace, :backtrace
|
30
32
|
|
31
33
|
# Returns the backtrace for the exception. If
|
@@ -116,7 +116,7 @@ module Polyphony
|
|
116
116
|
# operation will be resumed. This API is experimental and might be removed
|
117
117
|
# in the future.
|
118
118
|
#
|
119
|
-
# @
|
119
|
+
# @yield [any] given block
|
120
120
|
# @return [Fiber] self
|
121
121
|
def interject(&block)
|
122
122
|
raise Polyphony::Interjection.new(block)
|
@@ -132,7 +132,7 @@ module Polyphony
|
|
132
132
|
|
133
133
|
private
|
134
134
|
|
135
|
-
#
|
135
|
+
# @!visibility private
|
136
136
|
def error_from_raise_args(args)
|
137
137
|
case (arg = args.shift)
|
138
138
|
when String then RuntimeError.new(arg)
|
@@ -171,10 +171,11 @@ module Polyphony
|
|
171
171
|
#
|
172
172
|
# This method blocks indefinitely.
|
173
173
|
#
|
174
|
-
# @param
|
175
|
-
# @
|
176
|
-
# @
|
177
|
-
# @
|
174
|
+
# @param fibers [Array<Fiber>] fibers to supervise
|
175
|
+
# @option opts [Proc, nil] :on_done proc to call when a supervised fiber is terminated
|
176
|
+
# @option opts [Proc, nil] :on_error proc to call when a supervised fiber is terminated with an exception
|
177
|
+
# @option opts [:always, :on_error, nil] :restart whether to restart terminated fibers
|
178
|
+
# @yield [] supervisor block
|
178
179
|
# @return [void]
|
179
180
|
def supervise(*fibers, **opts, &block)
|
180
181
|
block ||= supervise_opts_to_block(opts)
|
@@ -198,7 +199,7 @@ module Polyphony
|
|
198
199
|
|
199
200
|
private
|
200
201
|
|
201
|
-
#
|
202
|
+
# @!visibility private
|
202
203
|
def supervise_opts_to_block(opts)
|
203
204
|
block = opts[:on_done] || opts[:on_error]
|
204
205
|
restart = opts[:restart]
|
@@ -228,7 +229,7 @@ module Polyphony
|
|
228
229
|
# terminates with an uncaught exception, `Fiber.await` will await all the
|
229
230
|
# other fibers to terminate, then reraise the exception.
|
230
231
|
#
|
231
|
-
# @param
|
232
|
+
# @param fibers [Array<Fiber>] fibers to wait for
|
232
233
|
# @return [Array<any>] return values of given fibers
|
233
234
|
def await(*fibers)
|
234
235
|
return [] if fibers.empty?
|
@@ -267,7 +268,7 @@ module Polyphony
|
|
267
268
|
# array containing the first terminated fiber and its return value. If an
|
268
269
|
# exception occurs in one of the given fibers, it will be reraised.
|
269
270
|
#
|
270
|
-
# @param
|
271
|
+
# @param fibers [Array<Fiber>] Fibers to wait for
|
271
272
|
# @return [Array] Array containing the first terminated fiber and its return value
|
272
273
|
def select(*fibers)
|
273
274
|
return nil if fibers.empty?
|
@@ -301,7 +302,7 @@ module Polyphony
|
|
301
302
|
# also be scheduled with priority. This method is mainly used trapping
|
302
303
|
# signals (see also the patched `Kernel#trap`)
|
303
304
|
#
|
304
|
-
# @
|
305
|
+
# @yield [] given block
|
305
306
|
# @return [void]
|
306
307
|
def schedule_priority_oob_fiber(&block)
|
307
308
|
oob_fiber = Fiber.new do
|
@@ -322,7 +323,7 @@ module Polyphony
|
|
322
323
|
|
323
324
|
private
|
324
325
|
|
325
|
-
#
|
326
|
+
# @!visibility private
|
326
327
|
def prepare_oob_fiber(fiber, block)
|
327
328
|
fiber.oob = true
|
328
329
|
fiber.tag = :oob
|
@@ -348,7 +349,7 @@ module Polyphony
|
|
348
349
|
#
|
349
350
|
# @param tag [any] child fiber's tag
|
350
351
|
# @param orig_caller [Array<String>] caller to set for fiber
|
351
|
-
# @
|
352
|
+
# @yield [any] child fiber's block
|
352
353
|
# @return [Fiber] child fiber
|
353
354
|
def spin(tag = nil, orig_caller = Kernel.caller, &block)
|
354
355
|
f = Fiber.new { |v| f.run(v) }
|
@@ -456,10 +457,11 @@ module Polyphony
|
|
456
457
|
|
457
458
|
# Removes a child fiber reference. Used internally.
|
458
459
|
#
|
459
|
-
# @param
|
460
|
+
# @param child_fiber [Fiber] child fiber to be removed
|
460
461
|
# @return [Fiber] self
|
461
462
|
def remove_child(child_fiber)
|
462
463
|
@children.delete(child_fiber) if @children
|
464
|
+
self
|
463
465
|
end
|
464
466
|
end
|
465
467
|
|
@@ -5,9 +5,10 @@ require 'open3'
|
|
5
5
|
# IO extensions
|
6
6
|
class ::IO
|
7
7
|
class << self
|
8
|
+
# @!visibility private
|
8
9
|
alias_method :orig_binread, :binread
|
9
10
|
|
10
|
-
#
|
11
|
+
# @!visibility private
|
11
12
|
def binread(name, length = nil, offset = nil)
|
12
13
|
File.open(name, 'rb:ASCII-8BIT') do |f|
|
13
14
|
f.seek(offset) if offset
|
@@ -15,7 +16,10 @@ class ::IO
|
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
19
|
+
# @!visibility private
|
18
20
|
alias_method :orig_binwrite, :binwrite
|
21
|
+
|
22
|
+
# @!visibility private
|
19
23
|
def binwrite(name, string, offset = nil)
|
20
24
|
File.open(name, 'wb:ASCII-8BIT') do |f|
|
21
25
|
f.seek(offset) if offset
|
@@ -23,24 +27,27 @@ class ::IO
|
|
23
27
|
end
|
24
28
|
end
|
25
29
|
|
30
|
+
# @!visibility private
|
26
31
|
EMPTY_HASH = {}.freeze
|
27
32
|
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
#
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
# end
|
33
|
+
# @!visibility private
|
34
|
+
alias_method :orig_foreach, :foreach
|
35
|
+
|
36
|
+
# @!visibility private
|
37
|
+
def foreach(name, sep = $/, limit = nil, getline_args = EMPTY_HASH, &block)
|
38
|
+
if sep.is_a?(Integer)
|
39
|
+
sep = $/
|
40
|
+
limit = sep
|
41
|
+
end
|
42
|
+
File.open(name, 'r') do |f|
|
43
|
+
f.each_line(sep, limit, chomp: getline_args[:chomp], &block)
|
44
|
+
end
|
45
|
+
end
|
42
46
|
|
47
|
+
# @!visibility private
|
43
48
|
alias_method :orig_read, :read
|
49
|
+
|
50
|
+
# @!visibility private
|
44
51
|
def read(name, length = nil, offset = nil, opt = EMPTY_HASH)
|
45
52
|
if length.is_a?(Hash)
|
46
53
|
opt = length
|
@@ -59,7 +66,10 @@ class ::IO
|
|
59
66
|
# end
|
60
67
|
# end
|
61
68
|
|
69
|
+
# @!visibility private
|
62
70
|
alias_method :orig_write, :write
|
71
|
+
|
72
|
+
# @!visibility private
|
63
73
|
def write(name, string, offset = nil, opt = EMPTY_HASH)
|
64
74
|
File.open(name, opt[:mode] || 'w') do |f|
|
65
75
|
f.seek(offset) if offset
|
@@ -67,22 +77,44 @@ class ::IO
|
|
67
77
|
end
|
68
78
|
end
|
69
79
|
|
80
|
+
# @!visibility private
|
70
81
|
alias_method :orig_popen, :popen
|
82
|
+
|
83
|
+
|
84
|
+
# @!visibility private
|
71
85
|
def popen(cmd, mode = 'r')
|
72
86
|
return orig_popen(cmd, mode) unless block_given?
|
73
87
|
|
74
88
|
Open3.popen2(cmd) { |_i, o, _t| yield o }
|
75
89
|
end
|
76
90
|
|
91
|
+
# Splices from one IO to another IO. At least one of the IOs must be a pipe.
|
92
|
+
#
|
93
|
+
# @param src [IO, Polyphony::Pipe] source to splice from
|
94
|
+
# @param dest [IO, Polyphony::Pipe] destination to splice to
|
95
|
+
# @param maxlen [Integer] maximum bytes to splice
|
96
|
+
# @return [Integer] bytes spliced
|
77
97
|
def splice(src, dest, maxlen)
|
78
98
|
Polyphony.backend_splice(src, dest, maxlen)
|
79
99
|
end
|
80
100
|
|
81
101
|
if RUBY_PLATFORM =~ /linux/
|
102
|
+
# Creates a pipe and splices data between the two given IOs using the
|
103
|
+
# pipe, splicing until EOF.
|
104
|
+
#
|
105
|
+
# @param src [IO, Polyphony::Pipe] source to splice from
|
106
|
+
# @param dest [IO, Polyphony::Pipe] destination to splice to
|
107
|
+
# @return [Integer] total bytes spliced
|
82
108
|
def double_splice(src, dest)
|
83
109
|
Polyphony.backend_double_splice(src, dest)
|
84
110
|
end
|
85
111
|
|
112
|
+
# Tees data from the source to the desination.
|
113
|
+
#
|
114
|
+
# @param src [IO, Polyphony::Pipe] source to tee from
|
115
|
+
# @param dest [IO, Polyphony::Pipe] destination to tee to
|
116
|
+
# @param maxlen [Integer] maximum bytes to tee
|
117
|
+
# @return [Integer] total bytes teed
|
86
118
|
def tee(src, dest, maxlen)
|
87
119
|
Polyphony.backend_tee(src, dest, maxlen)
|
88
120
|
end
|
@@ -92,10 +124,12 @@ end
|
|
92
124
|
|
93
125
|
# IO instance method patches
|
94
126
|
class ::IO
|
127
|
+
# @!visibility private
|
95
128
|
def __read_method__
|
96
129
|
:backend_read
|
97
130
|
end
|
98
131
|
|
132
|
+
# @!visibility private
|
99
133
|
def __write_method__
|
100
134
|
:backend_write
|
101
135
|
end
|
@@ -114,13 +148,21 @@ class ::IO
|
|
114
148
|
# def each_codepoint
|
115
149
|
# end
|
116
150
|
|
151
|
+
# @!visibility private
|
117
152
|
alias_method :orig_getbyte, :getbyte
|
153
|
+
|
154
|
+
|
155
|
+
# @!visibility private
|
118
156
|
def getbyte
|
119
157
|
char = getc
|
120
158
|
char ? char.getbyte(0) : nil
|
121
159
|
end
|
122
160
|
|
161
|
+
# @!visibility private
|
123
162
|
alias_method :orig_getc, :getc
|
163
|
+
|
164
|
+
|
165
|
+
# @!visibility private
|
124
166
|
def getc
|
125
167
|
return @read_buffer.slice!(0) if @read_buffer && !@read_buffer.empty?
|
126
168
|
|
@@ -131,6 +173,7 @@ class ::IO
|
|
131
173
|
nil
|
132
174
|
end
|
133
175
|
|
176
|
+
# @!visibility private
|
134
177
|
def ungetc(c)
|
135
178
|
c = c.chr if c.is_a?(Integer)
|
136
179
|
if @read_buffer
|
@@ -141,7 +184,11 @@ class ::IO
|
|
141
184
|
end
|
142
185
|
alias_method :ungetbyte, :ungetc
|
143
186
|
|
187
|
+
# @!visibility private
|
144
188
|
alias_method :orig_read, :read
|
189
|
+
|
190
|
+
|
191
|
+
# @!visibility private
|
145
192
|
def read(len = nil, buf = nil, buf_pos = 0)
|
146
193
|
return '' if len == 0
|
147
194
|
|
@@ -158,7 +205,10 @@ class ::IO
|
|
158
205
|
already_read
|
159
206
|
end
|
160
207
|
|
208
|
+
# @!visibility private
|
161
209
|
alias_method :orig_readpartial, :read
|
210
|
+
|
211
|
+
# @!visibility private
|
162
212
|
def readpartial(len, str = +'', buffer_pos = 0, raise_on_eof = true)
|
163
213
|
result = Polyphony.backend_read(self, str, len, false, buffer_pos)
|
164
214
|
raise EOFError if !result && raise_on_eof
|
@@ -166,18 +216,27 @@ class ::IO
|
|
166
216
|
result
|
167
217
|
end
|
168
218
|
|
219
|
+
# @!visibility private
|
169
220
|
alias_method :orig_write, :write
|
221
|
+
|
222
|
+
# @!visibility private
|
170
223
|
def write(str, *args)
|
171
224
|
Polyphony.backend_write(self, str, *args)
|
172
225
|
end
|
173
226
|
|
227
|
+
# @!visibility private
|
174
228
|
alias_method :orig_write_chevron, :<<
|
229
|
+
|
230
|
+
# @!visibility private
|
175
231
|
def <<(str)
|
176
232
|
Polyphony.backend_write(self, str)
|
177
233
|
self
|
178
234
|
end
|
179
|
-
|
235
|
+
|
236
|
+
# @!visibility private
|
180
237
|
alias_method :orig_gets, :gets
|
238
|
+
|
239
|
+
# @!visibility private
|
181
240
|
def gets(sep = $/, _limit = nil, _chomp: nil)
|
182
241
|
if sep.is_a?(Integer)
|
183
242
|
sep = $/
|
@@ -198,6 +257,31 @@ class ::IO
|
|
198
257
|
return nil
|
199
258
|
end
|
200
259
|
|
260
|
+
# @!visibility private
|
261
|
+
def each_line(sep = $/, limit = nil, chomp: false)
|
262
|
+
if sep.is_a?(Integer)
|
263
|
+
limit = sep
|
264
|
+
sep = $/
|
265
|
+
end
|
266
|
+
sep_size = sep.bytesize
|
267
|
+
|
268
|
+
|
269
|
+
@read_buffer ||= +''
|
270
|
+
|
271
|
+
while true
|
272
|
+
while (idx = @read_buffer.index(sep))
|
273
|
+
line = @read_buffer.slice!(0, idx + sep_size)
|
274
|
+
line = line.chomp if chomp
|
275
|
+
yield line
|
276
|
+
end
|
277
|
+
|
278
|
+
result = readpartial(8192, @read_buffer, -1)
|
279
|
+
return self if !result
|
280
|
+
end
|
281
|
+
rescue EOFError
|
282
|
+
return self
|
283
|
+
end
|
284
|
+
|
201
285
|
# def print(*args)
|
202
286
|
# end
|
203
287
|
|
@@ -207,10 +291,15 @@ class ::IO
|
|
207
291
|
# def putc(obj)
|
208
292
|
# end
|
209
293
|
|
294
|
+
# @!visibility private
|
210
295
|
LINEFEED = "\n"
|
296
|
+
# @!visibility private
|
211
297
|
LINEFEED_RE = /\n$/.freeze
|
212
298
|
|
299
|
+
# @!visibility private
|
213
300
|
alias_method :orig_puts, :puts
|
301
|
+
|
302
|
+
# @!visibility private
|
214
303
|
def puts(*args)
|
215
304
|
if args.empty?
|
216
305
|
write LINEFEED
|
@@ -245,24 +334,66 @@ class ::IO
|
|
245
334
|
# def readlines(sep = $/, limit = nil, chomp: nil)
|
246
335
|
# end
|
247
336
|
|
337
|
+
# @!visibility private
|
248
338
|
alias_method :orig_write_nonblock, :write_nonblock
|
339
|
+
|
340
|
+
# @!visibility private
|
249
341
|
def write_nonblock(string, _options = {})
|
250
342
|
write(string)
|
251
343
|
end
|
252
344
|
|
345
|
+
# @!visibility private
|
253
346
|
alias_method :orig_read_nonblock, :read_nonblock
|
347
|
+
|
348
|
+
# @!visibility private
|
254
349
|
def read_nonblock(maxlen, buf = nil, _options = nil)
|
255
350
|
buf ? readpartial(maxlen, buf) : readpartial(maxlen)
|
256
351
|
end
|
257
352
|
|
353
|
+
# call-seq:
|
354
|
+
# io.read_loop { |data| ... }
|
355
|
+
# io.read_loop(maxlen) { |data| ... }
|
356
|
+
#
|
357
|
+
# Reads up to `maxlen` bytes at a time in an infinite loop. Read data
|
358
|
+
# will be passed to the given block.
|
359
|
+
#
|
360
|
+
# @param maxlen [Integer] maximum bytes to receive
|
361
|
+
# @yield [String] handler block
|
362
|
+
# @return [void]
|
258
363
|
def read_loop(maxlen = 8192, &block)
|
259
364
|
Polyphony.backend_read_loop(self, maxlen, &block)
|
260
365
|
end
|
261
366
|
|
367
|
+
# call-seq:
|
368
|
+
# io.feed_loop(receiver, method)
|
369
|
+
# io.feed_loop(receiver, method) { |result| ... }
|
370
|
+
#
|
371
|
+
# Receives data from the io in an infinite loop, passing the data to the given
|
372
|
+
# receiver using the given method. If a block is given, the result of the
|
373
|
+
# method call to the receiver is passed to the block.
|
374
|
+
#
|
375
|
+
# This method can be used to feed data into parser objects. The following
|
376
|
+
# example shows how to feed data from a io directly into a MessagePack
|
377
|
+
# unpacker:
|
378
|
+
#
|
379
|
+
# unpacker = MessagePack::Unpacker.new
|
380
|
+
# buffer = []
|
381
|
+
# reader = spin do
|
382
|
+
# io.feed_loop(unpacker, :feed_each) { |msg| handle_msg(msg) }
|
383
|
+
# end
|
384
|
+
#
|
385
|
+
# @param receiver [any] receiver object
|
386
|
+
# @param method [Symbol] method to call
|
387
|
+
# @yield [any] block to handle result of method call to receiver
|
388
|
+
# @return [void]
|
262
389
|
def feed_loop(receiver, method = :call, &block)
|
263
390
|
Polyphony.backend_feed_loop(self, receiver, method, &block)
|
264
391
|
end
|
265
392
|
|
393
|
+
# Waits for the IO to become readable, with an optional timeout.
|
394
|
+
#
|
395
|
+
# @param timeout [Integer, nil] optional timeout in seconds.
|
396
|
+
# @return [IO] self
|
266
397
|
def wait_readable(timeout = nil)
|
267
398
|
return self if @read_buffer && @read_buffer.size > 0
|
268
399
|
|
@@ -277,6 +408,10 @@ class ::IO
|
|
277
408
|
end
|
278
409
|
end
|
279
410
|
|
411
|
+
# Waits for the IO to become writeable, with an optional timeout.
|
412
|
+
#
|
413
|
+
# @param timeout [Integer, nil] optional timeout in seconds.
|
414
|
+
# @return [IO] self
|
280
415
|
def wait_writable(timeout = nil)
|
281
416
|
if timeout
|
282
417
|
move_on_after(timeout) do
|
@@ -289,11 +424,21 @@ class ::IO
|
|
289
424
|
end
|
290
425
|
end
|
291
426
|
|
427
|
+
# Splices data from the given IO.
|
428
|
+
#
|
429
|
+
# @param src [IO, Polpyhony::Pipe] source to splice from
|
430
|
+
# @param maxlen [Integer] maximum bytes to splice
|
431
|
+
# @return [Integer] bytes spliced
|
292
432
|
def splice_from(src, maxlen)
|
293
433
|
Polyphony.backend_splice(src, self, maxlen)
|
294
434
|
end
|
295
435
|
|
296
436
|
if RUBY_PLATFORM =~ /linux/
|
437
|
+
# Tees data from the given IO.
|
438
|
+
#
|
439
|
+
# @param src [IO, Polpyhony::Pipe] source to tee from
|
440
|
+
# @param maxlen [Integer] maximum bytes to tee
|
441
|
+
# @return [Integer] bytes teed
|
297
442
|
def tee_from(src, maxlen)
|
298
443
|
Polyphony.backend_tee(src, self, maxlen)
|
299
444
|
end
|