async-container 0.18.3 → 0.19.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.
@@ -1,199 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2020-2024, by Samuel Williams.
5
- # Copyright, 2020, by Olle Jonsson.
6
-
7
- require_relative 'channel'
8
- require_relative 'error'
9
- require_relative 'notify/pipe'
10
-
11
- module Async
12
- module Container
13
- # Represents a running child thread from the point of view of the parent container.
14
- class Thread < Channel
15
- # Used to propagate the exit status of a child process invoked by {Instance#exec}.
16
- class Exit < Exception
17
- # Initialize the exit status.
18
- # @parameter status [::Process::Status] The process exit status.
19
- def initialize(status)
20
- @status = status
21
- end
22
-
23
- # The process exit status.
24
- # @attribute [::Process::Status]
25
- attr :status
26
-
27
- # The process exit status if it was an error.
28
- # @returns [::Process::Status | Nil]
29
- def error
30
- unless status.success?
31
- status
32
- end
33
- end
34
- end
35
-
36
- # Represents a running child thread from the point of view of the child thread.
37
- class Instance < Notify::Pipe
38
- # Wrap an instance around the {Thread} instance from within the threaded child.
39
- # @parameter thread [Thread] The thread intance to wrap.
40
- def self.for(thread)
41
- instance = self.new(thread.out)
42
-
43
- return instance
44
- end
45
-
46
- def initialize(io)
47
- @name = nil
48
- @thread = ::Thread.current
49
-
50
- super
51
- end
52
-
53
- # Set the name of the thread.
54
- # @parameter value [String] The name to set.
55
- def name= value
56
- @thread.name = value
57
- end
58
-
59
- # Get the name of the thread.
60
- # @returns [String]
61
- def name
62
- @thread.name
63
- end
64
-
65
- # Execute a child process using {::Process.spawn}. In order to simulate {::Process.exec}, an {Exit} instance is raised to propagage exit status.
66
- # This creates the illusion that this method does not return (normally).
67
- def exec(*arguments, ready: true, **options)
68
- if ready
69
- self.ready!(status: "(spawn)")
70
- else
71
- self.before_spawn(arguments, options)
72
- end
73
-
74
- begin
75
- pid = ::Process.spawn(*arguments, **options)
76
- ensure
77
- _, status = ::Process.wait2(pid)
78
-
79
- raise Exit, status
80
- end
81
- end
82
- end
83
-
84
- def self.fork(**options)
85
- self.new(**options) do |thread|
86
- ::Thread.new do
87
- yield Instance.for(thread)
88
- end
89
- end
90
- end
91
-
92
- # Initialize the thread.
93
- # @parameter name [String] The name to use for the child thread.
94
- def initialize(name: nil)
95
- super()
96
-
97
- @status = nil
98
-
99
- @thread = yield(self)
100
- @thread.report_on_exception = false
101
- @thread.name = name
102
-
103
- @waiter = ::Thread.new do
104
- begin
105
- @thread.join
106
- rescue Exit => exit
107
- finished(exit.error)
108
- rescue Interrupt
109
- # Graceful shutdown.
110
- finished
111
- rescue Exception => error
112
- finished(error)
113
- else
114
- finished
115
- end
116
- end
117
- end
118
-
119
- # Set the name of the thread.
120
- # @parameter value [String] The name to set.
121
- def name= value
122
- @thread.name = value
123
- end
124
-
125
- # Get the name of the thread.
126
- # @returns [String]
127
- def name
128
- @thread.name
129
- end
130
-
131
- # A human readable representation of the thread.
132
- # @returns [String]
133
- def to_s
134
- "\#<#{self.class} #{@thread.name}>"
135
- end
136
-
137
- # Invoke {#terminate!} and then {#wait} for the child thread to exit.
138
- def close
139
- self.terminate!
140
- self.wait
141
- ensure
142
- super
143
- end
144
-
145
- # Raise {Interrupt} in the child thread.
146
- def interrupt!
147
- @thread.raise(Interrupt)
148
- end
149
-
150
- # Raise {Terminate} in the child thread.
151
- def terminate!
152
- @thread.raise(Terminate)
153
- end
154
-
155
- # Wait for the thread to exit and return he exit status.
156
- # @returns [Status]
157
- def wait
158
- if @waiter
159
- @waiter.join
160
- @waiter = nil
161
- end
162
-
163
- return @status
164
- end
165
-
166
- # A pseudo exit-status wrapper.
167
- class Status
168
- # Initialise the status.
169
- # @parameter error [::Process::Status] The exit status of the child thread.
170
- def initialize(error = nil)
171
- @error = error
172
- end
173
-
174
- # Whether the status represents a successful outcome.
175
- # @returns [Boolean]
176
- def success?
177
- @error.nil?
178
- end
179
-
180
- # A human readable representation of the status.
181
- def to_s
182
- "\#<#{self.class} #{success? ? "success" : "failure"}>"
183
- end
184
- end
185
-
186
- protected
187
-
188
- # Invoked by the @waiter thread to indicate the outcome of the child thread.
189
- def finished(error = nil)
190
- if error
191
- Console.logger.error(self) {error}
192
- end
193
-
194
- @status = Status.new(error)
195
- self.close_write
196
- end
197
- end
198
- end
199
- end