Skip to content

Commit

Permalink
[tools] Detect multiple instances running for the same app to only ke…
Browse files Browse the repository at this point in the history
…ep the most recent one
  • Loading branch information
jeremyfa committed Dec 28, 2024
1 parent 4fa75bd commit 4ac6792
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 47 deletions.
5 changes: 4 additions & 1 deletion plugins/android/tools/src/tools/AndroidUtils.hx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ class AndroidUtils {

var status = 0;

var cwd = options.cwd;
var logCwd = options.logCwd;

final proc = new Process(name, args, options.cwd);
Expand Down Expand Up @@ -105,6 +104,10 @@ class AndroidUtils {
final status = proc.tick_until_exit_status(() -> {
Runner.tick();
timer.update();
if (context.shouldExit) {
proc.kill(false);
Sys.exit(0);
}
});

return status;
Expand Down
4 changes: 4 additions & 0 deletions plugins/web/tools/src/tools/tasks/web/Web.hx
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,10 @@ class Web extends tools.Task {
final status = proc.tick_until_exit_status(() -> {
Runner.tick();
timer.update();
if (context.shouldExit) {
proc.kill(false);
Sys.exit(0);
}
});

if (status != 0 && (Timestamp.now() - time) < 5.0) {
Expand Down
3 changes: 3 additions & 0 deletions tools/src/tools/Context.hx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ class Context {
/** A flag to tell whether one icon or more have changed since last icon pass */
public var iconsChanged:Bool;

/** Set to `true` if the cli tools should exit */
public var shouldExit:Bool;

/** If `true`, data will be printed line by line instead of as a single chunk of data.
This is sometimes needed to prevent some truncated bug (seen in vscode + spawn) */
public var printSplitLines:Bool;
Expand Down
8 changes: 8 additions & 0 deletions tools/src/tools/Helpers.hx
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,10 @@ class Helpers {
if (tick != null) {
tick();
}
if (context.shouldExit) {
proc.kill(false);
Sys.exit(0);
}
});

return status;
Expand Down Expand Up @@ -829,6 +833,10 @@ class Helpers {
if (tick != null) {
tick();
}
if (context.shouldExit) {
proc.kill(false);
Sys.exit(0);
}
});

if (stdout != null) {
Expand Down
80 changes: 37 additions & 43 deletions tools/src/tools/InstanceManager.hx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import haxe.crypto.Md5;
import haxe.io.Path;
import sys.FileSystem;
import sys.io.File;
import sys.thread.Mutex;
import sys.thread.Thread;
import tools.Helpers.*;

Expand All @@ -12,42 +13,41 @@ import tools.Helpers.*;
*/
class InstanceManager {

private static var lockFile:String = null;
private static var onQuitCallback:()->Void;
private static var checkInterval:Float;

public static function makeUnique(identifier:String):Void {

final instanceManager = new InstanceManager(
if (InstanceManager.lockFile != null) return;

InstanceManager.init(
identifier,
() -> {
Sys.exit(0);
context.shouldExit = true;
}
);

}

private var lockFile:String;
private var watchThread:Thread;
private var shouldRun:Bool;
private var onQuitCallback:()->Void;
private var checkInterval:Float;

/**
* Creates a new instance manager
* @param identifier Any string that uniquely identifies what you want single-instanced
* @param onQuit Callback that will be called when this instance should quit
* @param checkIntervalMs How often to check for changes (in milliseconds)
*/
public function new(identifier:String, onQuit:()->Void, checkIntervalMs:Float = 1000) {
static function init(identifier:String, onQuit:()->Void, checkInterval:Float = 0.01) {
quitExisting(identifier);

this.onQuitCallback = onQuit;
this.checkInterval = checkIntervalMs / 1000.0; // Convert to seconds
this.shouldRun = true;
InstanceManager.onQuitCallback = onQuit;
InstanceManager.checkInterval = checkInterval;

// Create the lock file path
var hash = Md5.encode(identifier);
var homeDir = homedir();

var ceramicDir = Path.join([homeDir, ".ceramic"]);
this.lockFile = Path.join([ceramicDir, 'instance-${hash}.lock']);
InstanceManager.lockFile = Path.join([ceramicDir, 'instance-${hash}.lock']);

// Ensure .ceramic directory exists
if (!FileSystem.exists(ceramicDir)) {
Expand All @@ -61,48 +61,43 @@ class InstanceManager {
/**
* Initializes the file watching thread and writes initial lock file
*/
private function initializeWatcher():Void {
private static function initializeWatcher():Void {
// Write initial lock file with current timestamp
updateLockFile();

// Start watcher thread
watchThread = Thread.create(() -> {
var lastMod = FileSystem.stat(lockFile).mtime;

while (shouldRun) {
try {
Sys.sleep(checkInterval);

if (!FileSystem.exists(lockFile)) {
// Lock file was deleted, recreate it
updateLockFile();
lastMod = FileSystem.stat(lockFile).mtime;
continue;
}
// Start watcher
var lastMod = FileSystem.stat(lockFile).mtime;
var clearInterval = null;
clearInterval = timer.interval(InstanceManager.checkInterval, () -> {
try {
if (!FileSystem.exists(lockFile)) {
// Lock file was deleted, recreate it
updateLockFile();
lastMod = FileSystem.stat(lockFile).mtime;
return;
}

var currentMod = FileSystem.stat(lockFile).mtime;
if (currentMod.getTime() > lastMod.getTime()) {
// File was modified by another instance
shouldRun = false;
if (onQuitCallback != null) {
onQuitCallback();
}
break;
var currentMod = FileSystem.stat(lockFile).mtime;
if (currentMod.getTime() > lastMod.getTime()) {
// File was modified by another instance
if (onQuitCallback != null) {
onQuitCallback();
}

lastMod = currentMod;
}
catch (e:Dynamic) {
print('Error in watcher thread: $e');
clearInterval();
}

lastMod = currentMod;
}
catch (e:Dynamic) {
print('Error in watcher: $e');
}
});
}

/**
* Updates the lock file to signal presence to other instances
*/
private function updateLockFile():Void {
private static function updateLockFile():Void {
try {
File.saveContent(lockFile, Std.string(Sys.time()));
}
Expand All @@ -115,7 +110,6 @@ class InstanceManager {
* Call this when your application is shutting down
*/
public function dispose():Void {
shouldRun = false;
try {
if (FileSystem.exists(lockFile)) {
FileSystem.deleteFile(lockFile);
Expand Down
3 changes: 2 additions & 1 deletion tools/src/tools/Tools.hx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ class Tools {
iconsChanged: false,
printSplitLines: (args.indexOf('--print-split-lines') != -1),
haxePaths: [],
haxeLibraries: []
haxeLibraries: [],
shouldExit: false
};

// Check if we are embedded in electron
Expand Down
4 changes: 2 additions & 2 deletions tools/src/tools/tasks/Build.hx
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ class Build extends tools.Task {
break;
}
}
if (isRun) {
/*if (isRun) {
// Keep a file updated in home directory to let other ceramic scripts detect
// that a haxe server is running
var homedir:String = homedir();
Expand All @@ -161,7 +161,7 @@ class Build extends tools.Task {
Sys.exit(0);
}
});
}
}*/

// Get and run backend's build task
context.backend.runBuild(cwd, args, target, context.variant, configIndex);
Expand Down

0 comments on commit 4ac6792

Please sign in to comment.