polyphony 0.95 → 0.97
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/workflows/test.yml +1 -1
- data/.github/workflows/test_io_uring.yml +4 -4
- data/CHANGELOG.md +9 -0
- data/Rakefile +1 -1
- data/examples/adapters/redis_blpop.rb +4 -3
- data/examples/adapters/redis_channels.rb +16 -7
- data/examples/core/await.rb +1 -1
- data/examples/io/https_server.rb +2 -1
- data/examples/io/https_server_sni.rb +31 -0
- data/examples/io/https_server_sni_2.rb +41 -0
- data/examples/io/readline.rb +19 -0
- data/examples/pipes/http_server.rb +4 -1
- data/ext/polyphony/backend_common.c +22 -0
- data/ext/polyphony/backend_libev.c +2 -2
- data/lib/polyphony/adapters/irb.rb +2 -42
- data/lib/polyphony/adapters/readline.rb +6 -4
- data/lib/polyphony/adapters/redis.rb +28 -87
- data/lib/polyphony/core/channel.rb +15 -0
- data/lib/polyphony/core/sync.rb +4 -0
- data/lib/polyphony/debugger.rb +2 -2
- data/lib/polyphony/extensions/io.rb +12 -0
- data/lib/polyphony/extensions.rb +0 -1
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony.rb +4 -0
- data/polyphony.gemspec +8 -8
- data/test/helper.rb +0 -5
- data/test/test_ext.rb +63 -0
- data/test/test_thread_pool.rb +2 -2
- metadata +22 -33
- data/lib/polyphony/extensions/enumerator.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a8218fc4160e0772ee3aada1f72ea9d063724ccf9b0aee7dfa6e9e4d69ebfe4
|
4
|
+
data.tar.gz: 74aae84e991cd4330ad282553061b29e71a6434ce2dafc9985c62feb3a966a68
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 772d0be4ef2f723fa27cc5ecb8ef9e9b5d012c501a364ab23dce7d145fc6d156d5e219e52e871a2aadb41a397c5fb044c8aaee9b42906f4894ff3447af0e93ba
|
7
|
+
data.tar.gz: b74d6958ea8a77ffee45aed8184091b5ef54cb4fcaca9798351533b13d2f2202f28ab95f56a26e09692bf40f00e723d2df9c99007dbe10631d1c69c523fb3a58
|
data/.github/workflows/test.yml
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
name: Tests
|
1
|
+
name: Tests (io_uring)
|
2
2
|
|
3
3
|
on: [push, pull_request]
|
4
4
|
|
@@ -8,7 +8,7 @@ jobs:
|
|
8
8
|
fail-fast: false
|
9
9
|
matrix:
|
10
10
|
os: [ubuntu-latest]
|
11
|
-
ruby: ['
|
11
|
+
ruby: ['3.0', '3.1', '3.2']
|
12
12
|
|
13
13
|
name: >-
|
14
14
|
${{matrix.os}}, ${{matrix.ruby}}
|
@@ -16,7 +16,7 @@ jobs:
|
|
16
16
|
runs-on: ${{matrix.os}}
|
17
17
|
steps:
|
18
18
|
- name: Checkout repository and submodules
|
19
|
-
uses: actions/checkout@
|
19
|
+
uses: actions/checkout@v3
|
20
20
|
with:
|
21
21
|
submodules: recursive
|
22
22
|
- name: Setup Ruby
|
@@ -33,4 +33,4 @@ jobs:
|
|
33
33
|
- name: Compile C-extension
|
34
34
|
run: bundle exec rake compile
|
35
35
|
- name: Run tests
|
36
|
-
run: bundle exec ruby test/run.rb
|
36
|
+
run: bundle exec ruby test/run.rb
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## 0.97 2023-02-28
|
2
|
+
|
3
|
+
- Fix working with IRB (#5)
|
4
|
+
|
5
|
+
## 0.96 2023-02-11
|
6
|
+
- Rework Redis client adapter
|
7
|
+
- Fix working with Pry (#95, thanks @floriandejonckheere)
|
8
|
+
- Improve compatibility with Enumerator and other APIs.
|
9
|
+
|
1
10
|
## 0.95 2023-01-16
|
2
11
|
|
3
12
|
- Remove support for Ruby 2.7
|
data/Rakefile
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
4
|
-
|
5
|
-
|
3
|
+
require 'redis'
|
4
|
+
require_relative '../../lib/polyphony/adapters/redis'
|
5
|
+
require_relative '../../lib/polyphony'
|
6
|
+
|
6
7
|
|
7
8
|
redis = Redis.new(host: ENV['REDISHOST'] || 'localhost')
|
8
9
|
|
@@ -1,7 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
4
|
-
|
3
|
+
require 'redis'
|
4
|
+
require_relative '../../lib/polyphony/adapters/redis'
|
5
|
+
require_relative '../../lib/polyphony'
|
6
|
+
require_relative '../../lib/polyphony/core/channel'
|
5
7
|
|
6
8
|
class RedisChannel < Polyphony::Channel
|
7
9
|
def self.publish_connection
|
@@ -17,20 +19,24 @@ class RedisChannel < Polyphony::Channel
|
|
17
19
|
def self.start_monitor
|
18
20
|
@channels = {}
|
19
21
|
@monitor = spin do
|
22
|
+
p start_monitor: 1
|
20
23
|
subscribe_connection.subscribe(CHANNEL_MASTER_TOPIC) do |on|
|
24
|
+
p start_monitor: 2
|
21
25
|
on.message do |topic, message|
|
26
|
+
p start_monitor: 3, topic: topic, message: message
|
22
27
|
message = Marshal.load(message)
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
+
if CHANNEL_MASTER_TOPIC
|
29
|
+
handle_master_message(message)
|
30
|
+
else
|
31
|
+
handle_channel_message(topic, message)
|
32
|
+
end
|
28
33
|
end
|
29
34
|
end
|
30
35
|
end
|
31
36
|
end
|
32
37
|
|
33
38
|
def self.stop_monitor
|
39
|
+
p stop_monitor: 1
|
34
40
|
@monitor&.interrupt
|
35
41
|
end
|
36
42
|
|
@@ -111,10 +117,13 @@ end
|
|
111
117
|
|
112
118
|
spin do
|
113
119
|
move_on_after(3) do
|
120
|
+
p :move_on_after
|
114
121
|
throttled_loop(1) do
|
122
|
+
# p :throttled_loop
|
115
123
|
channel << Time.now
|
116
124
|
end
|
117
125
|
end
|
126
|
+
p :done
|
118
127
|
channel.close
|
119
128
|
RedisChannel.stop_monitor
|
120
129
|
end
|
data/examples/core/await.rb
CHANGED
data/examples/io/https_server.rb
CHANGED
@@ -18,7 +18,8 @@ puts 'Serving HTTPS on port 1234'
|
|
18
18
|
spin_loop(interval: 1) { STDOUT << '.' }
|
19
19
|
|
20
20
|
# server.accept_loop do |socket|
|
21
|
-
|
21
|
+
server.accept_loop do |socket|
|
22
|
+
# while (socket = (server.accept)
|
22
23
|
spin do
|
23
24
|
while (data = socket.gets("\n", 8192))
|
24
25
|
if data.chomp.empty?
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
require 'localhost/authority'
|
6
|
+
|
7
|
+
authority = Localhost::Authority.fetch
|
8
|
+
server_ctx = authority.server_context
|
9
|
+
server_ctx.servername_cb = proc { |_socket, name| server_ctx }
|
10
|
+
opts = {
|
11
|
+
reuse_addr: true,
|
12
|
+
dont_linger: true,
|
13
|
+
secure_context: server_ctx
|
14
|
+
}
|
15
|
+
|
16
|
+
server = Polyphony::Net.tcp_listen('localhost', 1234, opts)
|
17
|
+
|
18
|
+
puts 'Serving HTTPS on port 1234'
|
19
|
+
|
20
|
+
# server.accept_loop do |socket|
|
21
|
+
server.accept_loop do |socket|
|
22
|
+
# while (socket = (server.accept)
|
23
|
+
spin do
|
24
|
+
while (data = socket.gets("\n", 8192))
|
25
|
+
if data.chomp.empty?
|
26
|
+
socket << "HTTP/1.1 200 OK\nConnection: close\nContent-Length: 4\n\nfoo\n"
|
27
|
+
break
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
require 'localhost/authority'
|
6
|
+
|
7
|
+
authority = Localhost::Authority.fetch
|
8
|
+
server_ctx = authority.server_context
|
9
|
+
|
10
|
+
resolver = spin_loop do
|
11
|
+
name, client = receive
|
12
|
+
client << server_ctx
|
13
|
+
end
|
14
|
+
|
15
|
+
server_ctx.servername_cb = proc do |_socket, name|
|
16
|
+
resolver << [name, Fiber.current]
|
17
|
+
receive
|
18
|
+
end
|
19
|
+
|
20
|
+
opts = {
|
21
|
+
reuse_addr: true,
|
22
|
+
dont_linger: true,
|
23
|
+
secure_context: server_ctx
|
24
|
+
}
|
25
|
+
|
26
|
+
server = Polyphony::Net.tcp_listen('localhost', 1234, opts)
|
27
|
+
|
28
|
+
puts 'Serving HTTPS on port 1234'
|
29
|
+
|
30
|
+
# server.accept_loop do |socket|
|
31
|
+
server.accept_loop do |socket|
|
32
|
+
# while (socket = (server.accept)
|
33
|
+
spin do
|
34
|
+
while (data = socket.gets("\n", 8192))
|
35
|
+
if data.chomp.empty?
|
36
|
+
socket << "HTTP/1.1 200 OK\nConnection: close\nContent-Length: 4\n\nfoo\n"
|
37
|
+
break
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
require 'polyphony/adapters/readline'
|
6
|
+
|
7
|
+
$counter = 0
|
8
|
+
timer = spin do
|
9
|
+
throttled_loop(5) do
|
10
|
+
$counter += 1
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
at_exit { timer.stop }
|
15
|
+
|
16
|
+
puts 'try typing $counter to see the counter incremented in the background'
|
17
|
+
loop do
|
18
|
+
puts eval Readline::readline('> ', true)
|
19
|
+
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'bundler/inline'
|
4
4
|
|
5
5
|
gemfile do
|
6
|
+
source 'https://rubygems.org'
|
6
7
|
gem 'h1p'
|
7
8
|
gem 'polyphony', path: '.'
|
8
9
|
end
|
@@ -22,10 +23,12 @@ def handle_client(conn)
|
|
22
23
|
|
23
24
|
conn << "HTTP/1.1 200 OK\r\nContent-Length: 14\r\n\r\nHello, world!\n"
|
24
25
|
end
|
26
|
+
rescue Errno::ECONNRESET
|
27
|
+
# ignore
|
25
28
|
rescue H1P::Error
|
26
29
|
puts 'Got invalid request, closing connection...'
|
27
30
|
ensure
|
28
|
-
conn.close
|
31
|
+
conn.close rescue nil
|
29
32
|
end
|
30
33
|
end
|
31
34
|
|
@@ -60,6 +60,23 @@ inline void conditional_nonblocking_poll(VALUE backend, struct Backend_base *bas
|
|
60
60
|
Backend_poll(backend, Qnil);
|
61
61
|
}
|
62
62
|
|
63
|
+
// Verifies that the given fiber has the @thread ivar set. If not, sets it to
|
64
|
+
// the current thread. This is essential for the correct functioning of fibers
|
65
|
+
// not created by Polyphony, such as those used by the Enumerator class, and
|
66
|
+
// probably others as well. With a nil thread ivar, a fiber can not be
|
67
|
+
// scheduled. This macro is called in backend_base_switch_fiber() and in
|
68
|
+
// backend_snooze().
|
69
|
+
//
|
70
|
+
// In order to refrain from calling rb_thread_current() multiple times, we
|
71
|
+
// allow it to be passed to this macro, and if not we call it on the spot.
|
72
|
+
#define CHECK_FIBER_THREAD_REF(fiber, current_thread) { \
|
73
|
+
VALUE thread = rb_ivar_get(fiber, ID_ivar_thread); \
|
74
|
+
if (thread == Qnil) { \
|
75
|
+
thread = (current_thread != Qnil) ? current_thread : rb_thread_current(); \
|
76
|
+
rb_ivar_set(fiber, ID_ivar_thread, thread); \
|
77
|
+
} \
|
78
|
+
}
|
79
|
+
|
63
80
|
VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base) {
|
64
81
|
VALUE current_fiber = rb_fiber_current();
|
65
82
|
runqueue_entry next;
|
@@ -71,6 +88,8 @@ VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base) {
|
|
71
88
|
if (SHOULD_TRACE(base))
|
72
89
|
TRACE(base, 3, SYM_block, current_fiber, CALLER());
|
73
90
|
|
91
|
+
CHECK_FIBER_THREAD_REF(current_fiber, Qnil);
|
92
|
+
|
74
93
|
while (1) {
|
75
94
|
next = runqueue_shift(&base->runqueue);
|
76
95
|
if (next.fiber != Qnil) {
|
@@ -261,6 +280,9 @@ inline VALUE backend_snooze(struct Backend_base *backend) {
|
|
261
280
|
VALUE ret;
|
262
281
|
VALUE fiber = rb_fiber_current();
|
263
282
|
VALUE thread = rb_thread_current();
|
283
|
+
|
284
|
+
CHECK_FIBER_THREAD_REF(fiber, thread);
|
285
|
+
|
264
286
|
Fiber_make_runnable(fiber, Qnil);
|
265
287
|
ret = Thread_switch_fiber(thread);
|
266
288
|
|
@@ -107,7 +107,7 @@ void break_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, in
|
|
107
107
|
// of a *blocking* event loop (waking it up) in a thread-safe, signal-safe manner
|
108
108
|
}
|
109
109
|
|
110
|
-
inline struct ev_loop *libev_new_loop() {
|
110
|
+
inline struct ev_loop *libev_new_loop(void) {
|
111
111
|
#ifdef POLYPHONY_USE_PIDFD_OPEN
|
112
112
|
return ev_loop_new(EVFLAG_NOSIGMASK);
|
113
113
|
#else
|
@@ -1474,7 +1474,7 @@ void Backend_unpark_fiber(VALUE self, VALUE fiber) {
|
|
1474
1474
|
backend_base_unpark_fiber(&backend->base, fiber);
|
1475
1475
|
}
|
1476
1476
|
|
1477
|
-
void Init_Backend() {
|
1477
|
+
void Init_Backend(void) {
|
1478
1478
|
ev_set_allocator(xrealloc);
|
1479
1479
|
|
1480
1480
|
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cObject);
|
@@ -2,46 +2,6 @@
|
|
2
2
|
|
3
3
|
require 'polyphony'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
class Reline::ANSI
|
8
|
-
def self.select(read_ios = [], write_ios = [], error_ios = [], timeout = nil)
|
9
|
-
# p [:select, read_ios, timeout]
|
10
|
-
# puts caller.join("\n")
|
11
|
-
raise if read_ios.size > 1
|
12
|
-
raise if write_ios.size > 0
|
13
|
-
raise if error_ios.size > 0
|
14
|
-
|
15
|
-
# p 1
|
16
|
-
fiber = Fiber.current
|
17
|
-
timer = spin do
|
18
|
-
sleep timeout
|
19
|
-
fiber.cancel
|
20
|
-
end
|
21
|
-
# p 2
|
22
|
-
read_ios.each do |io|
|
23
|
-
# p wait: io
|
24
|
-
Polyphony.backend_wait_io(io, false)
|
25
|
-
# p :done_wait
|
26
|
-
return [io]
|
27
|
-
end
|
28
|
-
# p 3
|
29
|
-
rescue Polyphony::Cancel
|
30
|
-
# p :cancel
|
31
|
-
return nil
|
32
|
-
ensure
|
33
|
-
# p :ensure
|
34
|
-
timer.stop
|
35
|
-
# p :ensure_done
|
36
|
-
end
|
37
|
-
end
|
38
|
-
else
|
39
|
-
require_relative './readline'
|
40
|
-
|
41
|
-
# RubyLex patches
|
42
|
-
class ::RubyLex
|
43
|
-
class TerminateLineInput2 < RuntimeError
|
44
|
-
end
|
45
|
-
const_set(:TerminateLineInput, TerminateLineInput2)
|
46
|
-
end
|
5
|
+
module Kernel
|
6
|
+
alias_method :gets, :orig_gets
|
47
7
|
end
|
@@ -7,11 +7,13 @@ require 'readline'
|
|
7
7
|
# thread pool. That way, the reactor loop can keep running while waiting for
|
8
8
|
# readline to return
|
9
9
|
module ::Readline
|
10
|
-
|
10
|
+
class << self
|
11
|
+
alias_method :orig_readline, :readline
|
11
12
|
|
12
|
-
|
13
|
+
Worker = Polyphony::ThreadPool.new(1)
|
13
14
|
|
14
|
-
|
15
|
-
|
15
|
+
def readline(*args)
|
16
|
+
Worker.process { orig_readline(*args) }
|
17
|
+
end
|
16
18
|
end
|
17
19
|
end
|
@@ -2,94 +2,35 @@
|
|
2
2
|
|
3
3
|
require_relative '../../polyphony'
|
4
4
|
|
5
|
-
require 'redis'
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
class
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@reader = ::Hiredis::Reader.new
|
34
|
-
end
|
35
|
-
|
36
|
-
# Returns true if connected to server.
|
37
|
-
#
|
38
|
-
# @return [bool] is connected to server
|
39
|
-
def connected?
|
40
|
-
@connection && !@connection.closed?
|
41
|
-
end
|
42
|
-
|
43
|
-
# Sets a timeout for the connection.
|
44
|
-
#
|
45
|
-
# @return [void]
|
46
|
-
def timeout=(timeout)
|
47
|
-
# ignore timeout for now
|
48
|
-
end
|
49
|
-
|
50
|
-
# Disconnects from the server.
|
51
|
-
#
|
52
|
-
# @return [void]
|
53
|
-
def disconnect
|
54
|
-
@connection.close
|
55
|
-
@connection = nil
|
56
|
-
end
|
57
|
-
|
58
|
-
# Sends a command to the server.
|
59
|
-
#
|
60
|
-
# @param command [Array] Redis command
|
61
|
-
# @return [void]
|
62
|
-
def write(command)
|
63
|
-
@connection.write(format_command(command))
|
64
|
-
end
|
65
|
-
|
66
|
-
# Formats a command for sending to server.
|
67
|
-
#
|
68
|
-
# @param args [Array] command
|
69
|
-
# @return [String] formatted command
|
70
|
-
def format_command(args)
|
71
|
-
args = args.flatten
|
72
|
-
(+"*#{args.size}\r\n").tap do |s|
|
73
|
-
args.each do |a|
|
74
|
-
a = a.to_s
|
75
|
-
s << "$#{a.bytesize}\r\n#{a}\r\n"
|
5
|
+
require 'redis-client'
|
6
|
+
|
7
|
+
class RedisClient
|
8
|
+
class RubyConnection
|
9
|
+
class BufferedIO
|
10
|
+
def fill_buffer(strict, size = @chunk_size)
|
11
|
+
remaining = size
|
12
|
+
empty_buffer = @offset >= @buffer.bytesize
|
13
|
+
|
14
|
+
loop do
|
15
|
+
max_read = [remaining, @chunk_size].max
|
16
|
+
bytes = if empty_buffer
|
17
|
+
@io.readpartial(max_read, @buffer)
|
18
|
+
else
|
19
|
+
@io.readpartial(max_read)
|
20
|
+
end
|
21
|
+
|
22
|
+
raise Errno::ECONNRESET if bytes.nil?
|
23
|
+
|
24
|
+
if empty_buffer
|
25
|
+
@offset = 0
|
26
|
+
empty_buffer = false
|
27
|
+
else
|
28
|
+
@buffer << bytes
|
29
|
+
end
|
30
|
+
remaining -= bytes.bytesize
|
31
|
+
return if !strict || remaining <= 0
|
32
|
+
end
|
76
33
|
end
|
77
34
|
end
|
78
35
|
end
|
79
|
-
|
80
|
-
# Reads from the connection, feeding incoming data to the parser.
|
81
|
-
#
|
82
|
-
# @return [void]
|
83
|
-
def read
|
84
|
-
reply = @reader.gets
|
85
|
-
return reply if reply
|
86
|
-
|
87
|
-
@connection.read_loop do |data|
|
88
|
-
@reader.feed(data)
|
89
|
-
reply = @reader.gets
|
90
|
-
return reply unless reply == false
|
91
|
-
end
|
92
|
-
end
|
93
36
|
end
|
94
|
-
|
95
|
-
Redis::Connection.drivers << Polyphony::RedisDriver
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../polyphony'
|
4
|
+
|
5
|
+
module Polyphony
|
6
|
+
# Implements a unidirectional communication channel along the lines of Go
|
7
|
+
# (buffered) channels.
|
8
|
+
class Channel < Polyphony::Queue
|
9
|
+
alias_method :receive, :shift
|
10
|
+
|
11
|
+
def close
|
12
|
+
flush_waiters(Polyphony::MoveOn.new)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/polyphony/core/sync.rb
CHANGED
@@ -101,6 +101,8 @@ module Polyphony
|
|
101
101
|
#
|
102
102
|
# @return [void]
|
103
103
|
def signal
|
104
|
+
return if @queue.empty?
|
105
|
+
|
104
106
|
fiber = @queue.shift
|
105
107
|
fiber.schedule
|
106
108
|
end
|
@@ -109,6 +111,8 @@ module Polyphony
|
|
109
111
|
#
|
110
112
|
# @return [void]
|
111
113
|
def broadcast
|
114
|
+
return if @queue.empty?
|
115
|
+
|
112
116
|
while (fiber = @queue.shift)
|
113
117
|
fiber.schedule
|
114
118
|
end
|
data/lib/polyphony/debugger.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'polyphony/
|
3
|
+
require 'polyphony/core/debug'
|
4
4
|
|
5
5
|
module Polyphony
|
6
6
|
TP_EVENTS = [
|
@@ -188,7 +188,7 @@ module Polyphony
|
|
188
188
|
def start_server_thread
|
189
189
|
@thread = Thread.new do
|
190
190
|
puts("Listening on #{@socket_path}")
|
191
|
-
FileUtils.rm(@socket_path) if File.
|
191
|
+
FileUtils.rm(@socket_path) if File.exist?(@socket_path)
|
192
192
|
socket = UNIXServer.new(@socket_path)
|
193
193
|
loop do
|
194
194
|
@client = socket.accept
|
@@ -131,6 +131,16 @@ class ::IO
|
|
131
131
|
nil
|
132
132
|
end
|
133
133
|
|
134
|
+
def ungetc(c)
|
135
|
+
c = c.chr if c.is_a?(Integer)
|
136
|
+
if @read_buffer
|
137
|
+
@read_buffer.prepend(c)
|
138
|
+
else
|
139
|
+
@read_buffer = +c
|
140
|
+
end
|
141
|
+
end
|
142
|
+
alias_method :ungetbyte, :ungetc
|
143
|
+
|
134
144
|
alias_method :orig_read, :read
|
135
145
|
def read(len = nil, buf = nil, buf_pos = 0)
|
136
146
|
if buf
|
@@ -252,6 +262,8 @@ class ::IO
|
|
252
262
|
end
|
253
263
|
|
254
264
|
def wait_readable(timeout = nil)
|
265
|
+
return self if @read_buffer && @read_buffer.size > 0
|
266
|
+
|
255
267
|
if timeout
|
256
268
|
move_on_after(timeout) do
|
257
269
|
Polyphony.backend_wait_io(self, false)
|
data/lib/polyphony/extensions.rb
CHANGED
data/lib/polyphony/version.rb
CHANGED
data/lib/polyphony.rb
CHANGED
@@ -117,6 +117,10 @@ module Polyphony
|
|
117
117
|
$VERBOSE = nil
|
118
118
|
Object.const_set(:Queue, Polyphony::Queue)
|
119
119
|
Object.const_set(:Mutex, Polyphony::Mutex)
|
120
|
+
|
121
|
+
require 'monitor'
|
122
|
+
Object.const_set(:Monitor, Polyphony::Mutex)
|
123
|
+
|
120
124
|
Object.const_set(:ConditionVariable, Polyphony::ConditionVariable)
|
121
125
|
$VERBOSE = verbose
|
122
126
|
end
|
data/polyphony.gemspec
CHANGED
@@ -11,6 +11,7 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.homepage = 'https://digital-fabric.github.io/polyphony'
|
12
12
|
s.metadata = {
|
13
13
|
"source_code_uri" => "https://github.com/digital-fabric/polyphony",
|
14
|
+
# "documentation_uri" => "https://www.rubydoc.info/gems/polyphony",
|
14
15
|
"documentation_uri" => "https://digital-fabric.github.io/polyphony/",
|
15
16
|
"homepage_uri" => "https://digital-fabric.github.io/polyphony/",
|
16
17
|
"changelog_uri" => "https://github.com/digital-fabric/polyphony/blob/master/CHANGELOG.md"
|
@@ -21,16 +22,15 @@ Gem::Specification.new do |s|
|
|
21
22
|
s.require_paths = ["lib"]
|
22
23
|
s.required_ruby_version = '>= 3.0'
|
23
24
|
|
24
|
-
s.add_development_dependency 'rake-compiler', '1.
|
25
|
-
s.add_development_dependency 'minitest', '5.
|
26
|
-
s.add_development_dependency '
|
27
|
-
s.add_development_dependency '
|
28
|
-
s.add_development_dependency '
|
29
|
-
s.add_development_dependency 'pry', '0.13.1'
|
25
|
+
s.add_development_dependency 'rake-compiler', '1.2.1'
|
26
|
+
s.add_development_dependency 'minitest', '5.17.0'
|
27
|
+
s.add_development_dependency 'simplecov', '0.22.0'
|
28
|
+
s.add_development_dependency 'rubocop', '1.45.1'
|
29
|
+
s.add_development_dependency 'pry', '0.14.2'
|
30
30
|
|
31
|
-
s.add_development_dependency 'msgpack', '1.
|
31
|
+
s.add_development_dependency 'msgpack', '1.6.0'
|
32
32
|
s.add_development_dependency 'httparty', '0.21.0'
|
33
|
-
s.add_development_dependency 'localhost', '
|
33
|
+
s.add_development_dependency 'localhost', '1.1.10'
|
34
34
|
|
35
35
|
# s.add_development_dependency 'jekyll', '~>3.8.6'
|
36
36
|
# s.add_development_dependency 'jekyll-remote-theme', '~>0.4.1'
|
data/test/helper.rb
CHANGED
@@ -11,16 +11,11 @@ require 'fileutils'
|
|
11
11
|
require_relative './eg'
|
12
12
|
|
13
13
|
require 'minitest/autorun'
|
14
|
-
require 'minitest/reporters'
|
15
14
|
|
16
15
|
::Exception.__disable_sanitized_backtrace__ = true
|
17
16
|
|
18
17
|
IS_LINUX = RUBY_PLATFORM =~ /linux/
|
19
18
|
|
20
|
-
# Minitest::Reporters.use! [
|
21
|
-
# Minitest::Reporters::SpecReporter.new
|
22
|
-
# ]
|
23
|
-
|
24
19
|
module ::Kernel
|
25
20
|
def trace(*args)
|
26
21
|
STDOUT.orig_write(format_trace(args))
|
data/test/test_ext.rb
CHANGED
@@ -39,6 +39,69 @@ class ExceptionTest < MiniTest::Test
|
|
39
39
|
Exception.__disable_sanitized_backtrace__ = prev_disable
|
40
40
|
end
|
41
41
|
|
42
|
+
LOCATION_ONLY = ->(s) { s =~ /^(.+)\:in / && Regexp.last_match[1] }
|
43
|
+
|
44
|
+
def test_backtrace_on_main_fiber
|
45
|
+
base = caller.map(&LOCATION_ONLY)
|
46
|
+
begin
|
47
|
+
lineno = __LINE__ + 1
|
48
|
+
raise 'foo'
|
49
|
+
rescue => e
|
50
|
+
end
|
51
|
+
|
52
|
+
assert !!e
|
53
|
+
|
54
|
+
bt = e.backtrace.map(&LOCATION_ONLY)
|
55
|
+
assert_equal ["#{__FILE__}:#{lineno}"] + base, bt
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_backtrace_on_child_fiber
|
59
|
+
Exception.__disable_sanitized_backtrace__ = false
|
60
|
+
|
61
|
+
lineno = __LINE__ + 1
|
62
|
+
fiber = spin do
|
63
|
+
raise receive
|
64
|
+
end
|
65
|
+
|
66
|
+
base = caller.map(&LOCATION_ONLY)
|
67
|
+
|
68
|
+
begin
|
69
|
+
fiber << 'foo'
|
70
|
+
fiber.await
|
71
|
+
rescue => e
|
72
|
+
end
|
73
|
+
|
74
|
+
assert !!e
|
75
|
+
bt = e.backtrace.map(&LOCATION_ONLY)
|
76
|
+
assert_equal ["#{__FILE__}:#{lineno + 1}", "#{__FILE__}:#{lineno}"] + base, bt
|
77
|
+
ensure
|
78
|
+
Exception.__disable_sanitized_backtrace__ = true
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_backtrace_on_grand_child_fiber
|
82
|
+
Exception.__disable_sanitized_backtrace__ = false
|
83
|
+
|
84
|
+
lineno = __LINE__ + 1
|
85
|
+
f1 = spin do
|
86
|
+
f2 = spin do
|
87
|
+
raise 'foo'
|
88
|
+
end
|
89
|
+
f2.await
|
90
|
+
end
|
91
|
+
|
92
|
+
base = caller.map(&LOCATION_ONLY)
|
93
|
+
|
94
|
+
begin
|
95
|
+
f1.await
|
96
|
+
rescue => e
|
97
|
+
end
|
98
|
+
|
99
|
+
assert !!e
|
100
|
+
bt = e.backtrace.map(&LOCATION_ONLY)
|
101
|
+
assert_equal ["#{__FILE__}:#{lineno + 2}", "#{__FILE__}:#{lineno + 1}", "#{__FILE__}:#{lineno}"] + base, bt
|
102
|
+
ensure
|
103
|
+
Exception.__disable_sanitized_backtrace__ = true
|
104
|
+
end
|
42
105
|
end
|
43
106
|
|
44
107
|
class ProcessTest < MiniTest::Test
|
data/test/test_thread_pool.rb
CHANGED
@@ -56,7 +56,7 @@ class ThreadPoolTest < MiniTest::Test
|
|
56
56
|
t0 = Time.now
|
57
57
|
threads = []
|
58
58
|
buffer = []
|
59
|
-
|
59
|
+
20.times do |i|
|
60
60
|
@pool.cast do
|
61
61
|
sleep 0.01
|
62
62
|
threads << Thread.current
|
@@ -70,7 +70,7 @@ class ThreadPoolTest < MiniTest::Test
|
|
70
70
|
|
71
71
|
sleep 0.20 # allow time for threads to spawn
|
72
72
|
assert_equal @pool.size, threads.uniq.size
|
73
|
-
assert_equal (0..
|
73
|
+
assert_equal (0..19).to_a, buffer.sort if IS_LINUX
|
74
74
|
end
|
75
75
|
|
76
76
|
def test_busy?
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: polyphony
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.97'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-02-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -16,98 +16,84 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.
|
19
|
+
version: 1.2.1
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.
|
26
|
+
version: 1.2.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: minitest
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 5.
|
33
|
+
version: 5.17.0
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 5.
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: minitest-reporters
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - '='
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: 1.4.2
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - '='
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: 1.4.2
|
40
|
+
version: 5.17.0
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
42
|
name: simplecov
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
58
44
|
requirements:
|
59
45
|
- - '='
|
60
46
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.
|
47
|
+
version: 0.22.0
|
62
48
|
type: :development
|
63
49
|
prerelease: false
|
64
50
|
version_requirements: !ruby/object:Gem::Requirement
|
65
51
|
requirements:
|
66
52
|
- - '='
|
67
53
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.
|
54
|
+
version: 0.22.0
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
56
|
name: rubocop
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
72
58
|
requirements:
|
73
59
|
- - '='
|
74
60
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
61
|
+
version: 1.45.1
|
76
62
|
type: :development
|
77
63
|
prerelease: false
|
78
64
|
version_requirements: !ruby/object:Gem::Requirement
|
79
65
|
requirements:
|
80
66
|
- - '='
|
81
67
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
68
|
+
version: 1.45.1
|
83
69
|
- !ruby/object:Gem::Dependency
|
84
70
|
name: pry
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
86
72
|
requirements:
|
87
73
|
- - '='
|
88
74
|
- !ruby/object:Gem::Version
|
89
|
-
version: 0.
|
75
|
+
version: 0.14.2
|
90
76
|
type: :development
|
91
77
|
prerelease: false
|
92
78
|
version_requirements: !ruby/object:Gem::Requirement
|
93
79
|
requirements:
|
94
80
|
- - '='
|
95
81
|
- !ruby/object:Gem::Version
|
96
|
-
version: 0.
|
82
|
+
version: 0.14.2
|
97
83
|
- !ruby/object:Gem::Dependency
|
98
84
|
name: msgpack
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|
100
86
|
requirements:
|
101
87
|
- - '='
|
102
88
|
- !ruby/object:Gem::Version
|
103
|
-
version: 1.
|
89
|
+
version: 1.6.0
|
104
90
|
type: :development
|
105
91
|
prerelease: false
|
106
92
|
version_requirements: !ruby/object:Gem::Requirement
|
107
93
|
requirements:
|
108
94
|
- - '='
|
109
95
|
- !ruby/object:Gem::Version
|
110
|
-
version: 1.
|
96
|
+
version: 1.6.0
|
111
97
|
- !ruby/object:Gem::Dependency
|
112
98
|
name: httparty
|
113
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -126,16 +112,16 @@ dependencies:
|
|
126
112
|
name: localhost
|
127
113
|
requirement: !ruby/object:Gem::Requirement
|
128
114
|
requirements:
|
129
|
-
- -
|
115
|
+
- - '='
|
130
116
|
- !ruby/object:Gem::Version
|
131
|
-
version: 1.1.
|
117
|
+
version: 1.1.10
|
132
118
|
type: :development
|
133
119
|
prerelease: false
|
134
120
|
version_requirements: !ruby/object:Gem::Requirement
|
135
121
|
requirements:
|
136
|
-
- -
|
122
|
+
- - '='
|
137
123
|
- !ruby/object:Gem::Version
|
138
|
-
version: 1.1.
|
124
|
+
version: 1.1.10
|
139
125
|
description:
|
140
126
|
email: sharon@noteflakes.com
|
141
127
|
executables: []
|
@@ -264,6 +250,8 @@ files:
|
|
264
250
|
- examples/io/http1_splice_chunked.rb
|
265
251
|
- examples/io/httparty.rb
|
266
252
|
- examples/io/https_server.rb
|
253
|
+
- examples/io/https_server_sni.rb
|
254
|
+
- examples/io/https_server_sni_2.rb
|
267
255
|
- examples/io/irb.rb
|
268
256
|
- examples/io/net-http.rb
|
269
257
|
- examples/io/open.rb
|
@@ -271,6 +259,7 @@ files:
|
|
271
259
|
- examples/io/pry.rb
|
272
260
|
- examples/io/rack_server.rb
|
273
261
|
- examples/io/raw.rb
|
262
|
+
- examples/io/readline.rb
|
274
263
|
- examples/io/reline.rb
|
275
264
|
- examples/io/splice_chunks.rb
|
276
265
|
- examples/io/splice_echo_server.rb
|
@@ -364,6 +353,7 @@ files:
|
|
364
353
|
- lib/polyphony/adapters/readline.rb
|
365
354
|
- lib/polyphony/adapters/redis.rb
|
366
355
|
- lib/polyphony/adapters/sequel.rb
|
356
|
+
- lib/polyphony/core/channel.rb
|
367
357
|
- lib/polyphony/core/debug.rb
|
368
358
|
- lib/polyphony/core/exceptions.rb
|
369
359
|
- lib/polyphony/core/global_api.rb
|
@@ -374,7 +364,6 @@ files:
|
|
374
364
|
- lib/polyphony/core/timer.rb
|
375
365
|
- lib/polyphony/debugger.rb
|
376
366
|
- lib/polyphony/extensions.rb
|
377
|
-
- lib/polyphony/extensions/enumerator.rb
|
378
367
|
- lib/polyphony/extensions/exception.rb
|
379
368
|
- lib/polyphony/extensions/fiber.rb
|
380
369
|
- lib/polyphony/extensions/io.rb
|
@@ -1,16 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Extensions to the Enumerator class
|
4
|
-
class ::Enumerator
|
5
|
-
alias_method :orig_next, :next
|
6
|
-
def next
|
7
|
-
Fiber.current.thread ||= Thread.current
|
8
|
-
orig_next
|
9
|
-
end
|
10
|
-
|
11
|
-
alias_method :orig_each, :each
|
12
|
-
def each(*a, &b)
|
13
|
-
Fiber.current.thread ||= Thread.current
|
14
|
-
orig_each(*a, &b)
|
15
|
-
end
|
16
|
-
end
|