Skip to content

Commit 482621e

Browse files
authored
Merge pull request #1902 from OpenC3/kill_on_thread_crash
Kill Microservice on any Thread Crash
2 parents 2c13970 + 52ca5e9 commit 482621e

26 files changed

+479
-62
lines changed

openc3-cosmos-script-runner-api/Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ group :development, :test do
3333
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
3434
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
3535
gem 'rspec-rails', '~> 7.0'
36+
gem 'rspec'
3637
gem 'simplecov', '~> 0.20'
3738
gem 'simplecov-cobertura', '~> 2.1'
3839
end

openc3-cosmos-script-runner-api/spec/spec_helper.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
# the additional setup, and require it from the spec files that actually need
3535
# it.
3636

37+
require 'rspec'
38+
3739
# NOTE: You MUST require simplecov before anything else!
3840
if !ENV['OPENC3_NO_SIMPLECOV']
3941
require 'simplecov'

openc3/lib/openc3/microservices/cleanup_microservice.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,8 @@ def shutdown
100100
end
101101
end
102102

103-
OpenC3::CleanupMicroservice.run if __FILE__ == $0
103+
if __FILE__ == $0
104+
OpenC3::CleanupMicroservice.run
105+
ThreadManager.instance.shutdown
106+
ThreadManager.instance.join
107+
end

openc3/lib/openc3/microservices/decom_microservice.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ def start
4848
@logger.error "#{@microservice_name}: Limits Response thread died: #{e.formatted}"
4949
raise e
5050
end
51+
ThreadManager.instance.register(@thread, stop_object: self)
5152
end
5253

5354
def stop
@@ -251,4 +252,8 @@ def limits_change_callback(packet, item, old_limits_state, value, log_change)
251252
end
252253
end
253254

254-
OpenC3::DecomMicroservice.run if __FILE__ == $0
255+
if __FILE__ == $0
256+
OpenC3::DecomMicroservice.run
257+
ThreadManager.instance.shutdown
258+
ThreadManager.instance.join
259+
end

openc3/lib/openc3/microservices/interface_microservice.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def start
7070
@logger.error "#{@interface.name}: Command handler thread died: #{e.formatted}"
7171
raise e
7272
end
73+
ThreadManager.instance.register(@thread, stop_object: self)
7374
end
7475

7576
def stop()
@@ -346,6 +347,7 @@ def start
346347
@logger.error "#{@router.name}: Telemetry handler thread died: #{e.formatted}"
347348
raise e
348349
end
350+
ThreadManager.instance.register(@thread, stop_object: self)
349351
end
350352

351353
def stop
@@ -813,4 +815,8 @@ def graceful_kill
813815
end
814816
end
815817

816-
OpenC3::InterfaceMicroservice.run if __FILE__ == $0
818+
if __FILE__ == $0
819+
OpenC3::InterfaceMicroservice.run
820+
ThreadManager.instance.shutdown
821+
ThreadManager.instance.join
822+
end

openc3/lib/openc3/microservices/log_microservice.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,8 @@ def shutdown
142142
end
143143
end
144144

145-
OpenC3::LogMicroservice.run if __FILE__ == $0
145+
if __FILE__ == $0
146+
OpenC3::LogMicroservice.run
147+
ThreadManager.instance.shutdown
148+
ThreadManager.instance.join
149+
end

