using System.Diagnostics; using LiquidCode.Tester.Worker.Services.Isolate; namespace LiquidCode.Tester.Worker.Services; /// /// Python program execution service using Isolate sandbox /// public class PythonExecutionServiceIsolate : IExecutionService { private readonly ILogger _logger; private readonly IsolateService _isolateService; private readonly IsolateBoxPool _boxPool; private readonly IConfiguration _configuration; public PythonExecutionServiceIsolate( ILogger logger, IsolateService isolateService, IsolateBoxPool boxPool, IConfiguration configuration) { _logger = logger; _isolateService = isolateService; _boxPool = boxPool; _configuration = configuration; } public async Task ExecuteAsync( string executablePath, string inputFilePath, int timeLimitMs, int memoryLimitMb) { _logger.LogInformation( "Executing Python with Isolate: {Executable}, time={TimeLimit}ms, memory={MemoryLimit}MB", executablePath, timeLimitMs, memoryLimitMb); var result = new ExecutionResult(); var stopwatch = Stopwatch.StartNew(); int boxId = -1; try { // Acquire a box from pool boxId = await _boxPool.AcquireBoxAsync(); _logger.LogDebug("Acquired isolate box {BoxId}", boxId); // Initialize the box await _isolateService.InitBoxAsync(boxId); // Copy Python script to box var boxDir = $"/var/local/lib/isolate/{boxId}/box"; var scriptName = Path.GetFileName(executablePath); var boxScriptPath = Path.Combine(boxDir, scriptName); File.Copy(executablePath, boxScriptPath, overwrite: true); // Prepare input/output files inside the sandbox var outputFilePath = Path.Combine(boxDir, "output.txt"); string? sandboxInputPath = null; if (!string.IsNullOrEmpty(inputFilePath) && File.Exists(inputFilePath)) { sandboxInputPath = Path.Combine(boxDir, "input.txt"); File.Copy(inputFilePath, sandboxInputPath, overwrite: true); } // Get Python executable from configuration var pythonExecutable = _configuration["Python:Executable"] ?? "python3"; // Run in Isolate var isolateResult = await _isolateService.RunAsync(new IsolateRunOptions { BoxId = boxId, Executable = $"/usr/bin/{pythonExecutable}", Arguments = new[] { $"/box/{scriptName}" }, TimeLimitSeconds = timeLimitMs / 1000.0, WallTimeLimitSeconds = (timeLimitMs / 1000.0) * 2, MemoryLimitKb = memoryLimitMb * 1024, StackLimitKb = 256 * 1024, ProcessLimit = 1, // Single process for Python EnableNetwork = false, StdinFile = sandboxInputPath, StdoutFile = outputFilePath, WorkingDirectory = "/box" }); stopwatch.Stop(); // Read output if (File.Exists(outputFilePath)) { result.Output = await File.ReadAllTextAsync(outputFilePath); } // Map Isolate result to ExecutionResult result.ExecutionTimeMs = (long)(isolateResult.CpuTimeSeconds * 1000); result.MemoryUsedMb = isolateResult.MemoryUsedKb / 1024; result.ErrorOutput = isolateResult.ErrorOutput; result.ExitCode = isolateResult.ExitCode; if (isolateResult.TimeLimitExceeded) { result.TimeLimitExceeded = true; result.ErrorMessage = $"Time limit exceeded: {isolateResult.CpuTimeSeconds:F3}s"; _logger.LogWarning("Time limit exceeded for box {BoxId}", boxId); } else if (isolateResult.MemoryLimitExceeded) { result.MemoryLimitExceeded = true; result.ErrorMessage = $"Memory limit exceeded: {isolateResult.MemoryUsedKb / 1024}MB"; if (isolateResult.CgroupOomKilled) { result.ErrorMessage += " (OOM killed by cgroup)"; } _logger.LogWarning("Memory limit exceeded for box {BoxId}", boxId); } else if (isolateResult.RuntimeError) { result.RuntimeError = true; result.ErrorMessage = $"Runtime error: {isolateResult.Message}"; if (isolateResult.ExitSignal.HasValue) { result.ErrorMessage += $" (signal {isolateResult.ExitSignal})"; } _logger.LogWarning("Runtime error for box {BoxId}: {Message}", boxId, isolateResult.Message); } else if (isolateResult.ExitCode == 0) { result.Success = true; _logger.LogInformation( "Execution successful: time={Time}ms, memory={Memory}MB, box={BoxId}", result.ExecutionTimeMs, result.MemoryUsedMb, boxId); } else { result.RuntimeError = true; result.ErrorMessage = $"Non-zero exit code: {isolateResult.ExitCode}"; _logger.LogWarning("Non-zero exit code {ExitCode} for box {BoxId}", isolateResult.ExitCode, boxId); } _logger.LogDebug( "Isolate stats: CPU={Cpu}s, Wall={Wall}s, Memory={Mem}KB", isolateResult.CpuTimeSeconds, isolateResult.WallTimeSeconds, isolateResult.MemoryUsedKb); } catch (Exception ex) { stopwatch.Stop(); result.ExecutionTimeMs = stopwatch.ElapsedMilliseconds; result.RuntimeError = true; result.ErrorMessage = $"Execution error: {ex.Message}"; _logger.LogError(ex, "Error during Isolate execution"); } finally { // Cleanup box if (boxId >= 0) { try { await _isolateService.CleanupBoxAsync(boxId); _logger.LogDebug("Cleaned up isolate box {BoxId}", boxId); } catch (Exception ex) { _logger.LogWarning(ex, "Failed to cleanup box {BoxId}", boxId); } // Release box back to pool _boxPool.ReleaseBox(boxId); _logger.LogDebug("Released box {BoxId} back to pool", boxId); } } return result; } }