File tree Expand file tree Collapse file tree 6 files changed +76
-0
lines changed
Expand file tree Collapse file tree 6 files changed +76
-0
lines changed Original file line number Diff line number Diff line change 55
66require "covered/sus"
77include Covered ::Sus
8+
9+ require "async/safe"
10+ Async ::Safe . enable!
Original file line number Diff line number Diff line change 1+ #!/usr/bin/env ruby
2+ # frozen_string_literal: true
3+
4+ require_relative "../../lib/io/stream"
5+
6+ require "async"
7+ require "async/safe"
8+
9+ # Enable concurrent access detection:
10+ Async ::Safe . enable!
11+
12+ # Create a simple stream with some data:
13+ input , output = IO . pipe
14+ stream = IO ::Stream ::Buffered . new ( input )
15+ output . write ( "Hello" )
16+
17+ Async do |task |
18+ task . async ( transient : true ) do
19+ data = stream . read ( 10 )
20+ end
21+
22+ # This should raise ViolationError:
23+ stream . read ( 10 )
24+ end
Original file line number Diff line number Diff line change 3131
3232 gem "sus-fixtures-async"
3333 gem "sus-fixtures-openssl"
34+
35+ gem "async-safe"
3436end
Original file line number Diff line number Diff line change @@ -18,6 +18,14 @@ class Generic
1818 include Readable
1919 include Writable
2020
21+ # Check if a method is async-safe.
22+ #
23+ # @parameter method [Symbol] The method name to check.
24+ # @returns [Symbol | Boolean] The concurrency guard for the given method.
25+ def self . async_safe? ( method )
26+ Readable . async_safe? ( method ) || Writable . async_safe? ( method )
27+ end
28+
2129 # Initialize a new generic stream.
2230 # @parameter options [Hash] Options passed to included modules.
2331 def initialize ( **options )
Original file line number Diff line number Diff line change @@ -25,6 +25,31 @@ module IO::Stream
2525 #
2626 # You must implement the `sysread` method to read data from the underlying IO.
2727 module Readable
28+ ASYNC_SAFE = {
29+ read : :readable ,
30+ read_partial : :readable ,
31+ read_exactly : :readable ,
32+ read_until : :readable ,
33+ peek : :readable ,
34+ gets : :readable ,
35+ getc : :readable ,
36+ getbyte : :readable ,
37+ readline : :readable ,
38+ readlines : :readable ,
39+ readable? : :readable ,
40+ fill_read_buffer : :readable ,
41+ eof? : :readable ,
42+ finished? : :readable ,
43+ } . freeze
44+
45+ # Check if a method is async-safe.
46+ #
47+ # @parameter method [Symbol] The method name to check.
48+ # @returns [Symbol | Boolean] The concurrency guard for the given method.
49+ def self . async_safe? ( method )
50+ ASYNC_SAFE . fetch ( method , false )
51+ end
52+
2853 # Initialize readable stream functionality.
2954 # @parameter minimum_read_size [Integer] The minimum size for read operations.
3055 # @parameter maximum_read_size [Integer] The maximum size for read operations.
Original file line number Diff line number Diff line change @@ -13,6 +13,20 @@ module IO::Stream
1313 #
1414 # You must implement the `syswrite` method to write data to the underlying IO.
1515 module Writable
16+ ASYNC_SAFE = {
17+ write : true ,
18+ puts : true ,
19+ flush : true ,
20+ } . freeze
21+
22+ # Check if a method is async-safe.
23+ #
24+ # @parameter method [Symbol] The method name to check.
25+ # @returns [Symbol | Boolean] The concurrency guard for the given method.
26+ def self . async_safe? ( method )
27+ ASYNC_SAFE . fetch ( method , false )
28+ end
29+
1630 # Initialize writable stream functionality.
1731 # @parameter minimum_write_size [Integer] The minimum buffer size before flushing.
1832 def initialize ( minimum_write_size : MINIMUM_WRITE_SIZE , **, &block )
You can’t perform that action at this time.
0 commit comments