async-container 0.18.2 → 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,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