async-safe 0.4.0 → 0.4.1

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: 7859608fbc62080a81bb9f20ce2c9ffe8f299f76432d1ec9f4a3d4aa47291240
4
- data.tar.gz: e9a4f441e4ccdbfe0f086d6df486f22c7aa9504d07f42128689f54fd1befad4d
3
+ metadata.gz: 72500ee1207a863df7f749896936bf9edce7e06f2f7d7a519329c0ada72951eb
4
+ data.tar.gz: 584c0393294503ea01cf1e1858f88f3c2faf5e2cd6fdf1b2f632af0708e4d787
5
5
  SHA512:
6
- metadata.gz: 12d90b93da4da2d525d6bf9e14acf235f0a2c50bc8301cd265b88f333a9d8c9af55ed0a7d69cb992239af715f5cd680650de0d2b111fb474c3a487f1007acfdb
7
- data.tar.gz: 84d82aa42d46b54494ac5f4c8b025c80f41b2c8ccb7e1412d89a5d1073c662f9b86c7a84dce7c782c73e97164fc8a2df0a5d064bb4dac83a9e0de3d7a85cc264
6
+ metadata.gz: 305ea4886f6b9fa0452de045244623806ed824ba24791ed0d2d1e6bf96a14f536f31fccbd54f099e5cc10ee1dc39067609be9b646daac413c47126a86f0306a2
7
+ data.tar.gz: 78dd7ab4aab4893c2824788e03522905698d6f400557976677253fbcc53643b4a0dbaaa8a8157249415f9380a317ffddc126948bb484fa2b6c795513abf4fc2d
checksums.yaml.gz.sig CHANGED
Binary file
@@ -12,6 +12,10 @@
12
12
  class Array
13
13
  ASYNC_SAFE = false
14
14
 
15
+ # Traverse array elements during ownership transfer.
16
+ #
17
+ # @parameter instance [Array] The array instance to traverse.
18
+ # @parameter block [Proc] Block to call for each element.
15
19
  def self.async_safe_traverse(instance, &block)
16
20
  instance.each(&block)
17
21
  end
@@ -21,6 +25,10 @@ end
21
25
  class Hash
22
26
  ASYNC_SAFE = false
23
27
 
28
+ # Traverse hash keys and values during ownership transfer.
29
+ #
30
+ # @parameter instance [Hash] The hash instance to traverse.
31
+ # @parameter block [Proc] Block to call for each key and value.
24
32
  def self.async_safe_traverse(instance, &block)
25
33
  instance.each_key(&block)
26
34
  instance.each_value(&block)
@@ -31,6 +39,10 @@ end
31
39
  class Set
32
40
  ASYNC_SAFE = false
33
41
 
42
+ # Traverse set elements during ownership transfer.
43
+ #
44
+ # @parameter instance [Set] The set instance to traverse.
45
+ # @parameter block [Proc] Block to call for each element.
34
46
  def self.async_safe_traverse(instance, &block)
35
47
  instance.each(&block)
36
48
  end
@@ -6,6 +6,9 @@
6
6
  require "set"
7
7
  require "weakref"
8
8
 
9
+ # Fiber-local variable to track when we're in a transfer operation:
10
+ Fiber.attr_accessor :async_safe_transfer
11
+
9
12
  module Async
10
13
  module Safe
11
14
  # Raised when an object is accessed from a different fiber than the one that owns it.
@@ -95,28 +98,31 @@ module Async
95
98
  current = Fiber.current
96
99
  visited = Set.new
97
100
 
98
- # Traverse object graph (outside mutex to avoid deadlock):
99
- objects.each do |object|
100
- traverse_objects(object, visited)
101
- end
102
-
103
- # Transfer all visited objects (convert to array to avoid triggering TracePoint in sync block):
104
- objects_to_transfer = visited.to_a
101
+ # Disable tracking during traversal to avoid deadlock:
102
+ current.async_safe_transfer = true
105
103
 
106
- @mutex.synchronize do
107
- objects_to_transfer.each do |object|
108
- @owners[object] = current if @owners.key?(object)
104
+ begin
105
+ # Traverse object graph:
106
+ objects.each do |object|
107
+ traverse_objects(object, visited)
109
108
  end
109
+
110
+ # Transfer all visited objects:
111
+ @mutex.synchronize do
112
+ visited.each do |object|
113
+ @owners[object] = current if @owners.key?(object)
114
+ end
115
+ end
116
+ ensure
117
+ current.async_safe_transfer = false
110
118
  end
111
119
  end
112
120
 
113
- private
114
-
115
121
  # Traverse the object graph and collect all reachable objects.
116
122
  #
117
123
  # @parameter object [Object] The object to traverse.
118
124
  # @parameter visited [Set] Set of visited objects (object references, not IDs).
119
- def traverse_objects(object, visited)
125
+ private def traverse_objects(object, visited)
120
126
  # Avoid circular references:
121
127
  return if visited.include?(object)
122
128
 
@@ -140,6 +146,9 @@ module Async
140
146
  #
141
147
  # @parameter trace_point [TracePoint] The trace point containing access information.
142
148
  def check_access(trace_point)
149
+ # Skip if we're in a transfer operation:
150
+ return if Fiber.current.async_safe_transfer
151
+
143
152
  object = trace_point.self
144
153
 
145
154
  # Skip tracking class/module methods:
@@ -5,7 +5,7 @@
5
5
 
6
6
  module Async
7
7
  module Safe
8
- VERSION = "0.4.0"
8
+ VERSION = "0.4.1"
9
9
  end
10
10
  end
11
11
 
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async-safe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
metadata.gz.sig CHANGED
Binary file