// TODO: double check and maybe change order of parameters public ProcessExecutionResult Execute(string fileName, string inputData, int timeLimit, int memoryLimit, IEnumerable <string> executionArguments = null) { var result = new ProcessExecutionResult { Type = ProcessExecutionResultType.Success }; var workingDirectory = new FileInfo(fileName).DirectoryName; using (var restrictedProcess = new RestrictedProcess(fileName, workingDirectory, executionArguments, Math.Max(4096, (inputData.Length * 2) + 4))) { // Write to standard input using another thread restrictedProcess.StandardInput.WriteLineAsync(inputData).ContinueWith( delegate { // ReSharper disable once AccessToDisposedClosure if (!restrictedProcess.IsDisposed) { // ReSharper disable once AccessToDisposedClosure restrictedProcess.StandardInput.FlushAsync().ContinueWith( delegate { restrictedProcess.StandardInput.Close(); }); } }); // Read standard output using another thread to prevent process locking (waiting us to empty the output buffer) var processOutputTask = restrictedProcess.StandardOutput.ReadToEndAsync().ContinueWith( x => { result.ReceivedOutput = x.Result; }); // Read standard error using another thread var errorOutputTask = restrictedProcess.StandardError.ReadToEndAsync().ContinueWith( x => { result.ErrorOutput = x.Result; }); // Read memory consumption every few milliseconds to determine the peak memory usage of the process const int TimeIntervalBetweenTwoMemoryConsumptionRequests = 45; var memoryTaskCancellationToken = new CancellationTokenSource(); var memoryTask = Task.Run( () => { while (true) { // ReSharper disable once AccessToDisposedClosure var peakWorkingSetSize = restrictedProcess.PeakWorkingSetSize; result.MemoryUsed = Math.Max(result.MemoryUsed, peakWorkingSetSize); if (memoryTaskCancellationToken.IsCancellationRequested) { return; } Thread.Sleep(TimeIntervalBetweenTwoMemoryConsumptionRequests); } }, memoryTaskCancellationToken.Token); // Start the process restrictedProcess.Start(timeLimit, memoryLimit); // Wait the process to complete. Kill it after (timeLimit * 1.5) milliseconds if not completed. // We are waiting the process for more than defined time and after this we compare the process time with the real time limit. var exited = restrictedProcess.WaitForExit((int)(timeLimit * 1.5)); if (!exited) { restrictedProcess.Kill(); result.Type = ProcessExecutionResultType.TimeLimit; } // Close the memory consumption check thread memoryTaskCancellationToken.Cancel(); try { // To be sure that memory consumption will be evaluated correctly memoryTask.Wait(TimeIntervalBetweenTwoMemoryConsumptionRequests); } catch (AggregateException ex) { logger.Warn("AggregateException caught.", ex.InnerException); } // Close the task that gets the process error output try { errorOutputTask.Wait(100); } catch (AggregateException ex) { logger.Warn("AggregateException caught.", ex.InnerException); } // Close the task that gets the process output try { processOutputTask.Wait(100); } catch (AggregateException ex) { logger.Warn("AggregateException caught.", ex.InnerException); } Debug.Assert(restrictedProcess.HasExited, "Restricted process didn't exit!"); // Report exit code and total process working time result.ExitCode = restrictedProcess.ExitCode; result.TimeWorked = restrictedProcess.ExitTime - restrictedProcess.StartTime; result.PrivilegedProcessorTime = restrictedProcess.PrivilegedProcessorTime; result.UserProcessorTime = restrictedProcess.UserProcessorTime; } if (result.TotalProcessorTime.TotalMilliseconds > timeLimit) { result.Type = ProcessExecutionResultType.TimeLimit; } if (!string.IsNullOrEmpty(result.ErrorOutput)) { result.Type = ProcessExecutionResultType.RunTimeError; } if (result.MemoryUsed > memoryLimit) { result.Type = ProcessExecutionResultType.MemoryLimit; } return(result); }
public static void Execute(string fileName, string inputData, int timeLimit, int memoryLimit, IEnumerable <string> executionArguments = null) { result = "ok"; var workingDirectory = new FileInfo(fileName).DirectoryName; using (var restrictedProcess = new RestrictedProcess(fileName, workingDirectory, executionArguments, Math.Max(4096, (inputData.Length * 2) + 4))) { restrictedProcess.StandardInput.WriteLineAsync(inputData).ContinueWith( delegate { if (!restrictedProcess.IsDisposed) { restrictedProcess.StandardInput.FlushAsync().ContinueWith( delegate { restrictedProcess.StandardInput.Close(); }); } }); var processOutputTask = restrictedProcess.StandardOutput.ReadToEndAsync().ContinueWith( x => { output = x.Result; }); var errorOutputTask = restrictedProcess.StandardError.ReadToEndAsync().ContinueWith( x => { erroroutput = x.Result; }); const int TimeIntervalBetweenTwoMemoryConsumptionRequests = 45; var memoryTaskCancellationToken = new CancellationTokenSource(); var memoryTask = Task.Run( () => { while (true) { var peakWorkingSetSize = restrictedProcess.PeakWorkingSetSize; memoryused = Math.Max(memoryused, peakWorkingSetSize); if (memoryTaskCancellationToken.IsCancellationRequested) { return; } Thread.Sleep(TimeIntervalBetweenTwoMemoryConsumptionRequests); } }, memoryTaskCancellationToken.Token); restrictedProcess.Start(timeLimit, memoryLimit); var exited = restrictedProcess.WaitForExit((int)(timeLimit * 1.5)); if (!exited) { restrictedProcess.Kill(); result = "tl"; } memoryTaskCancellationToken.Cancel(); try { memoryTask.Wait(TimeIntervalBetweenTwoMemoryConsumptionRequests); } catch (AggregateException ex) { } try { errorOutputTask.Wait(100); } catch (AggregateException ex) { } try { processOutputTask.Wait(100); } catch (AggregateException ex) { } Debug.Assert(restrictedProcess.HasExited, "Restricted process didn't exit!"); exitcode = restrictedProcess.ExitCode; time = restrictedProcess.ExitTime - restrictedProcess.StartTime; processortime = restrictedProcess.PrivilegedProcessorTime; userprocessortime = restrictedProcess.UserProcessorTime; } if ((processortime + userprocessortime).TotalMilliseconds > timeLimit) { result = "tl"; } if (!string.IsNullOrEmpty(erroroutput)) { result = "re"; } if (memoryused > memoryLimit) { result = "ml"; } }