backports 3.20.1 → 3.20.2

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 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