polyphony 0.99.4 → 0.99.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +11 -0
- data/.yardopts +0 -2
- data/README.md +1 -1
- data/docs/readme.md +1 -1
- data/docs/tutorial.md +2 -2
- data/examples/pipes/gzip_http_server.rb +2 -2
- data/examples/pipes/http_server.rb +1 -1
- data/examples/pipes/tcp_proxy.rb +1 -1
- data/ext/polyphony/backend_common.c +4 -4
- data/ext/polyphony/backend_io_uring.c +8 -8
- data/ext/polyphony/backend_libev.c +5 -5
- data/ext/polyphony/fiber.c +33 -42
- data/ext/polyphony/io_extensions.c +50 -37
- data/ext/polyphony/pipe.c +6 -20
- data/ext/polyphony/polyphony.c +72 -144
- data/ext/polyphony/queue.c +23 -63
- data/ext/polyphony/thread.c +4 -13
- data/lib/polyphony/adapters/process.rb +2 -5
- data/lib/polyphony/adapters/sequel.rb +2 -2
- data/lib/polyphony/core/debug.rb +1 -4
- data/lib/polyphony/core/exceptions.rb +1 -5
- data/lib/polyphony/core/resource_pool.rb +7 -8
- data/lib/polyphony/core/sync.rb +5 -8
- data/lib/polyphony/core/thread_pool.rb +3 -10
- data/lib/polyphony/core/throttler.rb +1 -5
- data/lib/polyphony/core/timer.rb +23 -30
- data/lib/polyphony/extensions/fiber.rb +513 -543
- data/lib/polyphony/extensions/io.rb +5 -14
- data/lib/polyphony/extensions/object.rb +283 -2
- data/lib/polyphony/extensions/openssl.rb +5 -26
- data/lib/polyphony/extensions/pipe.rb +6 -17
- data/lib/polyphony/extensions/socket.rb +24 -118
- data/lib/polyphony/extensions/thread.rb +3 -18
- data/lib/polyphony/extensions/timeout.rb +0 -1
- data/lib/polyphony/net.rb +5 -9
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony.rb +2 -6
- data/test/test_io.rb +221 -221
- data/test/test_socket.rb +3 -3
- data/test/test_trace.rb +2 -2
- metadata +5 -9
- data/docs/index.md +0 -94
- data/docs/link_rewriter.rb +0 -17
- data/docs/main-concepts/index.md +0 -9
- data/lib/polyphony/core/global_api.rb +0 -309
- /data/{assets → docs/assets}/echo-fibers.svg +0 -0
- /data/{assets → docs/assets}/polyphony-logo.png +0 -0
- /data/{assets → docs/assets}/sleeping-fiber.svg +0 -0
data/ext/polyphony/thread.c
CHANGED
@@ -19,11 +19,8 @@ inline void schedule_fiber(VALUE self, VALUE fiber, VALUE value, int prioritize)
|
|
19
19
|
Backend_schedule_fiber(self, rb_ivar_get(self, ID_ivar_backend), fiber, value, prioritize);
|
20
20
|
}
|
21
21
|
|
22
|
-
/*
|
23
|
-
* thread.unschedule_fiber(fiber)
|
22
|
+
/* Removes the given fiber from the thread's runqueue.
|
24
23
|
*
|
25
|
-
* Removes the given fiber from the thread's runqueue.
|
26
|
-
*
|
27
24
|
* @param fiber [Fiber] fiber to unschedule
|
28
25
|
* @return [Thread] self
|
29
26
|
*/
|
@@ -45,12 +42,9 @@ inline void Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE v
|
|
45
42
|
// schedule_fiber(self, fiber, value, 1);
|
46
43
|
}
|
47
44
|
|
48
|
-
/*
|
49
|
-
* thread.switch_fiber()
|
45
|
+
/* Switches to the next fiber in the thread's runqueue.
|
50
46
|
*
|
51
|
-
*
|
52
|
-
*
|
53
|
-
* @return [void]
|
47
|
+
* @return [any] resume value
|
54
48
|
*/
|
55
49
|
|
56
50
|
VALUE Thread_switch_fiber(VALUE self) {
|
@@ -80,11 +74,8 @@ VALUE Thread_debug(VALUE self) {
|
|
80
74
|
return self;
|
81
75
|
}
|
82
76
|
|
83
|
-
/*
|
84
|
-
* Thread.backend
|
77
|
+
/* Returns the backend for the current thread.
|
85
78
|
*
|
86
|
-
* Returns the backend for the current thread.
|
87
|
-
*
|
88
79
|
* @return [Polyphony::Backend] backend for the current thread
|
89
80
|
*/
|
90
81
|
|
@@ -8,13 +8,12 @@ module Polyphony
|
|
8
8
|
# Watches a forked or spawned process, waiting for it to terminate. If
|
9
9
|
# `cmd` is given it is spawned, otherwise the process is forked with the
|
10
10
|
# given block.
|
11
|
-
#
|
11
|
+
#
|
12
12
|
# If the operation is interrupted for any reason, the spawned or forked
|
13
13
|
# process is killed.
|
14
14
|
#
|
15
15
|
# @param cmd [String, nil] command to spawn
|
16
|
-
# @
|
17
|
-
# @return [void]
|
16
|
+
# @return [true]
|
18
17
|
def watch(cmd = nil, &block)
|
19
18
|
terminated = nil
|
20
19
|
pid = cmd ? Kernel.spawn(cmd) : Polyphony.fork(&block)
|
@@ -28,7 +27,6 @@ module Polyphony
|
|
28
27
|
# seconds.
|
29
28
|
#
|
30
29
|
# @param pid [Integer] pid
|
31
|
-
# @return [void]
|
32
30
|
def kill_process(pid)
|
33
31
|
cancel_after(5) do
|
34
32
|
kill_and_await('TERM', pid)
|
@@ -43,7 +41,6 @@ module Polyphony
|
|
43
41
|
#
|
44
42
|
# @param sig [String, Symbol, Integer] signal to use
|
45
43
|
# @param pid [Integer] pid
|
46
|
-
# @return [void]
|
47
44
|
def kill_and_await(sig, pid)
|
48
45
|
::Process.kill(sig, pid)
|
49
46
|
Polyphony.backend_waitpid(pid)
|
@@ -4,10 +4,10 @@ require_relative '../../polyphony'
|
|
4
4
|
require 'sequel'
|
5
5
|
|
6
6
|
module Polyphony
|
7
|
-
|
7
|
+
|
8
8
|
# Sequel ConnectionPool that delegates to Polyphony::ResourcePool.
|
9
9
|
class FiberConnectionPool < Sequel::ConnectionPool
|
10
|
-
|
10
|
+
|
11
11
|
# Initializes the connection pool.
|
12
12
|
#
|
13
13
|
# @param db [any] db to connect to
|
data/lib/polyphony/core/debug.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# Kernel extensions
|
2
2
|
module ::Kernel
|
3
3
|
# Prints a trace message to `STDOUT`, bypassing the Polyphony backend.
|
4
|
-
#
|
5
|
-
# @return [void]
|
6
4
|
def trace(*args)
|
7
5
|
STDOUT.orig_write(format_trace(args))
|
8
6
|
end
|
@@ -32,8 +30,7 @@ module Polyphony
|
|
32
30
|
# If an IO instance is given, events are dumped to it instead.
|
33
31
|
#
|
34
32
|
# @param io [IO, nil] IO instance
|
35
|
-
# @yield [Hash] event
|
36
|
-
# @return [void]
|
33
|
+
# @yield [Hash] event information
|
37
34
|
def start_event_firehose(io = nil, &block)
|
38
35
|
Thread.backend.trace_proc = firehose_proc(io, block)
|
39
36
|
end
|
@@ -16,7 +16,6 @@ module Polyphony
|
|
16
16
|
# Initializes the exception, setting the caller and the value.
|
17
17
|
#
|
18
18
|
# @param value [any] Exception value
|
19
|
-
# @return [void]
|
20
19
|
def initialize(value = nil)
|
21
20
|
@caller_backtrace = caller
|
22
21
|
@value = value
|
@@ -40,18 +39,15 @@ module Polyphony
|
|
40
39
|
|
41
40
|
# Interjection is used to run arbitrary code on arbitrary fibers at any point
|
42
41
|
class Interjection < BaseException
|
43
|
-
|
42
|
+
|
44
43
|
# Initializes an Interjection with the given proc.
|
45
44
|
#
|
46
45
|
# @param proc [Proc] interjection proc
|
47
|
-
# @return [void]
|
48
46
|
def initialize(proc)
|
49
47
|
@proc = proc
|
50
48
|
end
|
51
49
|
|
52
50
|
# Invokes the exception by calling the associated proc.
|
53
|
-
#
|
54
|
-
# @return [void]
|
55
51
|
def invoke
|
56
52
|
@proc.call
|
57
53
|
end
|
@@ -5,13 +5,15 @@ module Polyphony
|
|
5
5
|
class ResourcePool
|
6
6
|
attr_reader :limit, :size
|
7
7
|
|
8
|
-
# Initializes a new resource pool.
|
8
|
+
# Initializes a new resource pool. The given block is used for creating a
|
9
|
+
# resource:
|
9
10
|
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
|
11
|
+
# ResourcePool.new { Sequel.connect(DB_URL) }
|
12
|
+
#
|
13
|
+
# @param limit [Integer] maximum open resources
|
14
|
+
def initialize(limit: 4, &block)
|
13
15
|
@allocator = block
|
14
|
-
@limit =
|
16
|
+
@limit = limit
|
15
17
|
@size = 0
|
16
18
|
@stock = Polyphony::Queue.new
|
17
19
|
@acquired_resources = {}
|
@@ -36,7 +38,6 @@ module Polyphony
|
|
36
38
|
# db.query(sql).to_a
|
37
39
|
# end
|
38
40
|
#
|
39
|
-
# @yield [any] code to run
|
40
41
|
# @return [any] return value of block
|
41
42
|
def acquire(&block)
|
42
43
|
fiber = Fiber.current
|
@@ -55,7 +56,6 @@ module Polyphony
|
|
55
56
|
#
|
56
57
|
# @param sym [Symbol] method name
|
57
58
|
# @param args [Array<any>] method arguments
|
58
|
-
# @yield [any] block passed to method
|
59
59
|
# @return [any] result of method call
|
60
60
|
def method_missing(sym, *args, &block)
|
61
61
|
acquire { |r| r.send(sym, *args, &block) }
|
@@ -88,7 +88,6 @@ module Polyphony
|
|
88
88
|
# Acquires a resource from stock, yielding it to the given block.
|
89
89
|
#
|
90
90
|
# @param fiber [Fiber] the fiber the resource will be associated with
|
91
|
-
# @yield [any] given block
|
92
91
|
# @return [any] return value of block
|
93
92
|
def acquire_from_stock(fiber)
|
94
93
|
add_to_stock if (@stock.empty? || @stock.pending?) && @size < @limit
|
data/lib/polyphony/core/sync.rb
CHANGED
@@ -17,7 +17,6 @@ module Polyphony
|
|
17
17
|
# This method is re-entrant. Recursive calls from the given block will not
|
18
18
|
# block.
|
19
19
|
#
|
20
|
-
# @yield [] code to run
|
21
20
|
# @return [any] return value of block
|
22
21
|
def synchronize(&block)
|
23
22
|
return yield if @holding_fiber == Fiber.current
|
@@ -28,7 +27,7 @@ module Polyphony
|
|
28
27
|
# Conditionally releases the mutex. This method is used by condition
|
29
28
|
# variables.
|
30
29
|
#
|
31
|
-
# @return [
|
30
|
+
# @return [nil]
|
32
31
|
def conditional_release
|
33
32
|
@store << @token
|
34
33
|
@token = nil
|
@@ -38,7 +37,7 @@ module Polyphony
|
|
38
37
|
# Conditionally reacquires the mutex. This method is used by condition
|
39
38
|
# variables.
|
40
39
|
#
|
41
|
-
# @return [
|
40
|
+
# @return [Fiber] current fiber
|
42
41
|
def conditional_reacquire
|
43
42
|
@token = @store.shift
|
44
43
|
@holding_fiber = Fiber.current
|
@@ -64,7 +63,7 @@ module Polyphony
|
|
64
63
|
# @return [Mutex] self
|
65
64
|
def lock
|
66
65
|
raise ThreadError if owned?
|
67
|
-
|
66
|
+
|
68
67
|
@token = @store.shift
|
69
68
|
@holding_fiber = Fiber.current
|
70
69
|
self
|
@@ -141,7 +140,7 @@ module Polyphony
|
|
141
140
|
#
|
142
141
|
# @param mutex [Polyphony::Mutex] mutex to release while waiting for signal
|
143
142
|
# @param _timeout [Number, nil] timeout in seconds (currently not implemented)
|
144
|
-
# @return [
|
143
|
+
# @return [any]
|
145
144
|
def wait(mutex, _timeout = nil)
|
146
145
|
mutex.conditional_release
|
147
146
|
@queue << Fiber.current
|
@@ -152,7 +151,7 @@ module Polyphony
|
|
152
151
|
# Signals the condition variable, causing the first fiber in the waiting
|
153
152
|
# queue to be resumed.
|
154
153
|
#
|
155
|
-
# @return [
|
154
|
+
# @return [Fiber] resumed fiber
|
156
155
|
def signal
|
157
156
|
return if @queue.empty?
|
158
157
|
|
@@ -161,8 +160,6 @@ module Polyphony
|
|
161
160
|
end
|
162
161
|
|
163
162
|
# Resumes all waiting fibers.
|
164
|
-
#
|
165
|
-
# @return [void]
|
166
163
|
def broadcast
|
167
164
|
return if @queue.empty?
|
168
165
|
|
@@ -3,16 +3,15 @@
|
|
3
3
|
require 'etc'
|
4
4
|
|
5
5
|
module Polyphony
|
6
|
-
|
6
|
+
|
7
7
|
# Implements a pool of threads
|
8
8
|
class ThreadPool
|
9
|
-
|
9
|
+
|
10
10
|
# The pool size.
|
11
11
|
attr_reader :size
|
12
12
|
|
13
13
|
# Runs the given block on an available thread from the default thread pool.
|
14
14
|
#
|
15
|
-
# @yield [] given block
|
16
15
|
# @return [any] return value of given block
|
17
16
|
def self.process(&block)
|
18
17
|
@default_pool ||= new
|
@@ -21,7 +20,7 @@ module Polyphony
|
|
21
20
|
|
22
21
|
# Resets the default thread pool.
|
23
22
|
#
|
24
|
-
# @return [
|
23
|
+
# @return [nil]
|
25
24
|
def self.reset
|
26
25
|
return unless @default_pool
|
27
26
|
|
@@ -41,7 +40,6 @@ module Polyphony
|
|
41
40
|
|
42
41
|
# Runs the given block on an available thread from the pool.
|
43
42
|
#
|
44
|
-
# @yield [] given block
|
45
43
|
# @return [any] return value of block
|
46
44
|
def process(&block)
|
47
45
|
setup unless @task_queue
|
@@ -55,7 +53,6 @@ module Polyphony
|
|
55
53
|
# method does not block. The task will be performed once a thread becomes
|
56
54
|
# available.
|
57
55
|
#
|
58
|
-
# @yield [] given block
|
59
56
|
# @return [Polyphony::ThreadPool] self
|
60
57
|
def cast(&block)
|
61
58
|
setup unless @task_queue
|
@@ -81,8 +78,6 @@ module Polyphony
|
|
81
78
|
private
|
82
79
|
|
83
80
|
# Runs a processing loop on a worker thread.
|
84
|
-
#
|
85
|
-
# @return [void]
|
86
81
|
def thread_loop
|
87
82
|
while true
|
88
83
|
run_queued_task
|
@@ -90,8 +85,6 @@ module Polyphony
|
|
90
85
|
end
|
91
86
|
|
92
87
|
# Runs the first queued task in the task queue.
|
93
|
-
#
|
94
|
-
# @return [void]
|
95
88
|
def run_queued_task
|
96
89
|
(block, watcher) = @task_queue.shift
|
97
90
|
result = block.()
|
@@ -13,10 +13,6 @@ module Polyphony
|
|
13
13
|
@next_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
14
14
|
end
|
15
15
|
|
16
|
-
# call-seq:
|
17
|
-
# throttler.call { ... }
|
18
|
-
# throttler.process { ... }
|
19
|
-
#
|
20
16
|
# Invokes the throttler with the given block. The throttler will
|
21
17
|
# automatically introduce a delay to keep to the maximum specified rate.
|
22
18
|
# The throttler instance is passed to the given block.
|
@@ -32,7 +28,7 @@ module Polyphony
|
|
32
28
|
@next_time += @min_dt
|
33
29
|
break if @next_time > now
|
34
30
|
end
|
35
|
-
|
31
|
+
|
36
32
|
result
|
37
33
|
end
|
38
34
|
alias_method :process, :call
|
data/lib/polyphony/core/timer.rb
CHANGED
@@ -28,7 +28,6 @@ module Polyphony
|
|
28
28
|
# Sleeps for the given duration.
|
29
29
|
#
|
30
30
|
# @param duration [Number] sleep duration in seconds
|
31
|
-
# @return [void]
|
32
31
|
def sleep(duration)
|
33
32
|
fiber = Fiber.current
|
34
33
|
@timeouts[fiber] = {
|
@@ -44,7 +43,6 @@ module Polyphony
|
|
44
43
|
# given delay.
|
45
44
|
#
|
46
45
|
# @param interval [Number] delay in seconds before running the given block
|
47
|
-
# @yield [] block to run
|
48
46
|
# @return [Fiber] spun fiber
|
49
47
|
def after(interval, &block)
|
50
48
|
spin do
|
@@ -57,8 +55,6 @@ module Polyphony
|
|
57
55
|
# consecutive iterations.
|
58
56
|
#
|
59
57
|
# @param interval [Number] interval between consecutive iterations in seconds
|
60
|
-
# @yield [] block to run
|
61
|
-
# @return [void]
|
62
58
|
def every(interval)
|
63
59
|
fiber = Fiber.current
|
64
60
|
@timeouts[fiber] = {
|
@@ -74,14 +70,6 @@ module Polyphony
|
|
74
70
|
@timeouts.delete(fiber)
|
75
71
|
end
|
76
72
|
|
77
|
-
# call-seq:
|
78
|
-
# timer.cancel_after(interval) { ... }
|
79
|
-
# timer.cancel_after(interval, with_exception: exception) { ... }
|
80
|
-
# timer.cancel_after(interval, with_exception: [klass, message]) { ... }
|
81
|
-
# timer.cancel_after(interval) { |timeout| ... }
|
82
|
-
# timer.cancel_after(interval, with_exception: exception) { |timeout| ... }
|
83
|
-
# timer.cancel_after(interval, with_exception: [klass, message]) { |timeout| ... }
|
84
|
-
#
|
85
73
|
# Runs the given block after setting up a cancellation timer for
|
86
74
|
# cancellation. If the cancellation timer elapses, the execution will be
|
87
75
|
# interrupted with an exception defaulting to `Polyphony::Cancel`.
|
@@ -108,10 +96,20 @@ module Polyphony
|
|
108
96
|
# end
|
109
97
|
# end
|
110
98
|
#
|
111
|
-
# @
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
99
|
+
# @overload cancel_after(interval)
|
100
|
+
# @param interval [Number] timout in seconds
|
101
|
+
# @yield [Fiber] timeout fiber
|
102
|
+
# @return [any] block's return value
|
103
|
+
# @overload cancel_after(interval, with_exception: exception)
|
104
|
+
# @param interval [Number] timout in seconds
|
105
|
+
# @param with_exception [Class, Exception] exception or exception class
|
106
|
+
# @yield [Fiber] timeout fiber
|
107
|
+
# @return [any] block's return value
|
108
|
+
# @overload cancel_after(interval, with_exception: [klass, message])
|
109
|
+
# @param interval [Number] timout in seconds
|
110
|
+
# @param with_exception [Array] array containing class and message to use as exception
|
111
|
+
# @yield [Fiber] timeout fiber
|
112
|
+
# @return [any] block's return value
|
115
113
|
def cancel_after(interval, with_exception: Polyphony::Cancel)
|
116
114
|
fiber = Fiber.current
|
117
115
|
@timeouts[fiber] = {
|
@@ -124,12 +122,6 @@ module Polyphony
|
|
124
122
|
@timeouts.delete(fiber)
|
125
123
|
end
|
126
124
|
|
127
|
-
# call-seq:
|
128
|
-
# timer.move_on_after(interval) { ... }
|
129
|
-
# timer.move_on_after(interval, with_value: value) { ... }
|
130
|
-
# timer.move_on_after(interval) { |canceller| ... }
|
131
|
-
# timer.move_on_after(interval, with_value: value) { |canceller| ... }
|
132
|
-
#
|
133
125
|
# Runs the given block after setting up a cancellation timer for
|
134
126
|
# cancellation. If the cancellation timer elapses, the execution will be
|
135
127
|
# interrupted with a `Polyphony::MoveOn` exception, which will be rescued,
|
@@ -162,10 +154,15 @@ module Polyphony
|
|
162
154
|
# end
|
163
155
|
# end
|
164
156
|
#
|
165
|
-
# @
|
166
|
-
#
|
167
|
-
#
|
168
|
-
#
|
157
|
+
# @overload move_on_after(interval) { ... }
|
158
|
+
# @param interval [Number] timout in seconds
|
159
|
+
# @yield [Fiber] timeout fiber
|
160
|
+
# @return [any] block's return value
|
161
|
+
# @overload move_on_after(interval, with_value: value) { ... }
|
162
|
+
# @param interval [Number] timout in seconds
|
163
|
+
# @param with_value [any] return value in case of timeout
|
164
|
+
# @yield [Fiber] timeout fiber
|
165
|
+
# @return [any] block's return value
|
169
166
|
def move_on_after(interval, with_value: nil)
|
170
167
|
fiber = Fiber.current
|
171
168
|
@timeouts[fiber] = {
|
@@ -181,8 +178,6 @@ module Polyphony
|
|
181
178
|
end
|
182
179
|
|
183
180
|
# Resets the timeout for the current fiber.
|
184
|
-
#
|
185
|
-
# @return [void]
|
186
181
|
def reset
|
187
182
|
record = @timeouts[Fiber.current]
|
188
183
|
return unless record
|
@@ -217,8 +212,6 @@ module Polyphony
|
|
217
212
|
end
|
218
213
|
|
219
214
|
# Runs a timer iteration, invoking any timeouts that are due.
|
220
|
-
#
|
221
|
-
# @return [void]
|
222
215
|
def update
|
223
216
|
return if @timeouts.empty?
|
224
217
|
|