@@ -110,6 +110,17 @@ module Daemon
110
110
class Init
111
111
112
112
def initialize
113
+
114
+ @safe_exit = SafeExit . new do
115
+ # Register exit routine
116
+ # This will only be called once
117
+ Util . log "Daemon Booter is now exiting."
118
+ Util . log "Cleaning up any running processes..."
119
+ cleanup_any_running_processes
120
+ Util . log "Daemon Booter - Over and Out."
121
+ Util . close_log
122
+ end
123
+
113
124
# This is where the Daemon begins and ends.
114
125
@scsynth_booter = nil
115
126
@tau_booter = nil
@@ -120,13 +131,7 @@ def initialize
120
131
Util . open_log
121
132
Util . log "Welcome to the Daemon Booter"
122
133
123
- at_exit do
124
- cleanup_any_running_processes
125
- Util . log "Daemon Booter - Over and Out."
126
- Util . close_log
127
- end
128
-
129
- ports = PortDiscovery . new . ports
134
+ ports = PortDiscovery . new ( @safe_exit ) . ports
130
135
131
136
Util . log "Selected ports: "
132
137
Util . log ports . inspect
@@ -135,7 +140,7 @@ def initialize
135
140
136
141
# Let the calling process (likely the GUI) know which port to
137
142
# listen to and communicate on with the Ruby spider server via
138
- # STDOUT:
143
+ # STDOUT.
139
144
puts "#{ ports [ "daemon-keep-alive" ] } #{ ports [ "gui-listen-to-server" ] } #{ ports [ "gui-send-to-server" ] } #{ ports [ "scsynth" ] } #{ ports [ "osc-cues" ] } "
140
145
STDOUT . flush
141
146
@@ -158,7 +163,7 @@ def initialize
158
163
159
164
@spider_booter . wait if @spider_booter
160
165
Util . log "Spider Server process has completed"
161
- end
166
+ end
162
167
163
168
164
169
# This is the Zombie Kill Switch
@@ -190,7 +195,7 @@ def spawn_zombie_kill_switch(port_num, &blk)
190
195
191
196
unless IO . select ( [ keep_alive_server ] , nil , nil , connect_timeout )
192
197
Util . log "Error. Unable to connect to GUI process on TCP port #{ port_num } "
193
- exit
198
+ @safe_exit . exit
194
199
end
195
200
196
201
client = keep_alive_server . accept
@@ -202,18 +207,19 @@ def spawn_zombie_kill_switch(port_num, &blk)
202
207
# For debug:
203
208
# Util.log "RCV #{received_data}"
204
209
end
205
-
210
+ rescue Errno ::ECONNRESET
211
+ Util . log "GUI forcibly closed the connection."
206
212
rescue StandardError => e
207
213
Util . log "Oh no, something went wrong reading keep alive messages from the GUI"
208
214
Util . log "Error Class: #{ e . class } "
209
215
Util . log "Error Message: #{ e . message } "
210
216
Util . log "Error Backtrace: #{ e . backtrace . inspect } "
211
217
end
212
218
213
- Util . log "Lost connection to server... shutting down..."
219
+ Util . log "Shutting down..."
214
220
client . close if client
215
221
keep_alive_server . close if keep_alive_server
216
- exit
222
+ @safe_exit . exit
217
223
end
218
224
end
219
225
@@ -322,6 +328,50 @@ def self.os
322
328
end
323
329
end
324
330
331
+ class SafeExit
332
+
333
+ def initialize ( &cleanup_procedure )
334
+
335
+ @exit_mut = Mutex . new
336
+ @exit_cleanup_mut = Mutex . new
337
+ @exit_in_progress = false
338
+ @exit_cleanup_completed = false
339
+ @cleanup_procedure = cleanup_procedure
340
+
341
+ at_exit do
342
+ @exit_mut . synchronize do
343
+ @exit_in_progress = true
344
+ idempotent_exit_cleanup
345
+ end
346
+ end
347
+ end
348
+
349
+ def exit
350
+ Thread . current . kill if @exit_in_progress
351
+
352
+ @exit_mut . synchronize do
353
+ if @exit_in_progress
354
+ Thread . current . kill
355
+ else
356
+ @exit_in_progress = true
357
+ idempotent_exit_cleanup
358
+ Kernel . exit
359
+ end
360
+ end
361
+ end
362
+
363
+ private
364
+
365
+ def idempotent_exit_cleanup
366
+ @exit_cleanup_mut . synchronize do
367
+ unless @exit_cleanup_completed
368
+ @cleanup_procedure . call
369
+ @exit_cleanup_completed = true
370
+ end
371
+ end
372
+ end
373
+ end
374
+
325
375
326
376
327
377
class ProcessBooter
@@ -694,7 +744,8 @@ class PortDiscovery
694
744
"websocket" => :dynamic
695
745
} . freeze
696
746
697
- def initialize
747
+ def initialize ( safe_exit )
748
+ @safe_exit = safe_exit
698
749
# choose random port to try first
699
750
@last_free_port = 49152 + rand ( 2000 )
700
751
@@ -735,7 +786,7 @@ def initialize
735
786
port = find_free_port
736
787
elsif default == :paired
737
788
raise "Invalid port default for port: #{ port_name } . This port can not be paired."
738
- exit
789
+ @safe_exit . exit
739
790
else
740
791
port = default
741
792
if ( !check_port ( port ) )
@@ -765,7 +816,7 @@ def check_port(port)
765
816
def find_free_port
766
817
while !check_port ( @last_free_port += 1 )
767
818
if @last_free_port > 65535
768
- exit
819
+ @safe_exit . exit
769
820
end
770
821
end
771
822
@last_free_port
0 commit comments