Skip to content

Commit e78081b

Browse files
committed
fix handling single instance
was broken because of the AppImage wrapper. Also, instead of relying on a hacky `pgrep` output, this now works with a lockfile under /tmp. It would be even better to use `fcntl` but it's an annoying api
1 parent 94f0552 commit e78081b

File tree

1 file changed

+34
-22
lines changed

1 file changed

+34
-22
lines changed

src/run/runner.cr

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -268,30 +268,42 @@ module Run
268268

269269
def handle_single_instance
270270
return if @settings.single_instance == SingleInstance::Off
271-
search_for = (PROGRAM_NAME + " " + ARGV.join(" ")).strip
272-
result = IO::Memory.new
271+
already_running_pid = -1
272+
# We don't check instance duplication when binary or path changes
273+
script_identifier = (((ENV["ARGV0"]? || PROGRAM_NAME) + " " + ARGV.join(" ")).strip).gsub('/',"\\")
274+
lock_path = "/tmp/ahk_x11 #{script_identifier}.lock"
275+
lock = File.open(lock_path, "a+")
273276
begin
274-
Process.run("pgrep", ["--full", "--exact", search_for], output: result)
275-
rescue e : IO::Error
276-
STDERR.puts "Warning: Could not determine previously running instance because pgrep is not installed. Using SingleInstance OFF."
277-
return
278-
end
279-
already_running = result.to_s.chomp
280-
.split('\n')
281-
.select{|p| p != Process.pid.to_s }
282-
.first?.try &.to_i?
283-
return if ! already_running
284-
case @settings.single_instance
285-
when SingleInstance::Force
286-
Process.signal(Signal::HUP, already_running)
287-
when SingleInstance::Ignore
288-
STDERR.puts "Instance already running and #SingleInstance Ignore passed. Exiting."
289-
::exit
290-
when SingleInstance::Prompt
291-
response = display.gtk.msgbox "An older instance of this script is already running. Replace it with this instance?\nNote: To avoid this message, see #SingleInstance in the help file.", options: Gtk::MsgBoxOptions::Yes_No.value
292-
::exit if response != Gtk::MsgBoxButton::Yes
293-
Process.signal(Signal::HUP, already_running)
277+
lock.flock_exclusive(blocking: false)
278+
rescue e
279+
already_running_pid = lock.gets_to_end.to_i
280+
end
281+
if already_running_pid > -1
282+
case @settings.single_instance
283+
when SingleInstance::Force
284+
Process.signal(Signal::HUP, already_running_pid)
285+
when SingleInstance::Ignore
286+
STDERR.puts "Instance already running and #SingleInstance Ignore passed. Exiting."
287+
::exit
288+
when SingleInstance::Prompt
289+
response = display.gtk.msgbox "An older instance of this script is already running. Replace it with this instance?\nNote: To avoid this message, see #SingleInstance in the help file.", options: Gtk::MsgBoxOptions::Yes_No.value
290+
::exit if response != Gtk::MsgBoxButton::Yes
291+
Process.signal(Signal::HUP, already_running_pid)
292+
end
293+
start = Time.monotonic
294+
while Process.exists?(already_running_pid)
295+
if Time.monotonic - start > 1.second
296+
raise "Failed to kill previous instance process with PID #{already_running_pid}"
297+
end
298+
sleep 10.milliseconds
299+
end
300+
# TODO: exceptions here aren't shown as popup?
301+
lock.flock_exclusive(blocking: false)
294302
end
303+
# TODO: clean up on exit
304+
lock.truncate
305+
lock << Process.pid
306+
lock.fsync
295307
end
296308

297309
@suspension = false

0 commit comments

Comments
 (0)