From b4879aa498b8e48fc25bda5b325276ecbe7f660e Mon Sep 17 00:00:00 2001 From: Bruno Sutic Date: Mon, 29 Apr 2024 23:59:22 +0200 Subject: [PATCH] Ensure ServerProcess is started only once --- lib/tailwindcss/server_process.rb | 74 ++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 12 deletions(-) diff --git a/lib/tailwindcss/server_process.rb b/lib/tailwindcss/server_process.rb index adbb250..fbca382 100644 --- a/lib/tailwindcss/server_process.rb +++ b/lib/tailwindcss/server_process.rb @@ -6,20 +6,12 @@ def self.start new.start end - def start + def initialize @server = Server.new(self) - @pid = fork do - monitor_server - exit_hook - # Using IO.popen(command, 'r+') will avoid watch_command read from $stdin. - # If we use system(*command) instead, IRB and Debug can't read from $stdin - # correctly bacause some keystrokes will be taken by watch_command. - IO.popen(Commands.watch_command, 'r+') do |io| - IO.copy_stream(io, $stdout) - end - end - Process.detach pid + end + def start + @pid = existing_process || start_process server.monitor_process server.exit_hook end @@ -41,6 +33,37 @@ def dead? private + def existing_process + if (pid = Pidfile.pid) + begin + Process.kill 0, pid + pid + rescue Errno::ESRCH + # Process does not exist + rescue Errno::EPERM + # Ignore process owned by another user + end + end + end + + def start_process + pid = fork do + Pidfile.write + monitor_server + exit_hook + # Using IO.popen(command, 'r+') will avoid watch_command read from $stdin. + # If we use system(*command) instead, IRB and Debug can't read from $stdin + # correctly bacause some keystrokes will be taken by watch_command. + IO.popen(Commands.watch_command, 'r+') do |io| + IO.copy_stream(io, $stdout) + end + ensure + Pidfile.delete + end + Process.detach pid + pid + end + def monitor_server Thread.new do loop do @@ -60,6 +83,33 @@ def exit_hook end end + module Pidfile + def self.path + Rails.root.join("tmp", "pids", "tailwindcss.txt") + end + + def self.read + File.read(path, mode: "rb:UTF-8") + rescue Errno::ENOENT + # File does not exist + end + + def self.write + File.write(path, Process.pid, mode: "wb:UTF-8") + end + + def self.delete + File.exist?(path) && File.delete(path) + end + + def self.pid + Integer(read) + rescue ArgumentError, TypeError + # Invalid content + delete + end + end + class Server attr_reader :process, :pid