async-safe 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e20fdea7f6f63f3a47aa8be85492fa2725e6b3ea2c0284de63dd6fe45af6f49c
4
+ data.tar.gz: 6556e3ed8f46cfe8157983229d4584a6b01eb0e8f2b36f83fe3ec5f541211830
5
+ SHA512:
6
+ metadata.gz: 6a02637fc476e5e38a6b1952394ad99ca3fbe13b106b6d7ac0abbafc7f285167f3a0ef9302a28ac0e12f29a70edce7158ea2690b88d309e99a5d1f073da58580
7
+ data.tar.gz: 24fbc11d3be799f80649e1a487edc6f1b4040fbb0e4688d590f2caafceb76b761c298569a3b2384939844753eec8b788ff0bf70d8f820b3fbe4c9d9403183542
checksums.yaml.gz.sig ADDED
Binary file
@@ -0,0 +1,176 @@
1
+ # Getting Started
2
+
3
+ This guide explains how to use `async-safe` to detect thread safety violations in your Ruby code.
4
+
5
+ ## Installation
6
+
7
+ Add the gem to your project:
8
+
9
+ ~~~ bash
10
+ $ bundle add async-safe
11
+ ~~~
12
+
13
+ ## Usage
14
+
15
+ Enable monitoring in your test suite or development environment:
16
+
17
+ ~~~ ruby
18
+ require 'async/safe'
19
+
20
+ # Enable monitoring
21
+ Async::Safe.enable!
22
+
23
+ # Your concurrent code here...
24
+ ~~~
25
+
26
+ When a violation is detected, an `Async::Safe::ViolationError` will be raised immediately with details about the object, method, and execution contexts involved.
27
+
28
+ ## Single-Owner Model
29
+
30
+ By default, all objects are assumed to follow a **single-owner model** - they should only be accessed from one fiber/thread at a time:
31
+
32
+ ~~~ ruby
33
+ class MyBody
34
+ def initialize(chunks)
35
+ @chunks = chunks
36
+ @index = 0
37
+ end
38
+
39
+ def read
40
+ chunk = @chunks[@index]
41
+ @index += 1
42
+ chunk
43
+ end
44
+ end
45
+
46
+ body = MyBody.new(["a", "b", "c"])
47
+ body.read # OK - accessed from main fiber
48
+
49
+ Fiber.schedule do
50
+ body.read # 💥 Raises Async::Safe::ViolationError!
51
+ end
52
+ ~~~
53
+
54
+ ## Marking Async-Safe Classes
55
+
56
+ Mark entire classes as safe for concurrent access:
57
+
58
+ ~~~ ruby
59
+ class MyQueue
60
+ async_safe!
61
+
62
+ def initialize
63
+ @queue = Thread::Queue.new
64
+ end
65
+
66
+ def push(item)
67
+ @queue.push(item)
68
+ end
69
+
70
+ def pop
71
+ @queue.pop
72
+ end
73
+ end
74
+
75
+ queue = MyQueue.new
76
+ queue.push("item")
77
+
78
+ Fiber.schedule do
79
+ queue.push("another") # ✅ OK - class is marked async-safe
80
+ end
81
+ ~~~
82
+
83
+ Alternatively, you can manually set the constant:
84
+
85
+ ~~~ ruby
86
+ class MyQueue
87
+ ASYNC_SAFE = true
88
+
89
+ # ... implementation
90
+ end
91
+ ~~~
92
+
93
+ ## Marking Async-Safe Methods
94
+
95
+ Mark specific methods as async-safe:
96
+
97
+ ~~~ ruby
98
+ class MixedSafety
99
+ include Async::Safe
100
+
101
+ async_safe :safe_read
102
+
103
+ def initialize(data)
104
+ @data = data
105
+ @count = 0
106
+ end
107
+
108
+ def safe_read
109
+ @data # Async-safe method
110
+ end
111
+
112
+ def increment
113
+ @count += 1 # Not async-safe
114
+ end
115
+ end
116
+
117
+ obj = MixedSafety.new("data")
118
+
119
+ Fiber.schedule do
120
+ obj.safe_read # ✅ OK - method is marked async-safe
121
+ obj.increment # 💥 Raises Async::Safe::ViolationError!
122
+ end
123
+ ~~~
124
+
125
+ ## Transferring Ownership
126
+
127
+ Explicitly transfer ownership between fibers:
128
+
129
+ ~~~ ruby
130
+ request = create_request
131
+ process_in_main_fiber(request)
132
+
133
+ Fiber.schedule do
134
+ Async::Safe.transfer(request) # Transfer ownership
135
+ process_in_worker_fiber(request) # ✅ OK now
136
+ end
137
+ ~~~
138
+
139
+ ## Integration with Tests
140
+
141
+ Add to your test helper (e.g., `config/sus.rb` or `spec/spec_helper.rb`):
142
+
143
+ ~~~ ruby
144
+ require 'async/safe'
145
+
146
+ Async::Safe.enable!
147
+ ~~~
148
+
149
+ Then run your tests normally:
150
+
151
+ ~~~ bash
152
+ $ bundle exec sus
153
+ ~~~
154
+
155
+ Any thread safety violations will cause your tests to fail immediately with a clear error message showing which object was accessed incorrectly and from which fibers.
156
+
157
+ ## How It Works
158
+
159
+ 1. **Default Assumption**: All objects follow a single-owner model (not thread-safe).
160
+ 2. **TracePoint Monitoring**: Tracks which fiber/thread first accesses each object.
161
+ 3. **Violation Detection**: Raises an exception when a different fiber/thread accesses the same object.
162
+ 4. **Explicit Safety**: Objects/methods can be marked as thread-safe to allow concurrent access.
163
+ 5. **Zero Overhead**: Monitoring is only active when explicitly enabled.
164
+
165
+ ## Use Cases
166
+
167
+ - **Detecting concurrency bugs** in development and testing.
168
+ - **Validating thread safety assumptions** in async/fiber-based code.
169
+ - **Finding race conditions** before they cause production issues.
170
+ - **Educational tool** for learning about thread safety in Ruby.
171
+
172
+ ## Performance
173
+
174
+ - **Zero overhead when disabled** - TracePoint is not activated.
175
+ - **Minimal overhead when enabled** - suitable for development/test environments.
176
+ - **Not recommended for production** - use only in development/testing.
@@ -0,0 +1,12 @@
1
+ # Automatically generated context index for Utopia::Project guides.
2
+ # Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
3
+ ---
4
+ description: Runtime thread safety monitoring for concurrent Ruby code.
5
+ metadata:
6
+ documentation_uri: https://socketry.github.io/async-safe/
7
+ source_code_uri: https://github.com/socketry/async-safe
8
+ files:
9
+ - path: getting-started.md
10
+ title: Getting Started
11
+ description: This guide explains how to use `async-safe` to detect thread safety
12
+ violations in your Ruby code.
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ # Mark Ruby's built-in thread-safe classes as async-safe
7
+ #
8
+ # Note: Immutable values (nil, true, false, integers, symbols, etc.) are already
9
+ # handled by the frozen? check in the monitor and don't need to be listed here.
10
+
11
+ # Thread synchronization primitives:
12
+ Thread::ASYNC_SAFE = true
13
+ Thread::Queue::ASYNC_SAFE = true
14
+ Thread::SizedQueue::ASYNC_SAFE = true
15
+ Thread::Mutex::ASYNC_SAFE = true
16
+ Thread::ConditionVariable::ASYNC_SAFE = true
17
+
18
+ # Fibers are async-safe:
19
+ Fiber::ASYNC_SAFE = true
20
+
21
+ # ObjectSpace::WeakMap is async-safe:
22
+ ObjectSpace::WeakMap::ASYNC_SAFE = true
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ # Extend Class with a default async_safe? implementation
7
+ class Class
8
+ # Check if this class or a specific method is async-safe.
9
+ #
10
+ # @parameter method [Symbol | Nil] The method name to check, or nil to check if the entire class is async-safe.
11
+ # @returns [Boolean] Whether the class or method is async-safe.
12
+ def async_safe?(method = nil)
13
+ # Check if entire class is marked async-safe via constant:
14
+ if const_defined?(:ASYNC_SAFE, false) && const_get(:ASYNC_SAFE)
15
+ return true
16
+ end
17
+
18
+ false
19
+ end
20
+
21
+ # Mark the class as async-safe or not.
22
+ #
23
+ # @parameter value [Boolean] Whether the class is async-safe.
24
+ # @returns [Boolean] Whether the class is async-safe.
25
+ def async_safe!(value = true)
26
+ self.const_set(:ASYNC_SAFE, value)
27
+ end
28
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ require "set"
7
+ require "weakref"
8
+
9
+ module Async
10
+ module Safe
11
+ # Raised when an object is accessed from a different fiber than the one that owns it.
12
+ class ViolationError < StandardError
13
+ # Initialize a new violation error.
14
+ #
15
+ # @parameter message [String | Nil] Optional custom message.
16
+ # @parameter target [Object] The object that was accessed.
17
+ # @parameter method [Symbol] The method that was called.
18
+ # @parameter owner [Fiber] The fiber that owns the object.
19
+ # @parameter current [Fiber] The fiber that attempted to access the object.
20
+ def initialize(message = nil, target:, method:, owner:, current:)
21
+ @target = target
22
+ @method = method
23
+ @owner = owner
24
+ @current = current
25
+
26
+ super(message || build_message)
27
+ end
28
+
29
+ attr_reader :object_class, :method, :owner, :current
30
+
31
+ # Convert the violation error to a JSON-serializable hash.
32
+ #
33
+ # @returns [Hash] A hash representation of the violation.
34
+ def as_json
35
+ {
36
+ object_class: @object_class,
37
+ method: @method,
38
+ owner: {
39
+ name: @owner.inspect,
40
+ backtrace: @owner.backtrace,
41
+ },
42
+ current: {
43
+ name: @current.inspect,
44
+ backtrace: @current.backtrace,
45
+ },
46
+ }
47
+ end
48
+
49
+ private def build_message
50
+ "Thread safety violation detected! #{@target.inspect}##{@method} was accessed from #{@current.inspect} by #{@owner.inspect}."
51
+ end
52
+ end
53
+
54
+ # The core monitoring implementation using TracePoint.
55
+ #
56
+ # This class tracks object ownership across fibers, detecting when an object
57
+ # is accessed from a different fiber than the one that originally created or
58
+ # accessed it.
59
+ #
60
+ # The monitor uses a TracePoint on `:call` events to track all method calls,
61
+ # and maintains a registry of which fiber "owns" each object. Uses weak references
62
+ # to avoid preventing garbage collection of tracked objects.
63
+ class Monitor
64
+ ASYNC_SAFE = true
65
+
66
+ # Initialize a new monitor instance.
67
+ def initialize
68
+ @owners = ObjectSpace::WeakMap.new
69
+ @mutex = Thread::Mutex.new
70
+ @trace_point = nil
71
+ end
72
+
73
+ attr :owners
74
+
75
+ # Enable the monitor by activating the TracePoint.
76
+ def enable!
77
+ @trace_point ||= TracePoint.trace(:call, &method(:check_access))
78
+ end
79
+
80
+ # Disable the monitor by deactivating the TracePoint.
81
+ def disable!
82
+ if trace_point = @trace_point
83
+ @trace_point = nil
84
+ trace_point.disable
85
+ end
86
+ end
87
+
88
+ # Explicitly transfer ownership of objects to the current fiber.
89
+ #
90
+ # @parameter objects [Array(Object)] The objects to transfer.
91
+ def transfer(*objects)
92
+ @mutex.synchronize do
93
+ current = Fiber.current
94
+
95
+ objects.each do |object|
96
+ @owners[object] = current
97
+ end
98
+ end
99
+ end
100
+
101
+ # Check if the current access is allowed or constitutes a violation.
102
+ #
103
+ # @parameter trace_point [TracePoint] The trace point containing access information.
104
+ def check_access(trace_point)
105
+ object = trace_point.self
106
+
107
+ # Skip tracking class/module methods:
108
+ return if object.is_a?(Class) || object.is_a?(Module)
109
+
110
+ # Skip frozen objects:
111
+ return if object.frozen?
112
+
113
+ method = trace_point.method_id
114
+ klass = trace_point.defined_class
115
+
116
+ # Check the object's actual class:
117
+ klass = object.class
118
+
119
+ # Check if the class or method is marked as async-safe:
120
+ if klass.async_safe?(method)
121
+ return
122
+ end
123
+
124
+ # Track ownership:
125
+ current = Fiber.current
126
+
127
+ @mutex.synchronize do
128
+ if owner = @owners[object]
129
+ # Violation if accessed from different fiber:
130
+ if owner != current
131
+ raise ViolationError.new(
132
+ target: object,
133
+ method: method,
134
+ owner: owner,
135
+ current: current,
136
+ )
137
+ end
138
+ else
139
+ # First access - record owner:
140
+ @owners[object] = current
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ module Async
7
+ module Safe
8
+ VERSION = "0.1.0"
9
+ end
10
+ end
11
+
data/lib/async/safe.rb ADDED
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ require_relative "safe/version"
7
+ require_relative "safe/class"
8
+ require_relative "safe/monitor"
9
+ require_relative "safe/builtins"
10
+
11
+ # @namespace
12
+ module Async
13
+ # Provides runtime thread safety monitoring for concurrent Ruby code.
14
+ #
15
+ # By default, all objects follow a **single-owner model** - they should only be accessed
16
+ # from one fiber/thread at a time. Objects or methods can be explicitly marked as
17
+ # async-safe to allow concurrent access.
18
+ #
19
+ # Enable monitoring in your test suite to catch concurrency bugs early.
20
+ module Safe
21
+ # Include this module to mark specific methods as async-safe
22
+ def self.included(base)
23
+ base.extend(ClassMethods)
24
+ end
25
+
26
+ # Class methods for marking async-safe methods
27
+ module ClassMethods
28
+ # Mark one or more methods as async-safe.
29
+ #
30
+ # @parameter method_names [Array(Symbol)] The methods to mark as async-safe.
31
+ def async_safe(*method_names)
32
+ @async_safe_methods ||= Set.new
33
+ @async_safe_methods.merge(method_names)
34
+ end
35
+
36
+ # Check if a method is async-safe.
37
+ #
38
+ # Overrides the default implementation from `Class` to also check method-level safety.
39
+ #
40
+ # @parameter method [Symbol | Nil] The method name to check, or nil to check if the entire class is async-safe.
41
+ # @returns [Boolean] Whether the method or class is async-safe.
42
+ def async_safe?(method = nil)
43
+ # Check if entire class is marked async-safe:
44
+ return true if super
45
+
46
+ # Check if specific method is marked async-safe:
47
+ if method
48
+ return @async_safe_methods&.include?(method)
49
+ end
50
+
51
+ # Default to false if no method is specified and the class is not async safe:
52
+ return false
53
+ end
54
+ end
55
+
56
+ class << self
57
+ # @attribute [Monitor] The global monitoring instance.
58
+ attr_reader :monitor
59
+
60
+ # Enable thread safety monitoring.
61
+ #
62
+ # This activates a TracePoint that tracks object access across fibers and threads.
63
+ # There is no performance overhead when monitoring is disabled.
64
+ def enable!
65
+ @monitor ||= Monitor.new
66
+ @monitor.enable!
67
+ end
68
+
69
+ # Disable thread safety monitoring.
70
+ def disable!
71
+ @monitor&.disable!
72
+ @monitor = nil
73
+ end
74
+
75
+ # Explicitly transfer ownership of objects to the current fiber.
76
+ #
77
+ # This allows an object to be safely passed between fibers.
78
+ #
79
+ # @parameter objects [Array(Object)] The objects to transfer ownership of.
80
+ #
81
+ # ~~~ ruby
82
+ # request = Request.new(...)
83
+ #
84
+ # Fiber.schedule do
85
+ # # Transfer ownership of the request to this fiber:
86
+ # Async::Safe.transfer(request)
87
+ # process(request)
88
+ # end
89
+ # ~~~
90
+ def transfer(*objects)
91
+ @monitor&.transfer(*objects)
92
+ end
93
+ end
94
+ end
95
+ end
96
+
data/license.md ADDED
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright, 2025, by Samuel Williams.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/readme.md ADDED
@@ -0,0 +1,55 @@
1
+ # Async::Safe
2
+
3
+ Runtime thread safety monitoring for concurrent Ruby code.
4
+
5
+ This gem provides a TracePoint-based ownership tracking system that detects when objects are accessed from multiple fibers or threads without proper synchronization. It helps catch concurrency bugs during development and testing with zero overhead in production.
6
+
7
+ [![Development Status](https://github.com/socketry/async-safe/workflows/Test/badge.svg)](https://github.com/socketry/async-safe/actions?workflow=Test)
8
+
9
+ ## Motivation
10
+
11
+ Ruby's fiber-based concurrency (via `async`) requires careful attention to object ownership. This gem helps you catch violations of the single-owner model in your test suite, preventing concurrency bugs from reaching production.
12
+
13
+ Enable it in your tests to get immediate feedback when objects are incorrectly shared across fibers.
14
+
15
+ ## Usage
16
+
17
+ Please see the [project documentation](https://socketry.github.io/async-safe/) for more details.
18
+
19
+ - [Getting Started](https://socketry.github.io/async-safe/guides/getting-started/index) - This guide explains how to use `async-safe` to detect thread safety violations in your Ruby code.
20
+
21
+ ## Releases
22
+
23
+ Please see the [project releases](https://socketry.github.io/async-safe/releases/index) for all releases.
24
+
25
+ ### v0.1.0
26
+
27
+ - Implement TracePoint-based ownership tracking.
28
+ - Add `Async::Safe::Concurrent` module for marking thread-safe classes.
29
+ - Add `thread_safe` class method for marking thread-safe methods.
30
+ - Add `Async::Safe.transfer` for explicit ownership transfer
31
+ - Add violation detection and reporting
32
+ - Zero overhead when monitoring is disabled
33
+
34
+ ## See Also
35
+
36
+ - [async](https://github.com/socketry/async) - Composable asynchronous I/O for Ruby.
37
+ - [Thread Safety Guide](https://github.com/socketry/async/blob/main/.context/async/thread-safety.md) - Best practices for concurrent Ruby code.
38
+
39
+ ## Contributing
40
+
41
+ We welcome contributions to this project.
42
+
43
+ 1. Fork it.
44
+ 2. Create your feature branch (`git checkout -b my-new-feature`).
45
+ 3. Commit your changes (`git commit -am 'Add some feature'`).
46
+ 4. Push to the branch (`git push origin my-new-feature`).
47
+ 5. Create new Pull Request.
48
+
49
+ ### Developer Certificate of Origin
50
+
51
+ In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
52
+
53
+ ### Community Guidelines
54
+
55
+ This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers.
data/releases.md ADDED
@@ -0,0 +1,10 @@
1
+ # Releases
2
+
3
+ ## v0.1.0
4
+
5
+ - Implement TracePoint-based ownership tracking.
6
+ - Add `Async::Safe::Concurrent` module for marking thread-safe classes.
7
+ - Add `thread_safe` class method for marking thread-safe methods.
8
+ - Add `Async::Safe.transfer` for explicit ownership transfer
9
+ - Add violation detection and reporting
10
+ - Zero overhead when monitoring is disabled
data.tar.gz.sig ADDED
Binary file
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: async-safe
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Samuel Williams
8
+ bindir: bin
9
+ cert_chain:
10
+ - |
11
+ -----BEGIN CERTIFICATE-----
12
+ MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11
13
+ ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK
14
+ CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz
15
+ MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd
16
+ MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj
17
+ bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
18
+ igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2
19
+ 9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW
20
+ sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE
21
+ e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN
22
+ XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss
23
+ RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn
24
+ tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM
25
+ zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW
26
+ xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
27
+ BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs
28
+ aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs
29
+ aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE
30
+ cBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl
31
+ xCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/
32
+ c1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp
33
+ 8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws
34
+ JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP
35
+ eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt
36
+ Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
37
+ voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
38
+ -----END CERTIFICATE-----
39
+ date: 1980-01-02 00:00:00.000000000 Z
40
+ dependencies: []
41
+ executables: []
42
+ extensions: []
43
+ extra_rdoc_files: []
44
+ files:
45
+ - context/getting-started.md
46
+ - context/index.yaml
47
+ - lib/async/safe.rb
48
+ - lib/async/safe/builtins.rb
49
+ - lib/async/safe/class.rb
50
+ - lib/async/safe/monitor.rb
51
+ - lib/async/safe/version.rb
52
+ - license.md
53
+ - readme.md
54
+ - releases.md
55
+ homepage: https://github.com/socketry/async-safe
56
+ licenses:
57
+ - MIT
58
+ metadata:
59
+ documentation_uri: https://socketry.github.io/async-safe/
60
+ source_code_uri: https://github.com/socketry/async-safe
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '3.2'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubygems_version: 3.6.9
76
+ specification_version: 4
77
+ summary: Runtime thread safety monitoring for concurrent Ruby code.
78
+ test_files: []
metadata.gz.sig ADDED
Binary file