async-container 0.18.2 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,200 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2020-2022, 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)") if ready
70
- else
71
- self.before_spawn(arguments, options)
72
- end
73
-
74
- begin
75
- # TODO prefer **options... but it doesn't support redirections on < 2.7
76
- pid = ::Process.spawn(*arguments, options)
77
- ensure
78
- _, status = ::Process.wait2(pid)
79
-
80
- raise Exit, status
81
- end
82
- end
83
- end
84
-
85
- def self.fork(**options)
86
- self.new(**options) do |thread|
87
- ::Thread.new do
88
- yield Instance.for(thread)
89
- end
90
- end
91
- end
92
-
93
- # Initialize the thread.
94
- # @parameter name [String] The name to use for the child thread.
95
- def initialize(name: nil)
96
- super()
97
-
98
- @status = nil
99
-
100
- @thread = yield(self)
101
- @thread.report_on_exception = false
102
- @thread.name = name
103
-
104
- @waiter = ::Thread.new do
105
- begin
106
- @thread.join
107
- rescue Exit => exit
108
- finished(exit.error)
109
- rescue Interrupt
110
- # Graceful shutdown.
111
- finished
112
- rescue Exception => error
113
- finished(error)
114
- else
115
- finished
116
- end
117
- end
118
- end
119
-
120
- # Set the name of the thread.
121
- # @parameter value [String] The name to set.
122
- def name= value
123
- @thread.name = value
124
- end
125
-
126
- # Get the name of the thread.
127
- # @returns [String]
128
- def name
129
- @thread.name
130
- end
131
-
132
- # A human readable representation of the thread.
133
- # @returns [String]
134
- def to_s
135
- "\#<#{self.class} #{@thread.name}>"
136
- end
137
-
138
- # Invoke {#terminate!} and then {#wait} for the child thread to exit.
139
- def close
140
- self.terminate!
141
- self.wait
142
- ensure
143
- super
144
- end
145
-
146
- # Raise {Interrupt} in the child thread.
147
- def interrupt!
148
- @thread.raise(Interrupt)
149
- end
150
-
151
- # Raise {Terminate} in the child thread.
152
- def terminate!
153
- @thread.raise(Terminate)
154
- end
155
-
156
- # Wait for the thread to exit and return he exit status.
157
- # @returns [Status]
158
- def wait
159
- if @waiter
160
- @waiter.join
161
- @waiter = nil
162
- end
163
-
164
- return @status
165
- end
166
-
167
- # A pseudo exit-status wrapper.
168
- class Status
169
- # Initialise the status.
170
- # @parameter error [::Process::Status] The exit status of the child thread.
171
- def initialize(error = nil)
172
- @error = error
173
- end
174
-
175
- # Whether the status represents a successful outcome.
176
- # @returns [Boolean]
177
- def success?
178
- @error.nil?
179
- end
180
-
181
- # A human readable representation of the status.
182
- def to_s
183
- "\#<#{self.class} #{success? ? "success" : "failure"}>"
184
- end
185
- end
186
-
187
- protected
188
-
189
- # Invoked by the @waiter thread to indicate the outcome of the child thread.
190
- def finished(error = nil)
191
- if error
192
- Console.logger.error(self) {error}
193
- end
194
-
195
- @status = Status.new(error)
196
- self.close_write
197
- end
198
- end
199
- end
200
- end