polyphony 0.99.4 → 0.99.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|