backports 3.20.1 → 3.20.2

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: aa5429b607594bcd855bad1a66bbfdc01e3facb1f76be0c338fd549b502ec1c4
4
- data.tar.gz: e7e08e840cc7559df5708a0bfa5d1499a3e809fb9bf79b260e4c98e7ca634c60
3
+ metadata.gz: 4e5c6feae3c15c70a78dda33abcb10c737393eb6898d1756c8f3da4e5f604b25
4
+ data.tar.gz: 3339a5a0ae48803b9853627c096790f555a3a8f852af7b66c4331f683fcaac55
5
5
  SHA512:
6
- metadata.gz: 5e59f88a7ef8c1487cc1c1fb04d28a96d44b26d8cc5379a6ea9f18edfca62d97fe0b84c27a03c600a6137febf9096917f0ec1d7a52574fc536f7c345b2e256af
7
- data.tar.gz: 9e732e7626034ae45dd1daf0cd54f20319854f27f041c5624e8c96d287a66ffeee780e8d0c142472e8b88cba597b65ee300b161b811acade9aaa697ff3352ff7
6
+ metadata.gz: d843af1e7bd02fe062f600df7b130c37b2ce41bf178831049bbcd57fadbb9cac1c73d9bf4feebca71dcb39998492442083822d60c1691e1e67f195e21005ea97
7
+ data.tar.gz: f37e52397480aeb259cf469093980cdef01500e845b94fbe90d67864f37323d2dcdbd59fa308ada90cc7c1820a02b20c07c4a647e986840f0c9222c1b4313a35
data/Gemfile CHANGED
@@ -11,6 +11,6 @@ end
11
11
 
12
12
  if RUBY_VERSION >= '2.4.0'
13
13
  group :development do
14
- gem 'rubocop', '~> 1.6.0'
14
+ gem 'rubocop'
15
15
  end
