polyphony 0.95 → 0.97

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 554854ea3a7b1bc8dd555b24283e42019bb7bfb91953cd0b3e6ac49c9c59198b
4
- data.tar.gz: 59a88ec85837e68ed50594ee892c814dee9e15ba19b4392d50436fb0b51b1d82
3
+ metadata.gz: 1a8218fc4160e0772ee3aada1f72ea9d063724ccf9b0aee7dfa6e9e4d69ebfe4
4
+ data.tar.gz: 74aae84e991cd4330ad282553061b29e71a6434ce2dafc9985c62feb3a966a68
5
5
  SHA512:
6
- metadata.gz: 3637e9af046327dd642bb555224e4dd36169824a698b1176f57ebeb309e61d1ac6c50d2423c8ab2ac72abf9d8e9184f37ebf9ca8343032d7e40b3244d99f8ca7
7
- data.tar.gz: 27fca71574396f73e783630691cbda094488ab19128c884ca2fdafd570b0df920f083e46750455a656c0f26c9acc6f11783634cb7d43b23f2689f000ee0c67ea
6
+ metadata.gz: 772d0be4ef2f723fa27cc5ecb8ef9e9b5d012c501a364ab23dce7d145fc6d156d5e219e52e871a2aadb41a397c5fb044c8aaee9b42906f4894ff3447af0e93ba
7
+ data.tar.gz: b74d6958ea8a77ffee45aed8184091b5ef54cb4fcaca9798351533b13d2f2202f28ab95f56a26e09692bf40f00e723d2df9c99007dbe10631d1c69c523fb3a58
@@ -1,4 +1,4 @@
1
- name: Tests
1
+ name: Tests (libev)
2
2
 
3
3
  on: [push, pull_request]
4
4
 
@@ -1,4 +1,4 @@
1
- name: Tests with io_uring
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: ['2.7', '3.0', '3.1', 'head']
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@v2
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 --verbose --name test_sleep
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
@@ -12,7 +12,7 @@ task :recompile => [:clean, :compile]
12
12
  task :default => [:compile, :test]
13
13
 
14
14
  task :test do
15
- exec 'ruby test/run.rb --verbose'
15
+ exec 'ruby test/run.rb'
16
16
  end
17
17
 
18
18
  task :stress_test do
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bundler/setup'
4
- require 'polyphony/adapters/redis'
5
- # require 'redis'
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 'bundler/setup'
4
- require 'polyphony/adapters/redis'
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
- topic == if CHANNEL_MASTER_TOPIC
24
- handle_master_message(message)
25
- else
26
- handle_channel_message(topic, message)
27
- end
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'bundler/setup'
4
4
  require 'polyphony'
5
- require 'polyphony/extensions/debug'
5
+ require 'polyphony/core/debug'
6
6
 
7
7
  Exception.__disable_sanitized_backtrace__ = true
8
8
 
@@ -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
- while (socket = server.accept)
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
- if Object.constants.include?(:Reline)
6
- puts "reline"
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
- alias_method :orig_readline, :readline
10
+ class << self
11
+ alias_method :orig_readline, :readline
11
12
 
12
- Worker = Polyphony::ThreadPool.new(1)
13
+ Worker = Polyphony::ThreadPool.new(1)
13
14
 
14
- def readline(*args)
15
- Worker.process { orig_readline(*args) }
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
- require 'hiredis/reader'
7
-
8
- # Polyphony-based Redis driver
9
- class Polyphony::RedisDriver
10
-
11
- # Connects to a Redis server using the given config.
12
- #
13
- # @return [TCPSocket, UNIXSocket, SSLSocket] client connectio
14
- def self.connect(config)
15
- raise 'unix sockets not supported' if config[:scheme] == 'unix'
16
-
17
- # connection.connect_unix(config[:path], connect_timeout)
18
-
19
- raise 'ssl not supported' if config[:scheme] == 'rediss' || config[:ssl]
20
-
21
- # raise NotImplementedError, "SSL not supported by hiredis driver"
22
-
23
- new(config[:host], config[:port])
24
- # connection.connect(config[:host], config[:port], connect_timeout)
25
- end
26
-
27
- # Initializes a Redis client connection.
28
- #
29
- # @param host [String] hostname
30
- # @param port [Integer] port number
31
- def initialize(host, port)
32
- @connection = Polyphony::Net.tcp_connect(host, port)
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
@@ -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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'polyphony/extensions/debug'
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.exists?(@socket_path)
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)
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './extensions/enumerator'
4
3
  require_relative './extensions/exception'
5
4
  require_relative './extensions/fiber'
6
5
  require_relative './extensions/io'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.95'
4
+ VERSION = '0.97'
5
5
  end
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.1.1'
25
- s.add_development_dependency 'minitest', '5.14.4'
26
- s.add_development_dependency 'minitest-reporters', '1.4.2'
27
- s.add_development_dependency 'simplecov', '0.17.1'
28
- s.add_development_dependency 'rubocop', '0.85.1'
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.4.2'
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', '~>1.1.4'
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
@@ -56,7 +56,7 @@ class ThreadPoolTest < MiniTest::Test
56
56
  t0 = Time.now
57
57
  threads = []
58
58
  buffer = []
59
- 10.times do |i|
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..9).to_a, buffer.sort if IS_LINUX
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.95'
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-01-16 00:00:00.000000000 Z
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.1.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.1.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.14.4
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.14.4
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.17.1
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.17.1
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: 0.85.1
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: 0.85.1
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.13.1
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.13.1
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.4.2
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.4.2
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.4
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.4
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