Skip to content

Commit 126d7f2

Browse files
committed
Merge branch 'main' of github.com:Quantus-Network/quantus-apps
2 parents d966512 + db5cb64 commit 126d7f2

2 files changed

Lines changed: 163 additions & 74 deletions

File tree

miner-app/lib/src/services/binary_manager.dart

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,11 @@ class BinaryManager {
221221
throw Exception('Unsupported platform: ${Platform.operatingSystem}');
222222
}
223223

224-
if (Platform.version.contains('arm64') || Platform.version.contains('aarch64')) {
224+
if (Platform.isWindows) {
225+
// Force x86_64 on Windows to support x64 emulation on ARM devices
226+
// unless we specifically start releasing native ARM64 Windows binaries
227+
arch = 'x86_64';
228+
} else if (Platform.version.contains('arm64') || Platform.version.contains('aarch64')) {
225229
arch = 'aarch64';
226230
} else {
227231
arch = 'x86_64';
@@ -487,7 +491,11 @@ class BinaryManager {
487491
throw Exception('Unsupported platform: ${Platform.operatingSystem}');
488492
}
489493

490-
if (Platform.version.contains('arm64') || Platform.version.contains('aarch64')) {
494+
if (Platform.isWindows) {
495+
// Force x86_64 on Windows to support x64 emulation on ARM devices
496+
// unless we specifically start releasing native ARM64 Windows binaries
497+
arch = 'x86_64';
498+
} else if (Platform.version.contains('arm64') || Platform.version.contains('aarch64')) {
491499
arch = 'aarch64';
492500
} else {
493501
arch = 'x86_64';
@@ -659,6 +667,13 @@ class BinaryManager {
659667

660668
static String _targetTriple() {
661669
final os = Platform.isMacOS ? 'apple-darwin' : (Platform.isWindows ? 'pc-windows-msvc' : 'unknown-linux-gnu');
670+
671+
// Force x86_64 on Windows to ensure we download the x64 binary even on ARM devices
672+
// (since they can emulate x64, and we don't likely have a native ARM build for Windows yet)
673+
if (Platform.isWindows) {
674+
return 'x86_64-$os';
675+
}
676+
662677
final arch = Platform.version.contains('arm64') || Platform.version.contains('aarch64') ? 'aarch64' : 'x86_64';
663678
return '$arch-$os';
664679
}

miner-app/lib/src/services/miner_process.dart

Lines changed: 146 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,7 @@ class MinerProcess {
180180
try {
181181
// Check if the process has exited by looking at its PID
182182
final pid = _externalMinerProcess!.pid;
183-
final result = await Process.run('kill', ['-0', pid.toString()]);
184-
if (result.exitCode != 0) {
185-
minerStillRunning = false;
186-
}
183+
minerStillRunning = await _isProcessRunning(pid);
187184
} catch (e) {
188185
minerStillRunning = false;
189186
}
@@ -346,8 +343,7 @@ class MinerProcess {
346343
Future.delayed(const Duration(seconds: 2)).then((_) async {
347344
// Check if process is still running and force kill if necessary
348345
try {
349-
final result = await Process.run('kill', ['-0', _externalMinerProcess!.pid.toString()]);
350-
if (result.exitCode == 0) {
346+
if (await _isProcessRunning(_externalMinerProcess!.pid)) {
351347
print('MinerProcess: External miner still running, force killing...');
352348
_externalMinerProcess!.kill(ProcessSignal.sigkill);
353349
}
@@ -378,8 +374,7 @@ class MinerProcess {
378374
Future.delayed(const Duration(seconds: 2)).then((_) async {
379375
// Check if process is still running and force kill if necessary
380376
try {
381-
final result = await Process.run('kill', ['-0', _nodeProcess.pid.toString()]);
382-
if (result.exitCode == 0) {
377+
if (await _isProcessRunning(_nodeProcess.pid)) {
383378
print('MinerProcess: Node process still running, force killing...');
384379
_nodeProcess.kill(ProcessSignal.sigkill);
385380
}
@@ -447,30 +442,67 @@ class MinerProcess {
447442
}
448443
}
449444

445+
/// Check if a process with the given PID is running
446+
Future<bool> _isProcessRunning(int pid) async {
447+
try {
448+
if (Platform.isWindows) {
449+
final result = await Process.run('tasklist', ['/FI', 'PID eq $pid']);
450+
return result.stdout.toString().contains(' $pid ');
451+
} else {
452+
final result = await Process.run('kill', ['-0', pid.toString()]);
453+
return result.exitCode == 0;
454+
}
455+
} catch (e) {
456+
return false;
457+
}
458+
}
459+
450460
/// Helper method to force kill a process by PID with verification
451461
Future<void> _forceKillProcess(int pid, String processName) async {
452462
try {
453463
print('MinerProcess: Force killing $processName process (PID: $pid)');
454464

455-
// First try SIGKILL via kill command for better reliability
456-
final killResult = await Process.run('kill', ['-9', pid.toString()]);
465+
if (Platform.isWindows) {
466+
final killResult = await Process.run('taskkill', ['/F', '/PID', pid.toString()]);
467+
if (killResult.exitCode == 0) {
468+
print('MinerProcess: Successfully force killed $processName (PID: $pid)');
469+
} else {
470+
print('MinerProcess: taskkill failed for $processName (PID: $pid), exit code: ${killResult.exitCode}');
471+
}
472+
473+
await Future.delayed(const Duration(milliseconds: 500));
457474

458-
if (killResult.exitCode == 0) {
459-
print('MinerProcess: Successfully force killed $processName (PID: $pid)');
475+
// Verify
476+
final checkResult = await Process.run('tasklist', ['/FI', 'PID eq $pid']);
477+
if (checkResult.stdout.toString().contains(' $pid ')) {
478+
print('MinerProcess: WARNING - $processName (PID: $pid) may still be running');
479+
// Try by name as last resort
480+
final binaryName = processName.contains('miner') ? 'quantus-miner.exe' : 'quantus-node.exe';
481+
await Process.run('taskkill', ['/F', '/IM', binaryName]);
482+
} else {
483+
print('MinerProcess: Verified $processName (PID: $pid) is terminated');
484+
}
460485
} else {
461-
print('MinerProcess: kill command failed for $processName (PID: $pid), exit code: ${killResult.exitCode}');
462-
}
486+
// First try SIGKILL via kill command for better reliability
487+
final killResult = await Process.run('kill', ['-9', pid.toString()]);
463488

464-
// Wait a moment then verify the process is dead
465-
await Future.delayed(const Duration(milliseconds: 500));
489+
if (killResult.exitCode == 0) {
490+
print('MinerProcess: Successfully force killed $processName (PID: $pid)');
491+
} else {
492+
print('MinerProcess: kill command failed for $processName (PID: $pid), exit code: ${killResult.exitCode}');
493+
}
466494

467-
final checkResult = await Process.run('kill', ['-0', pid.toString()]);
468-
if (checkResult.exitCode != 0) {
469-
print('MinerProcess: Verified $processName (PID: $pid) is terminated');
470-
} else {
471-
print('MinerProcess: WARNING - $processName (PID: $pid) may still be running');
472-
// Try pkill as last resort
473-
await Process.run('pkill', ['-9', '-f', processName.contains('miner') ? 'quantus-miner' : 'quantus-node']);
495+
// Wait a moment then verify the process is dead
496+
await Future.delayed(const Duration(milliseconds: 500));
497+
498+
final checkResult = await Process.run('kill', ['-0', pid.toString()]);
499+
if (checkResult.exitCode != 0) {
500+
print('MinerProcess: Verified $processName (PID: $pid) is terminated');
501+
} else {
502+
print('MinerProcess: WARNING - $processName (PID: $pid) may still be running');
503+
// Try pkill as last resort
504+
await Process.run('pkill', ['-9', '-f', processName.contains('miner') ? 'quantus-miner' : 'quantus-node']);
505+
}
474506
}
475507
} catch (e) {
476508
print('MinerProcess: Error in _forceKillProcess for $processName: $e');
@@ -575,8 +607,13 @@ class MinerProcess {
575607
/// Check if a port is currently in use
576608
Future<bool> _isPortInUse(int port) async {
577609
try {
578-
final result = await Process.run('lsof', ['-i', ':$port']);
579-
return result.exitCode == 0 && result.stdout.toString().isNotEmpty;
610+
if (Platform.isWindows) {
611+
final result = await Process.run('netstat', ['-ano']);
612+
return result.exitCode == 0 && result.stdout.toString().contains(':$port');
613+
} else {
614+
final result = await Process.run('lsof', ['-i', ':$port']);
615+
return result.exitCode == 0 && result.stdout.toString().isNotEmpty;
616+
}
580617
} catch (e) {
581618
// lsof might not be available, try netstat as fallback
582619
try {
@@ -592,13 +629,32 @@ class MinerProcess {
592629
/// Kill process using a specific port
593630
Future<void> _killProcessOnPort(int port) async {
594631
try {
595-
// Find process using the port
596-
final result = await Process.run('lsof', ['-ti', ':$port']);
597-
if (result.exitCode == 0) {
598-
final pids = result.stdout.toString().trim().split('\n');
599-
for (final pid in pids) {
600-
if (pid.isNotEmpty) {
601-
await Process.run('kill', ['-9', pid.trim()]);
632+
if (Platform.isWindows) {
633+
final result = await Process.run('netstat', ['-ano']);
634+
if (result.exitCode == 0) {
635+
final lines = result.stdout.toString().split('\n');
636+
for (final line in lines) {
637+
if (line.contains(':$port')) {
638+
final parts = line.trim().split(RegExp(r'\s+'));
639+
if (parts.isNotEmpty) {
640+
final pid = parts.last;
641+
// Verify it's a valid PID number
642+
if (int.tryParse(pid) != null) {
643+
await Process.run('taskkill', ['/F', '/PID', pid]);
644+
}
645+
}
646+
}
647+
}
648+
}
649+
} else {
650+
// Find process using the port
651+
final result = await Process.run('lsof', ['-ti', ':$port']);
652+
if (result.exitCode == 0) {
653+
final pids = result.stdout.toString().trim().split('\n');
654+
for (final pid in pids) {
655+
if (pid.isNotEmpty) {
656+
await Process.run('kill', ['-9', pid.trim()]);
657+
}
602658
}
603659
}
604660
}
@@ -618,30 +674,39 @@ class MinerProcess {
618674
/// Cleanup any existing quantus-miner processes
619675
Future<void> _cleanupExistingMinerProcesses() async {
620676
try {
621-
// Find all quantus-miner processes
622-
final result = await Process.run('pgrep', ['-f', 'quantus-miner']);
623-
if (result.exitCode == 0) {
624-
final pids = result.stdout.toString().trim().split('\n');
625-
for (final pid in pids) {
626-
if (pid.isNotEmpty) {
627-
try {
628-
// Try graceful termination first
629-
await Process.run('kill', ['-15', pid.trim()]);
630-
await Future.delayed(const Duration(seconds: 1));
631-
632-
// Check if still running, force kill if needed
633-
final checkResult = await Process.run('kill', ['-0', pid.trim()]);
634-
if (checkResult.exitCode == 0) {
635-
await Process.run('kill', ['-9', pid.trim()]);
677+
if (Platform.isWindows) {
678+
try {
679+
await Process.run('taskkill', ['/F', '/IM', 'quantus-miner.exe']);
680+
await Future.delayed(const Duration(seconds: 2));
681+
} catch (_) {
682+
// Process might not exist
683+
}
684+
} else {
685+
// Find all quantus-miner processes
686+
final result = await Process.run('pgrep', ['-f', 'quantus-miner']);
687+
if (result.exitCode == 0) {
688+
final pids = result.stdout.toString().trim().split('\n');
689+
for (final pid in pids) {
690+
if (pid.isNotEmpty) {
691+
try {
692+
// Try graceful termination first
693+
await Process.run('kill', ['-15', pid.trim()]);
694+
await Future.delayed(const Duration(seconds: 1));
695+
696+
// Check if still running, force kill if needed
697+
final checkResult = await Process.run('kill', ['-0', pid.trim()]);
698+
if (checkResult.exitCode == 0) {
699+
await Process.run('kill', ['-9', pid.trim()]);
700+
}
701+
} catch (e) {
702+
// Ignore cleanup errors
636703
}
637-
} catch (e) {
638-
// Ignore cleanup errors
639704
}
640705
}
641-
}
642706

643-
// Wait a moment for processes to fully terminate
644-
await Future.delayed(const Duration(seconds: 2));
707+
// Wait a moment for processes to fully terminate
708+
await Future.delayed(const Duration(seconds: 2));
709+
}
645710
}
646711
} catch (e) {
647712
// Ignore cleanup errors
@@ -651,30 +716,39 @@ class MinerProcess {
651716
/// Cleanup any existing quantus-node processes
652717
Future<void> _cleanupExistingNodeProcesses() async {
653718
try {
654-
// Find all quantus-node processes
655-
final result = await Process.run('pgrep', ['-f', 'quantus-node']);
656-
if (result.exitCode == 0) {
657-
final pids = result.stdout.toString().trim().split('\n');
658-
for (final pid in pids) {
659-
if (pid.isNotEmpty) {
660-
try {
661-
// Try graceful termination first
662-
await Process.run('kill', ['-15', pid.trim()]);
663-
await Future.delayed(const Duration(seconds: 2));
664-
665-
// Check if still running, force kill if needed
666-
final checkResult = await Process.run('kill', ['-0', pid.trim()]);
667-
if (checkResult.exitCode == 0) {
668-
await Process.run('kill', ['-9', pid.trim()]);
719+
if (Platform.isWindows) {
720+
try {
721+
await Process.run('taskkill', ['/F', '/IM', 'quantus-node.exe']);
722+
await Future.delayed(const Duration(seconds: 2));
723+
} catch (_) {
724+
// Process might not exist
725+
}
726+
} else {
727+
// Find all quantus-node processes
728+
final result = await Process.run('pgrep', ['-f', 'quantus-node']);
729+
if (result.exitCode == 0) {
730+
final pids = result.stdout.toString().trim().split('\n');
731+
for (final pid in pids) {
732+
if (pid.isNotEmpty) {
733+
try {
734+
// Try graceful termination first
735+
await Process.run('kill', ['-15', pid.trim()]);
736+
await Future.delayed(const Duration(seconds: 2));
737+
738+
// Check if still running, force kill if needed
739+
final checkResult = await Process.run('kill', ['-0', pid.trim()]);
740+
if (checkResult.exitCode == 0) {
741+
await Process.run('kill', ['-9', pid.trim()]);
742+
}
743+
} catch (e) {
744+
// Ignore cleanup errors
669745
}
670-
} catch (e) {
671-
// Ignore cleanup errors
672746
}
673747
}
674-
}
675748

676-
// Wait a moment for processes to fully terminate
677-
await Future.delayed(const Duration(seconds: 3));
749+
// Wait a moment for processes to fully terminate
750+
await Future.delayed(const Duration(seconds: 3));
751+
}
678752
}
679753
} catch (e) {
680754
// Ignore cleanup errors

0 commit comments

Comments
 (0)