ObjectDisposedException
discostu105 opened this issue · 2 comments
discostu105 commented
Hello. Thanks for this awesome library. It's very useful!
I've just integrated it into my app and it works most of the time, but sometimes I get this exception:
System.ObjectDisposedException: Safe handle has been closed.
Object name: 'SafeHandle'.
at System.Runtime.InteropServices.SafeHandle.DangerousAddRef(Boolean& success)
at Fs.Processes.JobObjects.JobObject.Notification(JobObjectMessage notifyMessage, IntPtr notifyData)
at Fs.Processes.JobObjects.JobObjectCompletionPort.IoCompletionPort.IoCompletionPortReader()
--- End of stack trace from previous location ---
at Fs.Processes.JobObjects.JobObjectCompletionPort.IoCompletionPort.<>c.<IoCompletionPortReader>b__6_0(Object edi)
at System.Threading.ThreadPoolWorkQueue.Dispatch()
Do you have an idea what's wrong? Shouldn't I dispose the JobObject?
Here is my code:
private async Task<ProcessRunner> Start() {
Console.WriteLine($"starting process. {this.ToString()}");
await Task.Run(async () => {
try {
using var jobObject = new JobObject();
var startInfo = new Fs.Processes.CreateProcessInfo() {
FileName = executable,
WorkingDirectory = workingDir.FullName,
Arguments = string.Join(" ", arguments),
RedirectStandardOutput = redirectOutput,
RedirectStandardError = redirectError,
WindowShow = Fs.Processes.WindowShow.Hidden
};
using var process = StartProcess(jobObject, startInfo);
Task outTask = null;
Task errTask = null;
if (redirectOutput) {
process.OutputDataReceived += (s, d) => { if (d.Data != null) stdOutSb.AppendLine(d.Data); };
outTask = process.BeginReadingStandardOutputAsync();
}
if (redirectError) {
process.ErrorDataReceived += (s, d) => { if (d.Data != null) stdErrSb.AppendLine(d.Data); };
errTask = process.BeginReadingStandardErrorAsync();
}
if (outTask != null) await outTask;
if (errTask != null) await errTask;
await process.Exited;
ExitCode = process.ExitCode ?? -1;
} catch (Exception e) {
throw new ProcessRunnerException($"An exception occurred while starting a process: {this.ToString()}", e);
}
});
return this;
}
private Fs.Processes.Process StartProcess(JobObject jobObjects, Fs.Processes.CreateProcessInfo startInfo) {
if (maxMemoryMb.HasValue || maxCpuPercent.HasValue) {
var jobLimits = new JobLimits();
if (maxMemoryMb.HasValue) {
jobLimits.MaximumProcessMemory = maxMemoryMb.Value * 1024 * 1024;
}
if (maxCpuPercent.HasValue) {
jobLimits.CpuRate = new CpuRateLimit(maxCpuPercent.Value, true);
}
jobObjects.SetLimits(jobLimits);
return jobObjects.CreateProcess(startInfo);
} else {
return new Fs.Processes.Process(startInfo);
}
}
I'd be grateful for any hints :).
williamb1024 commented
I believe the error that you are experiencing is a race condition between a kernel notification and the JobObject SafeHandle being disposed.
Until I (or someone) can add some protection into the routines to ensure that the race doesn't occur, you can add:
await jobObject.Idle;
after you assign the ExitCode value.
discostu105 commented
Awesome, thank you for responding! I will try that workaround.