16
16
  end
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Backports Library [<img src="https://travis-ci.org/marcandre/backports.svg?branch=master">](https://travis-ci.org/marcandre/backports) [<img src="https://badge.fury.io/rb/backports.svg" alt="Gem Version" />](http://badge.fury.io/rb/backports) [![Tidelift](https://tidelift.com/badges/package/rubygems/backports)](https://tidelift.com/subscription/pkg/rubygems-backports?utm_source=rubygems-backports&utm_medium=referral&utm_campaign=readme)
2
2
 
3
- Yearning to use write a gem using some new cool features in Ruby 3.0 while
3
+ Yearning to write a gem using some new cool features in Ruby 3.0 while
4
4
  still supporting Ruby 2.5.x?
5
5
  Have some legacy code in Ruby 1.8 but can't live without `flat_map`?
6
6
 
@@ -135,7 +135,7 @@ itself, JRuby and Rubinius.
135
135
  - `clamp` (with range)
136
136
 
137
137
  #### Complex
138
- - +<=>+
138
+ - `<=>`
139
139
 
140
140
  #### Enumerable
141
141
  - `filter_map`
@@ -167,10 +167,10 @@ itself, JRuby and Rubinius.
167
167
  - `then`
168
168
 
169
169
  #### Method
170
- - +<<+, +>>+
170
+ - `<<`, `>>`
171
171
 
172
172
  #### Proc
173
- - +<<+, +>>+
173
+ - `<<`, `>>`
174
174
 
175
175
  #### Range
176
176
  - `cover?` (with `Range` argument)
@@ -9,7 +9,7 @@ class Integer
9
9
  bits_shift = n.bit_length / 2 + 1
10
10
  bitn_mask = root = 1 << bits_shift
11
11
  loop do
12
- root ^= bitn_mask if (root * root) > n # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
12
+ root ^= bitn_mask if (root * root) > n
13
13
  bitn_mask >>= 1
14
14
  return root if bitn_mask == 0
15
15
  root |= bitn_mask
@@ -1,5 +1,19 @@
1
1
  if RUBY_VERSION < '2'
2
2
  warn 'Ractor not backported to Ruby 1.x'
3
+ elsif defined?(Ractor.current)
4
+ # all good
3
5
  else
4
- require_relative '../ractor/ractor' unless defined?(Ractor.current)
6
+ # Cloner:
7
+ require_relative '../2.4.0/hash/transform_values'
8
+ require_relative '../2.5.0/hash/transform_keys'
9
+ # Queues & FilteredQueue
10
+ require_relative '../2.3.0/queue/close'
11
+
12
+ class Ractor
13
+ end
14
+
15
+ module Backports
16
+ Ractor = ::Ractor
17
+ end
18
+ require_relative '../ractor/ractor'
5
19
  end
@@ -1,91 +1,94 @@
1
- require_relative '../2.4.0/hash/transform_values'
2
- require_relative '../2.5.0/hash/transform_keys'
1
+ # shareable_constant_value: literal
3
2
 
4
- class Ractor
5
- module Cloner
6
- extend self
3
+ using ::RubyNext if defined?(::RubyNext)
7
4
 
8
- def deep_clone(obj)
9
- return obj if Ractor.ractor_shareable_self?(obj, false) { false }
5
+ module Backports
6
+ class Ractor
7
+ module Cloner
8
+ extend self
10
9
 
11
- @processed = {}.compare_by_identity
12
- @changed = nil
13
- result = process(obj) do |r|
14
- copy_contents(r)
10
+ def deep_clone(obj)
11
+ return obj if Ractor.ractor_shareable_self?(obj, false) { false }
12
+
13
+ @processed = {}.compare_by_identity
14
+ @changed = nil
15
+ result = process(obj) do |r|
16
+ copy_contents(r)
17
+ end
18
+ return result if result
19
+
20
+ Ractor.ractor_mark_set_shareable(@processed)
21
+ obj
15
22
  end
16
- return result if result
17
23
 
18
- Ractor.ractor_mark_set_shareable(@processed)
19
- obj
20
- end
24
+ # Yields a deep copy.
25
+ # If no deep copy is needed, `obj` is returned and
26
+ # nothing is yielded
27
+ private def clone_deeper(obj)
28
+ return obj if Ractor.ractor_shareable_self?(obj, false) { false }
21
29
 
22
- # Yields a deep copy.
23
- # If no deep copy is needed, `obj` is returned and
24
- # nothing is yielded
25
- private def clone_deeper(obj)
26
- return obj if Ractor.ractor_shareable_self?(obj, false) { false }
30
+ result = process(obj) do |r|
31
+ copy_contents(r)
32
+ end
33
+ return obj unless result
27
34
 
28
- result = process(obj) do |r|
29
- copy_contents(r)
35
+ yield result if block_given?
36
+ result
30
37
  end
31
- return obj unless result
32
-
33
- yield result if block_given?
34
- result
35
- end
36
38
 
37
- # Yields if `obj` is a new structure
38
- # Returns the deep copy, or `false` if no deep copy is needed
39
- private def process(obj)
40
- @processed.fetch(obj) do
41
- # For recursive structures, assume that we'll need a duplicate.
42
- # If that's not the case, we will have duplicated the whole structure
43
- # for nothing...
44
- @processed[obj] = result = obj.dup
45
- changed = track_change { yield result }
46
- return false if obj.frozen? && !changed
39
+ # Yields if `obj` is a new structure
40
+ # Returns the deep copy, or `false` if no deep copy is needed
41
+ private def process(obj)
42
+ @processed.fetch(obj) do
43
+ # For recursive structures, assume that we'll need a duplicate.
44
+ # If that's not the case, we will have duplicated the whole structure
45
+ # for nothing...
46
+ @processed[obj] = result = obj.dup
47
+ changed = track_change { yield result }
48
+ return false if obj.frozen? && !changed
47
49
 
48
- @changed = true
49
- result.freeze if obj.frozen?
50
+ @changed = true
51
+ result.freeze if obj.frozen?
50
52
 
51
- result
53
+ result
54
+ end
52
55
  end
53
- end
54
56
 
55
- # returns if the block called `deep clone` and that the deep copy was needed
56
- private def track_change
57
- prev = @changed
58
- @changed = false
59
- yield
60
- @changed
61
- ensure
62
- @changed = prev
63
- end
57
+ # returns if the block called `deep clone` and that the deep copy was needed
58
+ private def track_change
59
+ prev = @changed
60
+ @changed = false
61
+ yield
62
+ @changed
63
+ ensure
64
+ @changed = prev
65
+ end
64
66
 
65
- # modifies in place `obj` by calling `deep clone` on its contents
66
- private def copy_contents(obj)
67
- case obj
68
- when ::Hash
69
- if obj.default
70
- clone_deeper(obj.default) do |copy|
71
- obj.default = copy
67
+ # modifies in place `obj` by calling `deep clone` on its contents
68
+ private def copy_contents(obj)
69
+ case obj
70
+ when ::Hash
71
+ if obj.default
72
+ clone_deeper(obj.default) do |copy|
73
+ obj.default = copy
74
+ end
75
+ end
76
+ obj.transform_keys! { |key| clone_deeper(key) }
77
+ obj.transform_values! { |value| clone_deeper(value) }
78
+ when ::Array
79
+ obj.map! { |item| clone_deeper(item) }
80
+ when ::Struct
81
+ obj.each_pair do |key, item|
82
+ clone_deeper(item) { |copy| obj[key] = copy }
72
83
  end
73
84
  end
74
- obj.transform_keys! { |key| clone_deeper(key) }
75
- obj.transform_values! { |value| clone_deeper(value) }
76
- when ::Array
77
- obj.map! { |item| clone_deeper(item) }
78
- when ::Struct
79
- obj.each_pair do |key, item|
80
- clone_deeper(item) { |copy| obj[key] = copy }
81
- end
82
- end
83
- obj.instance_variables.each do |var|
84
- clone_deeper(obj.instance_variable_get(var)) do |copy|
85
- obj.instance_variable_set(var, copy)
85
+ obj.instance_variables.each do |var|
86
+ clone_deeper(obj.instance_variable_get(var)) do |copy|
87
+ obj.instance_variable_set(var, copy)
88
+ end
86
89
  end
87
90
  end
88
91
  end
92
+ private_constant :Cloner
89
93
  end
90
- private_constant :Cloner
91
94
  end
@@ -1,16 +1,20 @@
1
- class Ractor
2
- class ClosedError < ::StopIteration
3
- end
1
+ # shareable_constant_value: literal
4
2
 
5
- class Error < ::StandardError
6
- end
3
+ module Backports
4
+ class Ractor
5
+ class ClosedError < ::StopIteration
6
+ end
7
+
8
+ class Error < ::StandardError
9
+ end
7
10
 
8
- class RemoteError < Error
9
- attr_reader :ractor
11
+ class RemoteError < Error
12
+ attr_reader :ractor
10
13
 
11
- def initialize(message = nil)
12
- @ractor = Ractor.current
13
- super
14
+ def initialize(message = nil)
15
+ @ractor = Ractor.current
16
+ super
17
+ end
14
18
  end
15
19
  end
16
20
  end
@@ -1,10 +1,18 @@
1
+ # shareable_constant_value: literal
2
+
1
3
  module Backports
4
+ # Like ::Queue, but with
5
+ # - filtering
6
+ # - timeout
7
+ # - raises on closed queues
8
+ #
9
+ # Independent from other Ractor related backports.
2
10
  class FilteredQueue
3
- require 'backports/2.3.0/queue/close'
4
11
  CONSUME_ON_ESCAPE = true
5
12
 
6
13
  class ClosedQueueError < ::ClosedQueueError
7
14
  end
15
+
8
16
  class TimeoutError < ::ThreadError
9
17
  end
10
18
 
@@ -20,11 +28,6 @@ module Backports
20
28
  end
21
29
  private_constant :Message
22
30
 
23
- # Like ::Queue, but with
24
- # - filtering
25
- # - timeout
26
- # - raises on closed queues
27
-
28
31
  attr_reader :num_waiting
29
32
 
30
33
  # Timeout processing based on https://spin.atomicobject.com/2017/06/28/queue-pop-with-timeout-fixed/
@@ -69,7 +72,7 @@ module Backports
69
72
  msg = nil
70
73
  exclude = [] if block # exclusion list of messages rejected by this call
71
74
  timeout_time = timeout + Time.now.to_f if timeout
72
- while true do
75
+ while true do # rubocop:disable Style/InfiniteLoop, Style/WhileUntilDo
73
76
  @mutex.synchronize do
74
77
  reenter if reentrant?
75
78
  msg = acquire!(timeout_time, exclude)
@@ -167,7 +170,7 @@ module Backports
167
170
  # private methods assume @mutex synchonized
168
171
  # adds to exclude list
169
172
  private def acquire!(timeout_time, exclude = nil)
170
- while true do
173
+ while true do # rubocop:disable Style/InfiniteLoop, Style/WhileUntilDo
171
174
  if (msg = available!(exclude))
172
175
  msg.reserved = true
173
176
  exclude << msg if exclude
@@ -1,62 +1,66 @@
1
- require_relative '../tools/filtered_queue'
2
-
3
- class Ractor
4
- # Standard ::Queue but raises if popping and closed
5
- class BaseQueue < ::Backports::FilteredQueue
6
- ClosedQueueError = ::Ractor::ClosedError
7
-
8
- # yields message (if any)
9
- def pop_non_blocking
10
- yield pop(timeout: 0)
11
- rescue TimeoutError
12
- nil
1
+ # shareable_constant_value: literal
2
+
3
+ require_relative 'filtered_queue'
4
+
5
+ module Backports
6
+ class Ractor
7
+ # Standard ::Queue but raises if popping and closed
8
+ class BaseQueue < FilteredQueue
9
+ ClosedQueueError = Ractor::ClosedError
10
+
11
+ # yields message (if any)
12
+ def pop_non_blocking
13
+ yield pop(timeout: 0)
14
+ rescue TimeoutError
15
+ nil
16
+ end
13
17
  end
14
- end
15
18
 
16
- class IncomingQueue < BaseQueue
17
- TYPE = :incoming
19
+ class IncomingQueue < BaseQueue
20
+ TYPE = :incoming
18
21
 
19
- protected def reenter
20
- raise ::Ractor::Error, 'Can not reenter'
22
+ protected def reenter
23
+ raise Ractor::Error, 'Can not reenter'
24
+ end
21
25
  end
22
- end
23
26
 
24
- # * Wraps exception
25
- # * Add `ack: ` to push (blocking)
26
- class OutgoingQueue < BaseQueue
27
- TYPE = :outgoing
27
+ # * Wraps exception
28
+ # * Add `ack: ` to push (blocking)
29
+ class OutgoingQueue < BaseQueue
30
+ TYPE = :outgoing
28
31
 
29
- WrappedException = ::Struct.new(:exception, :ractor)
32
+ WrappedException = ::Struct.new(:exception, :ractor)
30
33
 
31
- def initialize
32
- @ack_queue = ::Queue.new
33
- super
34
- end
34
+ def initialize
35
+ @ack_queue = ::Queue.new
36
+ super
37
+ end
35
38
 
36
- def pop(timeout: nil, ack: true)
37
- r = super(timeout: timeout)
38
- @ack_queue << :done if ack
39
- raise r.exception if WrappedException === r
39
+ def pop(timeout: nil, ack: true)
40
+ r = super(timeout: timeout)
41
+ @ack_queue << :done if ack
42
+ raise r.exception if WrappedException === r
40
43
 
41
- r
42
- end
44
+ r
45
+ end
43
46
 
44
- def close(how = :hard)
45
- super()
46
- return if how == :soft
47
+ def close(how = :hard)
48
+ super()
49
+ return if how == :soft
47
50
 
48
- clear
49
- @ack_queue.close
50
- end
51
+ clear
52
+ @ack_queue.close
53
+ end
51
54
 
52
- def push(obj, ack:)
53
- super(obj)
54
- if ack
55
- r = @ack_queue.pop # block until popped
56
- raise ClosedError, "The #{self.class::TYPE}-port is already closed" unless r == :done
55
+ def push(obj, ack:)
56
+ super(obj)
57
+ if ack
58
+ r = @ack_queue.pop # block until popped
59
+ raise ClosedError, "The #{self.class::TYPE}-port is already closed" unless r == :done
60
+ end
61
+ self
57
62
  end
58
- self
59
63
  end
64
+ private_constant :BaseQueue, :OutgoingQueue, :IncomingQueue
60
65
  end
61
- private_constant :BaseQueue, :OutgoingQueue, :IncomingQueue
62
66
  end
@@ -1,261 +1,272 @@
1
+ # shareable_constant_value: literal
2
+
1
3
  # Ruby 2.0+ backport of `Ractor` class
2
4
  # Extra private methods and instance variables all start with `ractor_`
3
- class Ractor
4
- require_relative '../tools/arguments'
5
-
6
- require_relative 'cloner'
7
- require_relative 'errors'
8
- require_relative 'queues'
9
- require_relative 'sharing'
10
-
11
- RactorThreadGroups = ::ObjectSpace::WeakMap.new # ThreadGroup => Ractor
12
- private_constant :RactorThreadGroups
13
- # Implementation notes
14
- #
15
- # Uses one `Thread` for each `Ractor`, as well as queues for communication
16
- #
17
- # The incoming queue is strict: contrary to standard queue, you can't pop from an empty closed queue.
18
- # Since standard queues return `nil` is those conditions, we wrap/unwrap `nil` values and consider
19
- # all `nil` values to be results of closed queues. `ClosedQueueError` are re-raised as `Ractor::ClosedError`
20
- #
21
- # The outgoing queue is strict and blocking. Same wrapping / raising as incoming,
22
- # with an extra queue to acknowledge when a value has been read (or if the port is closed while waiting).
23
- #
24
- # The last result is a bit tricky as it needs to be pushed on the outgoing queue but can not be blocking.
25
- # For this, we "soft close" the outgoing port.
26
-
27
- def initialize(*args, &block)
28
- @ractor_incoming_queue = IncomingQueue.new
29
- @ractor_outgoing_queue = OutgoingQueue.new
30
- raise ArgumentError, 'must be called with a block' unless block
31
-
32
- kw = args.last
33
- if kw.is_a?(Hash) && kw.size == 1 && kw.key?(:name)
34
- args.pop
35
- name = kw[:name]
36
- end
37
- @ractor_name = name && Backports.coerce_to_str(name)
38
-
39
- if Ractor.main == nil # then initializing main Ractor
40
- @ractor_thread = ::Thread.current
41
- @ractor_origin = nil
42
- @ractor_thread.thread_variable_set(:backports_ractor, self)
43
- else
44
- @ractor_origin = caller(1, 1).first.split(':in `').first
45
-
46
- args.map! { |a| Ractor.ractor_isolate(a, false) }
47
- ractor_thread_start(args, block)
5
+ module Backports
6
+ class Ractor
7
+ require_relative '../tools/arguments'
8
+
9
+ require_relative 'cloner'
10
+ require_relative 'errors'
11
+ require_relative 'queues'
12
+ require_relative 'sharing'
13
+
14
+ RactorThreadGroups = ::ObjectSpace::WeakMap.new # ThreadGroup => Ractor
15
+ private_constant :RactorThreadGroups
16
+ # Implementation notes
17
+ #
18
+ # Uses one `Thread` for each `Ractor`, as well as queues for communication
19
+ #
20
+ # The incoming queue is strict: contrary to standard queue, you can't pop from an empty closed queue.
21
+ # Since standard queues return `nil` is those conditions, we wrap/unwrap `nil` values and consider
22
+ # all `nil` values to be results of closed queues. `ClosedQueueError` are re-raised as `Ractor::ClosedError`
23
+ #
24
+ # The outgoing queue is strict and blocking. Same wrapping / raising as incoming,
25
+ # with an extra queue to acknowledge when a value has been read (or if the port is closed while waiting).
26
+ #
27
+ # The last result is a bit tricky as it needs to be pushed on the outgoing queue but can not be blocking.
28
+ # For this, we "soft close" the outgoing port.
29
+
30
+ def initialize(*args, &block)
31
+ @ractor_incoming_queue = IncomingQueue.new
32
+ @ractor_outgoing_queue = OutgoingQueue.new
33
+ raise ::ArgumentError, 'must be called with a block' unless block
34
+
35
+ kw = args.last
36
+ if kw.is_a?(::Hash) && kw.size == 1 && kw.key?(:name)
37
+ args.pop
38
+ name = kw[:name]
39
+ end
40
+ @ractor_name = name && Backports.coerce_to_str(name)
41
+
42
+ @id = Ractor.ractor_next_id
43
+ if Ractor.main == nil # then initializing main Ractor
44
+ @ractor_thread = ::Thread.current
45
+ @ractor_origin = nil
46
+ @ractor_thread.thread_variable_set(:backports_ractor, self)
47
+ else
48
+ @ractor_origin = caller(1, 1).first.split(':in `').first
49
+
50
+ args.map! { |a| Ractor.ractor_isolate(a, false) }
51
+ ractor_thread_start(args, block)
52
+ end
48
53
  end
49
- end
50
54
 
51
- private def ractor_thread_start(args, block)
52
- ::Thread.new do
53
- @ractor_thread = Thread.current
54
- @ractor_thread_group = ThreadGroup.new
55
- RactorThreadGroups[@ractor_thread_group] = self
56
- @ractor_thread_group.add(@ractor_thread)
57
- ::Thread.current.thread_variable_set(:backports_ractor, self)
58
- result = nil
59
- begin
60
- result = instance_exec(*args, &block)
61
- rescue ::Exception => err # rubocop:disable Lint/RescueException
55
+ private def ractor_thread_start(args, block)
56
+ ::Thread.new do
57
+ @ractor_thread = ::Thread.current
58
+ @ractor_thread_group = ::ThreadGroup.new
59
+ RactorThreadGroups[@ractor_thread_group] = self
60
+ @ractor_thread_group.add(@ractor_thread)
61
+ ::Thread.current.thread_variable_set(:backports_ractor, self)
62
+ result = nil
62
63
  begin
63
- raise RemoteError, "thrown by remote Ractor: #{err.message}"
64
- rescue RemoteError => e # Hack to create exception with `cause`
65
- result = OutgoingQueue::WrappedException.new(e)
64
+ result = instance_exec(*args, &block)
65
+ rescue ::Exception => err # rubocop:disable Lint/RescueException
66
+ begin
67
+ raise RemoteError, "thrown by remote Ractor: #{err.message}"
68
+ rescue RemoteError => e # Hack to create exception with `cause`
69
+ result = OutgoingQueue::WrappedException.new(e)
70
+ end
71
+ ensure
72
+ ractor_thread_terminate(result)
66
73
  end
67
- ensure
68
- ractor_thread_terminate(result)
69
74
  end
70
75
  end
71
- end
72
76
 
73
- private def ractor_thread_terminate(result)
74
- begin
75
- ractor_outgoing_queue.push(result, ack: false) unless ractor_outgoing_queue.closed?
76
- rescue ClosedQueueError
77
- return # ignore
78
- end
79
- ractor_incoming_queue.close
80
- ractor_outgoing_queue.close(:soft)
81
- ensure
82
- # TODO: synchronize?
83
- @ractor_thread_group.list.each do |thread|
84
- thread.kill unless thread == Thread.current
77
+ private def ractor_thread_terminate(result)
78
+ begin
79
+ ractor_outgoing_queue.push(result, ack: false) unless ractor_outgoing_queue.closed?
80
+ rescue ::ClosedQueueError
81
+ return # ignore
82
+ end
83
+ ractor_incoming_queue.close
84
+ ractor_outgoing_queue.close(:soft)
85
+ ensure
86
+ # TODO: synchronize?
87
+ @ractor_thread_group.list.each do |thread|
88
+ thread.kill unless thread == Thread.current
89
+ end
85
90
  end
86
- end
87
-
88
- def send(obj, move: false)
89
- ractor_incoming_queue << Ractor.ractor_isolate(obj, move)
90
- self
91
- rescue ::ClosedQueueError
92
- raise ClosedError, 'The incoming-port is already closed'
93
- end
94
- alias_method :<<, :send
95
-
96
- def take
97
- ractor_outgoing_queue.pop(ack: true)
98
- end
99
-
100
- def name
101
- @ractor_name
102
- end
103
91
 
104
- RACTOR_STATE = {
105
- 'sleep' => 'blocking',
106
- 'run' => 'running',
107
- 'aborting' => 'aborting',
108
- false => 'terminated',
109
- nil => 'terminated',
110
- }.freeze
111
- private_constant :RACTOR_STATE
112
-
113
- def inspect
114
- state = RACTOR_STATE[@ractor_thread ? @ractor_thread.status : 'run']
115
- info = [
116
- 'Ractor:#1',
117
- name,
118
- @ractor_origin,
119
- state
120
- ].compact.join(' ')
121
-
122
- "#<#{info}>"
123
- end
92
+ def send(obj, move: false)
93
+ ractor_incoming_queue << Ractor.ractor_isolate(obj, move)
94
+ self
95
+ rescue ::ClosedQueueError
96
+ raise ClosedError, 'The incoming-port is already closed'
97
+ end
98
+ alias_method :<<, :send
124
99
 
125
- def close_incoming
126
- r = ractor_incoming_queue.closed?
127
- ractor_incoming_queue.close
128
- r
129
- end
100
+ def take
101
+ ractor_outgoing_queue.pop(ack: true)
102
+ end
130
103
 
131
- def close_outgoing
132
- r = ractor_outgoing_queue.closed?
133
- ractor_outgoing_queue.close
134
- r
135
- end
104
+ def name
105
+ @ractor_name
106
+ end
136
107
 
137
- private def receive
138
- ractor_incoming_queue.pop
139
- end
108
+ RACTOR_STATE = {
109
+ 'sleep' => 'blocking',
110
+ 'run' => 'running',
111
+ 'aborting' => 'aborting',
112
+ false => 'terminated',
113
+ nil => 'terminated',
114
+ }.freeze
115
+ private_constant :RACTOR_STATE
116
+
117
+ def inspect
118
+ state = RACTOR_STATE[@ractor_thread ? @ractor_thread.status : 'run']
119
+ info = [
120
+ "Ractor:##{@id}",
121
+ name,
122
+ @ractor_origin,
123
+ state,
124
+ ].compact.join(' ')
125
+
126
+ "#<#{info}>"
127
+ end
140
128
 
141
- private def receive_if(&block)
142
- raise ArgumentError, 'no block given' unless block
143
- ractor_incoming_queue.pop(&block)
144
- end
129
+ def close_incoming
130
+ r = ractor_incoming_queue.closed?
131
+ ractor_incoming_queue.close
132
+ r
133
+ end
145
134
 
146
- def [](key)
147
- Ractor.current.ractor_locals[key]
148
- end
135
+ def close_outgoing
136
+ r = ractor_outgoing_queue.closed?
137
+ ractor_outgoing_queue.close
138
+ r
139
+ end
149
140
 
150
- def []=(key, value)
151
- Ractor.current.ractor_locals[key] = value
152
- end
141
+ private def receive
142
+ ractor_incoming_queue.pop
143
+ end
153
144
 
154
- # @api private
155
- def ractor_locals
156
- @ractor_locals ||= {}.compare_by_identity
157
- end
145
+ private def receive_if(&block)
146
+ raise ::ArgumentError, 'no block given' unless block
147
+ ractor_incoming_queue.pop(&block)
148
+ end
158
149
 
159
- class << self
160
- def yield(value, move: false)
161
- value = ractor_isolate(value, move)
162
- current.ractor_outgoing_queue.push(value, ack: true)
163
- rescue ClosedQueueError
164
- raise ClosedError, 'The outgoing-port is already closed'
150
+ def [](key)
151
+ Ractor.current.ractor_locals[key]
165
152
  end
166
153
 
167
- def receive
168
- current.__send__(:receive)
154
+ def []=(key, value)
155
+ Ractor.current.ractor_locals[key] = value
169
156
  end
170
- alias_method :recv, :receive
171
157
 
172
- def receive_if(&block)
173
- current.__send__(:receive_if, &block)
158
+ # @api private
159
+ def ractor_locals
160
+ @ractor_locals ||= {}.compare_by_identity
174
161
  end
175
162
 
176
- def select(*ractors, yield_value: not_given = true, move: false)
177
- cur = Ractor.current
178
- queues = ractors.map do |r|
179
- r == cur ? r.ractor_incoming_queue : r.ractor_outgoing_queue
163
+ class << self
164
+ def yield(value, move: false)
165
+ value = ractor_isolate(value, move)
166
+ current.ractor_outgoing_queue.push(value, ack: true)
167
+ rescue ::ClosedQueueError
168
+ raise ClosedError, 'The outgoing-port is already closed'
180
169
  end
181
- if !not_given
182
- out = current.ractor_outgoing_queue
183
- yield_value = ractor_isolate(yield_value, move)
184
- elsif ractors.empty?
185
- raise ArgumentError, 'specify at least one ractor or `yield_value`'
170
+
171
+ def receive
172
+ current.__send__(:receive)
186
173
  end
174
+ alias_method :recv, :receive
187
175
 
188
- while true # rubocop:disable Style/InfiniteLoop
189
- # Don't `loop`, in case of `ClosedError` (not that there should be any)
190
- queues.each_with_index do |q, i|
191
- q.pop_non_blocking do |val|
192
- r = ractors[i]
193
- return [r == cur ? :receive : r, val]
194
- end
195
- end
176
+ def receive_if(&block)
177
+ current.__send__(:receive_if, &block)
178
+ end
196
179
 
197
- if out && out.num_waiting > 0
198
- # Not quite atomic...
199
- out.push(yield_value, ack: true)
200
- return [:yield, nil]
180
+ def select(*ractors, yield_value: not_given = true, move: false)
181
+ cur = Ractor.current
182
+ queues = ractors.map do |r|
183
+ r == cur ? r.ractor_incoming_queue : r.ractor_outgoing_queue
184
+ end
185
+ if !not_given
186
+ out = current.ractor_outgoing_queue
187
+ yield_value = ractor_isolate(yield_value, move)
188
+ elsif ractors.empty?
189
+ raise ::ArgumentError, 'specify at least one ractor or `yield_value`'
201
190
  end
202
191
 
203
- sleep(0.001)
192
+ while true # rubocop:disable Style/InfiniteLoop
193
+ # Don't `loop`, in case of `ClosedError` (not that there should be any)
194
+ queues.each_with_index do |q, i|
195
+ q.pop_non_blocking do |val|
196
+ r = ractors[i]
197
+ return [r == cur ? :receive : r, val]
198
+ end
199
+ end
200
+
201
+ if out && out.num_waiting > 0
202
+ # Not quite atomic...
203
+ out.push(yield_value, ack: true)
204
+ return [:yield, nil]
205
+ end
206
+
207
+ sleep(0.001)
208
+ end
204
209
  end
205
- end
206
210
 
207
- def make_shareable(obj)
208
- return obj if ractor_check_shareability?(obj, true)
211
+ def make_shareable(obj)
212
+ return obj if ractor_check_shareability?(obj, true)
209
213
 
210
- raise Ractor::Error, '#freeze does not freeze object correctly'
211
- end
214
+ raise Ractor::Error, '#freeze does not freeze object correctly'
215
+ end
212
216
 
213
- def shareable?(obj)
214
- ractor_check_shareability?(obj, false)
215
- end
217
+ def shareable?(obj)
218
+ ractor_check_shareability?(obj, false)
219
+ end
216
220
 
217
- def current
218
- Thread.current.thread_variable_get(:backports_ractor) ||
219
- Thread.current.thread_variable_set(:backports_ractor, ractor_find_current)
220
- end
221
+ def current
222
+ ::Thread.current.thread_variable_get(:backports_ractor) ||
223
+ ::Thread.current.thread_variable_set(:backports_ractor, ractor_find_current)
224
+ end
221
225
 
222
- def count
223
- ObjectSpace.each_object(Ractor).count(&:ractor_live?)
224
- end
226
+ def count
227
+ ::ObjectSpace.each_object(Ractor).count(&:ractor_live?)
228
+ end
225
229
 
226
- # @api private
227
- def ractor_reset
228
- ObjectSpace.each_object(Ractor).each do |r|
229
- next if r == Ractor.current
230
- next unless (th = r.ractor_thread)
230
+ # @api private
231
+ def ractor_reset
232
+ ::ObjectSpace.each_object(Ractor).each do |r|
233
+ next if r == Ractor.current
234
+ next unless (th = r.ractor_thread)
231
235
 
232
- th.kill
233
- th.join
236
+ th.kill
237
+ th.join
238
+ end
239
+ Ractor.current.ractor_incoming_queue.clear
234
240
  end
235
- Ractor.current.ractor_incoming_queue.clear
236
- end
237
241
 
238
- attr_reader :main
242
+ # @api private
243
+ def ractor_next_id
244
+ @id ||= 0
245
+ @id += 1
246
+ end
239
247
 
240
- private def ractor_init
241
- @ractor_shareable = ::ObjectSpace::WeakMap.new
242
- @main = Ractor.new { nil }
243
- RactorThreadGroups[::ThreadGroup::Default] = @main
244
- end
248
+ attr_reader :main
245
249
 
246
- private def ractor_find_current
247
- RactorThreadGroups[Thread.current.group]
250
+ private def ractor_init
251
+ @ractor_shareable = ::ObjectSpace::WeakMap.new
252
+ @main = Ractor.new { nil }
253
+ RactorThreadGroups[::ThreadGroup::Default] = @main
254
+ end
255
+
256
+ private def ractor_find_current
257
+ RactorThreadGroups[Thread.current.group]
258
+ end
248
259
  end
249
- end
250
260
 
251
- # @api private
252
- def ractor_live?
253
- !defined?(@ractor_thread) || # May happen if `count` is called from another thread before `initialize` has completed
254
- @ractor_thread.status
255
- end
261
+ # @api private
262
+ def ractor_live?
263
+ !defined?(@ractor_thread) || # May happen if `count` is called from another thread before `initialize` has completed
264
+ @ractor_thread.status
265
+ end
256
266
 
257
- # @api private
258
- attr_reader :ractor_outgoing_queue, :ractor_incoming_queue, :ractor_thread
267
+ # @api private
268
+ attr_reader :ractor_outgoing_queue, :ractor_incoming_queue, :ractor_thread
259
269
 
260
- ractor_init
270
+ ractor_init
271
+ end
261
272
  end
@@ -1,92 +1,96 @@
1
- class Ractor
2
- class << self
3
- # @api private
4
- def ractor_isolate(val, move = false)
5
- return val if move
1
+ # shareable_constant_value: literal
6
2
 
7
- Cloner.deep_clone(val)
8
- end
3
+ module Backports
4
+ class Ractor
5
+ class << self
6
+ # @api private
7
+ def ractor_isolate(val, move = false)
8
+ return val if move
9
9
 
10
- private def ractor_check_shareability?(obj, freeze_all)
11
- ractor_shareable_self?(obj, freeze_all) do
12
- visited = {}
10
+ Cloner.deep_clone(val)
11
+ end
13
12
 
14
- return false unless ractor_shareable_parts?(obj, freeze_all, visited)
13
+ private def ractor_check_shareability?(obj, freeze_all)
14
+ ractor_shareable_self?(obj, freeze_all) do
15
+ visited = {}
15
16
 
16
- ractor_mark_set_shareable(visited)
17
+ return false unless ractor_shareable_parts?(obj, freeze_all, visited)
17
18
 
18
- true
19
- end
20
- end
19
+ ractor_mark_set_shareable(visited)
21
20
 
22
- # yield if shareability can't be determined without looking at its parts
23
- def ractor_shareable_self?(obj, freeze_all)
24
- return true if @ractor_shareable.key?(obj)
25
- return true if ractor_shareable_by_nature?(obj, freeze_all)
26
- if obj.frozen? || (freeze_all && obj.freeze)
27
- yield
28
- else
29
- false
21
+ true
22
+ end
30
23
  end
31
- end
32
-
33
- private def ractor_shareable_parts?(obj, freeze_all, visited)
34
- return true if visited.key?(obj)
35
- visited[obj] = true
36
24
 
37
- ractor_traverse(obj) do |part|
38
- return false unless ractor_shareable_self?(part, freeze_all) do
39
- ractor_shareable_parts?(part, freeze_all, visited)
25
+ # yield if shareability can't be determined without looking at its parts
26
+ def ractor_shareable_self?(obj, freeze_all)
27
+ return true if @ractor_shareable.key?(obj)
28
+ return true if ractor_shareable_by_nature?(obj, freeze_all)
29
+ if obj.frozen? || (freeze_all && obj.freeze)
30
+ yield
31
+ else
32
+ false
40
33
  end
41
34
  end
42
35
 
43
- true
44
- end
36
+ private def ractor_shareable_parts?(obj, freeze_all, visited)
37
+ return true if visited.key?(obj)
38
+ visited[obj] = true
39
+
40
+ ractor_traverse(obj) do |part|
41
+ return false unless ractor_shareable_self?(part, freeze_all) do
42
+ ractor_shareable_parts?(part, freeze_all, visited)
43
+ end
44
+ end
45
45
 
46
- def ractor_mark_set_shareable(visited)
47
- visited.each do |key|
48
- @ractor_shareable[key] = Ractor
46
+ true
49
47
  end
50
- end
51
48
 
52
- private def ractor_traverse(obj, &block)
53
- case obj
54
- when ::Hash
55
- Hash obj.default
56
- yield obj.default_proc
57
- obj.each do |key, value|
58
- yield key
59
- yield value
49
+ def ractor_mark_set_shareable(visited)
50
+ visited.each do |key|
51
+ @ractor_shareable[key] = Ractor
60
52
  end
61
- when ::Range
62
- yield obj.begin
63
- yield obj.end
64
- when ::Array, ::Struct
65
- obj.each(&block)
66
- when ::Complex
67
- yield obj.real
68
- yield obj.imaginary
69
- when ::Rational
70
- yield obj.numerator
71
- yield obj.denominator
72
53
  end
73
- obj.instance_variables.each do |var|
74
- yield obj.instance_variable_get(var)
54
+
55
+ private def ractor_traverse(obj, &block)
56
+ case obj
57
+ when ::Hash
58
+ Hash obj.default
59
+ yield obj.default_proc
60
+ obj.each do |key, value|
61
+ yield key
62
+ yield value
63
+ end
64
+ when ::Range
65
+ yield obj.begin
66
+ yield obj.end
67
+ when ::Array, ::Struct
68
+ obj.each(&block)
69
+ when ::Complex
70
+ yield obj.real
71
+ yield obj.imaginary
72
+ when ::Rational
73
+ yield obj.numerator
74
+ yield obj.denominator
75
+ end
76
+ obj.instance_variables.each do |var|
77
+ yield obj.instance_variable_get(var)
78
+ end
75
79
  end
76
- end
77
80
 
78
- private def ractor_shareable_by_nature?(obj, freeze_all)
79
- case obj
80
- when ::Module, ::Ractor
81
- true
82
- when ::Regexp, ::Range, ::Numeric
83
- !freeze_all # Assume that these are literals that would have been frozen in 3.0
84
- # unless we're making them shareable, in which case we might as well
85
- # freeze them for real.
86
- when ::Symbol, false, true, nil # Were only frozen in Ruby 2.3+
87
- true
88
- else
89
- false
81
+ private def ractor_shareable_by_nature?(obj, freeze_all)
82
+ case obj
83
+ when ::Module, Ractor
84
+ true
85
+ when ::Regexp, ::Range, ::Numeric
86
+ !freeze_all # Assume that these are literals that would have been frozen in 3.0
87
+ # unless we're making them shareable, in which case we might as well
88
+ # freeze them for real.
89
+ when ::Symbol, false, true, nil # Were only frozen in Ruby 2.3+
90
+ true
91
+ else
92
+ false
93
+ end
90
94
  end
91
95
  end
92
96
  end
@@ -1,3 +1,3 @@
1
1
  module Backports
2
- VERSION = "3.20.1" unless Backports.constants.include? :VERSION # the guard is against a redefinition warning that happens on Travis
2
+ VERSION = "3.20.2" unless Backports.constants.include? :VERSION # the guard is against a redefinition warning that happens on Travis
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: backports
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.20.1
4
+ version: 3.20.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc-André Lafortune
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-03 00:00:00.000000000 Z
11
+ date: 2021-01-27 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Essential backports that enable many of the nice features of Ruby for
14
14
  earlier versions.
@@ -551,6 +551,7 @@ files:
551
551
  - lib/backports/latest.rb
552
552
  - lib/backports/ractor/cloner.rb
553
553
  - lib/backports/ractor/errors.rb
554
+ - lib/backports/ractor/filtered_queue.rb
554
555
  - lib/backports/ractor/queues.rb
555
556
  - lib/backports/ractor/ractor.rb
556
557
  - lib/backports/ractor/sharing.rb
@@ -572,7 +573,6 @@ files:
572
573
  - lib/backports/tools/arguments.rb
573
574
  - lib/backports/tools/deprecation.rb
574
575
  - lib/backports/tools/extreme_object.rb
575
- - lib/backports/tools/filtered_queue.rb
576
576
  - lib/backports/tools/float_integer_conversion.rb
577
577
  - lib/backports/tools/io.rb
578
578
  - lib/backports/tools/make_block_optional.rb