openc3/lib/openc3/microservices/microservice.rb

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
OpenC3.require_file 'openc3/utilities/secrets'
3131
OpenC3.require_file 'openc3/utilities/sleeper'
3232
OpenC3.require_file 'openc3/utilities/open_telemetry'
33+
OpenC3.require_file 'openc3/utilities/thread_manager'
3334
OpenC3.require_file 'openc3/models/microservice_model'
3435
OpenC3.require_file 'openc3/models/microservice_status_model'
3536
OpenC3.require_file 'tmpdir'
@@ -49,22 +50,27 @@ class Microservice
4950
def self.run(name = nil)
5051
name = ENV['OPENC3_MICROSERVICE_NAME'] unless name
5152
microservice = self.new(name)
52-
begin
53-
MicroserviceStatusModel.set(microservice.as_json(:allow_nan => true), scope: microservice.scope)
54-
microservice.state = 'RUNNING'
55-
microservice.run
56-
microservice.state = 'FINISHED'
57-
rescue Exception => e
58-
if SystemExit === e or SignalException === e
59-
microservice.state = 'KILLED'
60-
else
61-
microservice.error = e
62-
microservice.state = 'DIED_ERROR'
63-
Logger.fatal("Microservice #{name} dying from exception\n#{e.formatted}")
53+
thread = Thread.new do
54+
begin
55+
MicroserviceStatusModel.set(microservice.as_json(:allow_nan => true), scope: microservice.scope)
56+
microservice.state = 'RUNNING'
57+
microservice.run
58+
microservice.state = 'FINISHED'
59+
rescue Exception => e
60+
if SystemExit === e or SignalException === e
61+
microservice.state = 'KILLED'
62+
else
63+
microservice.error = e
64+
microservice.state = 'DIED_ERROR'
65+
Logger.fatal("Microservice #{name} dying from exception\n#{e.formatted}")
66+
end
67+
ensure
68+
MicroserviceStatusModel.set(microservice.as_json(:allow_nan => true), scope: microservice.scope)
6469
end
65-
ensure
66-
MicroserviceStatusModel.set(microservice.as_json(:allow_nan => true), scope: microservice.scope)
6770
end
71+
ThreadManager.instance.register(thread, shutdown_object: microservice)
72+
ThreadManager.instance.monitor
73+
ThreadManager.instance.shutdown
6874
end
6975

7076
def as_json(*a)
@@ -192,6 +198,7 @@ def initialize(name, is_plugin: false)
192198
@logger.error "#{@name} status thread died: #{e.formatted}"
193199
raise e
194200
end
201+
ThreadManager.instance.register(@microservice_status_thread)
195202
end
196203
end
197204

openc3/lib/openc3/microservices/multi_microservice.rb

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@
1818

1919
require 'openc3/microservices/microservice'
2020
require 'openc3/topics/topic'
21+
require 'openc3/utilities/thread_manager'
2122

2223
module OpenC3
2324
class MultiMicroservice < Microservice
2425
def run
2526
@threads = []
2627
ARGV.each do |microservice_name|
2728
microservice_model = MicroserviceModel.get_model(name: microservice_name, scope: @scope)
28-
@threads << Thread.new do
29+
thread = Thread.new do
2930
cmd_line = microservice_model.cmd.join(' ')
3031
split_cmd_line = cmd_line.split(' ')
3132
filename = nil
@@ -42,21 +43,15 @@ def run
4243
klass = filename.filename_to_class_name.to_class
4344
klass.run(microservice_model.name)
4445
end
46+
ThreadManager.instance.register(thread)
4547
end
46-
@threads.each do |thread|
47-
thread.join
48-
end
49-
end
50-
51-
def shutdown
52-
super()
53-
if @threads
54-
@threads.each do |thread|
55-
thread.join
56-
end
57-
end
48+
ThreadManager.instance.monitor
49+
ThreadManager.instance.shutdown
5850
end
5951
end
6052
end
61-
62-
OpenC3::MultiMicroservice.run if __FILE__ == $0
53+
if __FILE__ == $0
54+
OpenC3::MultiMicroservice.run
55+
ThreadManager.instance.shutdown
56+
ThreadManager.instance.join
57+
end

openc3/lib/openc3/microservices/periodic_microservice.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,8 @@ def shutdown
8787
end
8888
end
8989

90-
OpenC3::PeriodicMicroservice.run if __FILE__ == $0
90+
if __FILE__ == $0
91+
OpenC3::PeriodicMicroservice.run
92+
ThreadManager.instance.shutdown
93+
ThreadManager.instance.join
94+
end

openc3/lib/openc3/microservices/reducer_microservice.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -633,4 +633,8 @@ def extract_entry_samples(packet)
633633
end
634634
end
635635

636-
OpenC3::ReducerMicroservice.run if __FILE__ == $0
636+
if __FILE__ == $0
637+
OpenC3::ReducerMicroservice.run
638+
ThreadManager.instance.shutdown
639+
ThreadManager.instance.join
640+
end

0 commit comments

Comments
 (0)