async-container 0.18.3 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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