Esempio n. 1
0
        private async void CompletionCallback(object context, bool timedOut)
        {
            if (timedOut)
            {
                Volatile.Write(ref m_timedout, true);

                // Attempt to dump the timed out process before killing it
                if (!m_processHandle.IsInvalid && !m_processHandle.IsClosed && m_workingDirectory != null)
                {
                    DumpFileDirectory = m_timeoutDumpDirectory;

                    Exception dumpCreationException;

                    try
                    {
                        Directory.CreateDirectory(DumpFileDirectory);
                    }
                    catch (Exception ex)
                    {
                        DumpCreationException = ex;
                    }

                    if (DumpCreationException == null &&
                        !ProcessDumper.TryDumpProcessAndChildren(
                            parentProcessId: m_processId,
                            dumpDirectory: DumpFileDirectory,
                            primaryDumpCreationException: out dumpCreationException))
                    {
                        DumpCreationException = dumpCreationException;
                    }
                }

                Kill(ExitCodes.Timeout);

                using (await m_syncSemaphore.AcquireAsync())
                {
                    if (m_processWaitHandle != null)
                    {
                        await m_processWaitHandle;
                        m_exited = true;
                    }
                }
            }
            else
            {
                m_exited = true;
            }

            using (var semaphoreReleaser = await m_syncSemaphore.AcquireAsync())
            {
                Contract.Assume(m_waiting, "CompletionCallback should only be triggered once.");
                StopWaiting(semaphoreReleaser);
            }

            try
            {
                await Task.Run(
                    async() =>
                {
                    // Before waiting on anything, we call the processExiting callback.
                    // This callback happens to be responsible for triggering or forcing
                    // cleanup of all processes in this job. We can't finish waiting on pipe EOF
                    // (error, output, report, and process-injector pipes) until all handles
                    // to the write-sides are closed.
                    Func <Task> processExiting = m_processExitingAsync;
                    if (processExiting != null)
                    {
                        await processExiting();
                    }

                    using (var semaphoreReleaser = await m_syncSemaphore.AcquireAsync())
                    {
                        // Error and output pipes: Finish reading and then expect EOF (see above).
                        await WaitUntilErrorAndOutputEof(false, semaphoreReleaser);

                        // Stop process injection service. This finishes reading the injector control pipe (for injection requests).
                        // Since we don't get to the 'end' of the pipe until all child-processes holding on to it exit, we must
                        // perform this wait after processExiting() above.
                        if (m_processInjector != null)
                        {
                            // Stop() discards all unhandled requests. That is only safe to do since we are assuming that all processes
                            // in the job have exited (so those requests aren't relevant anymore)
                            await m_processInjector.Stop();
                            m_hasDetoursFailures = m_processInjector.HasDetoursInjectionFailures;
                            m_processInjector.Dispose();
                            m_processInjector = null;
                        }
                    }

                    // Now, callback for additional cleanup (can safely wait on extra pipes, such as the SandboxedProcess report pipe,
                    // if processExiting causes process tree teardown; see above).
                    var processExited = m_processExited;
                    if (processExited != null)
                    {
                        await processExited();
                    }
                });
            }
            catch (Exception exception)
            {
                // Something above may fail and that has to be observed. Unfortunately, throwing a normal exception in a continuation
                // just means someone has to observe the continuation. So we tug on some bootstraps by killing the process here.
                // TODO: It'd be nice if we had a FailFast equivalent that went through AppDomain.UnhandledExceptionEvent for logging.
                ExceptionHandling.OnFatalException(exception, "FailFast in DetouredProcess completion callback");
            }
        }