Browse Source

Remove unnecessary CommonProcess abstraction

Mark Monteiro 5 năm trước cách đây
mục cha
commit
ee2f911a2b

+ 3 - 8
Emby.Server.Implementations/ApplicationHost.cs

@@ -1727,15 +1727,15 @@ namespace Emby.Server.Implementations
                 throw new NotSupportedException();
             }
 
-            var process = ProcessFactory.Create(new ProcessOptions
+            var process = ProcessFactory.Create(new ProcessStartInfo
             {
                 FileName = url,
-                EnableRaisingEvents = true,
                 UseShellExecute = true,
                 ErrorDialog = false
             });
 
-            process.Exited += ProcessExited;
+            process.EnableRaisingEvents = true;
+            process.Exited += (sender, args) => ((Process)sender).Dispose(); ;
 
             try
             {
@@ -1748,11 +1748,6 @@ namespace Emby.Server.Implementations
             }
         }
 
-        private static void ProcessExited(object sender, EventArgs e)
-        {
-            ((IProcess)sender).Dispose();
-        }
-
         public virtual void EnableLoopback(string appName)
         {
         }

+ 0 - 152
Emby.Server.Implementations/Diagnostics/CommonProcess.cs

@@ -1,152 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Diagnostics;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Diagnostics;
-
-namespace Emby.Server.Implementations.Diagnostics
-{
-    public class CommonProcess : IProcess
-    {
-        private readonly Process _process;
-
-        private bool _disposed = false;
-        private bool _hasExited;
-
-        public CommonProcess(ProcessOptions options)
-        {
-            StartInfo = options;
-
-            var startInfo = new ProcessStartInfo
-            {
-                Arguments = options.Arguments,
-                FileName = options.FileName,
-                WorkingDirectory = options.WorkingDirectory,
-                UseShellExecute = options.UseShellExecute,
-                CreateNoWindow = options.CreateNoWindow,
-                RedirectStandardError = options.RedirectStandardError,
-                RedirectStandardInput = options.RedirectStandardInput,
-                RedirectStandardOutput = options.RedirectStandardOutput,
-                ErrorDialog = options.ErrorDialog
-            };
-
-
-            if (options.IsHidden)
-            {
-                startInfo.WindowStyle = ProcessWindowStyle.Hidden;
-            }
-
-            _process = new Process
-            {
-                StartInfo = startInfo
-            };
-
-            if (options.EnableRaisingEvents)
-            {
-                _process.EnableRaisingEvents = true;
-                _process.Exited += OnProcessExited;
-            }
-        }
-
-        public event EventHandler Exited;
-
-        public ProcessOptions StartInfo { get; }
-
-        public StreamWriter StandardInput => _process.StandardInput;
-
-        public StreamReader StandardError => _process.StandardError;
-
-        public StreamReader StandardOutput => _process.StandardOutput;
-
-        public int ExitCode => _process.ExitCode;
-
-        private bool HasExited
-        {
-            get
-            {
-                if (_hasExited)
-                {
-                    return true;
-                }
-
-                try
-                {
-                    _hasExited = _process.HasExited;
-                }
-                catch (InvalidOperationException)
-                {
-                    _hasExited = true;
-                }
-
-                return _hasExited;
-            }
-        }
-
-        public void Start()
-        {
-            _process.Start();
-        }
-
-        public void Kill()
-        {
-            _process.Kill();
-        }
-
-        public bool WaitForExit(int timeMs)
-        {
-            return _process.WaitForExit(timeMs);
-        }
-
-        public Task<bool> WaitForExitAsync(int timeMs)
-        {
-            // Note: For this function to work correctly, the option EnableRisingEvents needs to be set to true.
-
-            if (HasExited)
-            {
-                return Task.FromResult(true);
-            }
-
-            timeMs = Math.Max(0, timeMs);
-
-            var tcs = new TaskCompletionSource<bool>();
-
-            var cancellationToken = new CancellationTokenSource(timeMs).Token;
-
-            _process.Exited += (sender, args) => tcs.TrySetResult(true);
-
-            cancellationToken.Register(() => tcs.TrySetResult(HasExited));
-
-            return tcs.Task;
-        }
-
-        public void Dispose()
-        {
-            Dispose(true);
-            GC.SuppressFinalize(this);
-        }
-
-        protected virtual void Dispose(bool disposing)
-        {
-            if (_disposed)
-            {
-                return;
-            }
-
-            if (disposing)
-            {
-                _process?.Dispose();
-            }
-
-            _disposed = true;
-        }
-
-        private void OnProcessExited(object sender, EventArgs e)
-        {
-            _hasExited = true;
-            Exited?.Invoke(this, e);
-        }
-    }
-}

+ 3 - 2
Emby.Server.Implementations/Diagnostics/ProcessFactory.cs

@@ -1,14 +1,15 @@
 #pragma warning disable CS1591
 
+using System.Diagnostics;
 using MediaBrowser.Model.Diagnostics;
 
 namespace Emby.Server.Implementations.Diagnostics
 {
     public class ProcessFactory : IProcessFactory
     {
-        public IProcess Create(ProcessOptions options)
+        public Process Create(ProcessStartInfo startInfo)
         {
-            return new CommonProcess(options);
+            return new Process { StartInfo = startInfo };
         }
     }
 }

+ 11 - 7
Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -3,6 +3,7 @@
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Globalization;
 using System.IO;
 using System.Linq;
@@ -1683,19 +1684,19 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
             try
             {
-                var process = _processFactory.Create(new ProcessOptions
+                var process = _processFactory.Create(new ProcessStartInfo
                 {
                     Arguments = GetPostProcessArguments(path, options.RecordingPostProcessorArguments),
                     CreateNoWindow = true,
-                    EnableRaisingEvents = true,
                     ErrorDialog = false,
                     FileName = options.RecordingPostProcessor,
-                    IsHidden = true,
+                    WindowStyle = ProcessWindowStyle.Hidden,
                     UseShellExecute = false
                 });
 
                 _logger.LogInformation("Running recording post processor {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
 
+                process.EnableRaisingEvents = true;
                 process.Exited += Process_Exited;
                 process.Start();
             }
@@ -1712,11 +1713,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
         private void Process_Exited(object sender, EventArgs e)
         {
-            using (var process = (IProcess)sender)
+            try
             {
-                _logger.LogInformation("Recording post-processing script completed with exit code {ExitCode}", process.ExitCode);
-
-                process.Dispose();
+                var exitCode = ((Process)sender).ExitCode;
+                _logger.LogInformation("Recording post-processing script completed with exit code {ExitCode}", exitCode);
+            }
+            finally
+            {
+                ((Process)sender).Dispose();
             }
         }
 

+ 11 - 12
Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs

@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Globalization;
 using System.IO;
 using System.Text;
@@ -30,7 +31,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         private bool _hasExited;
         private Stream _logFileStream;
         private string _targetPath;
-        private IProcess _process;
+        private Process _process;
         private readonly IProcessFactory _processFactory;
         private readonly IJsonSerializer _json;
         private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
@@ -80,7 +81,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             _targetPath = targetFile;
             Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
 
-            var process = _processFactory.Create(new ProcessOptions
+            _process = _processFactory.Create(new ProcessStartInfo
             {
                 CreateNoWindow = true,
                 UseShellExecute = false,
@@ -91,14 +92,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 FileName = _mediaEncoder.EncoderPath,
                 Arguments = GetCommandLineArgs(mediaSource, inputFile, targetFile, duration),
 
-                IsHidden = true,
-                ErrorDialog = false,
-                EnableRaisingEvents = true
+                WindowStyle = ProcessWindowStyle.Hidden,
+                ErrorDialog = false
             });
 
-            _process = process;
-
-            var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
+            var commandLineLogMessage = _process.StartInfo.FileName + " " + _process.StartInfo.Arguments;
             _logger.LogInformation(commandLineLogMessage);
 
             var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "record-transcode-" + Guid.NewGuid() + ".txt");
@@ -110,16 +108,17 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(_json.SerializeToString(mediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
             _logFileStream.Write(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length);
 
-            process.Exited += (sender, args) => OnFfMpegProcessExited(process, inputFile);
+            _process.EnableRaisingEvents = true;
+            _process.Exited += (sender, args) => OnFfMpegProcessExited(_process, inputFile);
 
-            process.Start();
+            _process.Start();
 
             cancellationToken.Register(Stop);
 
             onStarted();
 
             // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
-            StartStreamingLog(process.StandardError.BaseStream, _logFileStream);
+            StartStreamingLog(_process.StandardError.BaseStream, _logFileStream);
 
             _logger.LogInformation("ffmpeg recording process started for {0}", _targetPath);
 
@@ -293,7 +292,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         /// <summary>
         /// Processes the exited.
         /// </summary>
-        private void OnFfMpegProcessExited(IProcess process, string inputFile)
+        private void OnFfMpegProcessExited(Process process, string inputFile)
         {
             _hasExited = true;
 

+ 66 - 0
MediaBrowser.Common/Extensions/ProcessExtensions.cs

@@ -0,0 +1,66 @@
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Common.Extensions
+{
+    /// <summary>
+    /// Extension methods for <see cref="Process"/>.
+    /// </summary>
+    public static class ProcessExtensions
+    {
+        /// <summary>
+        /// Gets a value indicating whether the associated process has been terminated using
+        /// <see cref="Process.HasExited"/>. This is safe to call even if there is no operating system process
+        /// associated with the <see cref="Process"/>.
+        /// </summary>
+        /// <param name="process">The process to check the exit status for.</param>
+        /// <returns>
+        /// True if the operating system process referenced by the <see cref="Process"/> component has
+        /// terminated, or if there is no associated operating system process; otherwise, false.
+        /// </returns>
+        public static bool HasExitedSafe(this Process process)
+        {
+            try
+            {
+                return process.HasExited;
+            }
+            catch (InvalidOperationException)
+            {
+                return true;
+            }
+        }
+
+        /// <summary>
+        /// Asynchronously wait for the process to exit.
+        /// </summary>
+        /// <param name="process">The process to wait for.</param>
+        /// <param name="timeMs">A timeout, in milliseconds, after which to stop waiting for the task.</param>
+        /// <returns>True if the task exited normally, false if the timeout elapsed before the process exited.</returns>
+        public static async Task<bool> WaitForExitAsync(this Process process, int timeMs)
+        {
+            if (!process.EnableRaisingEvents)
+            {
+                throw new InvalidOperationException("EnableRisingEvents must be enabled to async wait for a task to exit.");
+            }
+
+            // Add an event handler for the process exit event
+            var tcs = new TaskCompletionSource<bool>();
+            process.Exited += (sender, args) => tcs.TrySetResult(true);
+
+            // Return immediately if the process has already exited
+            if (process.HasExitedSafe())
+            {
+                return true;
+            }
+
+            // Add an additional timeout then await
+            using (var cancelTokenSource = new CancellationTokenSource(Math.Max(0, timeMs)))
+            using (var cancelRegistration = cancelTokenSource.Token.Register(() => tcs.TrySetResult(process.HasExitedSafe())))
+            {
+                return await tcs.Task.ConfigureAwait(false);
+            }
+        }
+    }
+}

+ 16 - 15
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -13,7 +13,6 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.MediaEncoding.Probing;
 using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Diagnostics;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Globalization;
@@ -22,6 +21,8 @@ using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.System;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Configuration;
+using System.Diagnostics;
+using MediaBrowser.Model.Diagnostics;
 
 namespace MediaBrowser.MediaEncoding.Encoder
 {
@@ -362,7 +363,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 : "{0} -i {1} -threads 0 -v warning -print_format json -show_streams -show_format";
             args = string.Format(args, probeSizeArgument, inputPath).Trim();
 
-            var process = _processFactory.Create(new ProcessOptions
+            var process = _processFactory.Create(new ProcessStartInfo
             {
                 CreateNoWindow = true,
                 UseShellExecute = false,
@@ -374,10 +375,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 Arguments = args,
 
 
-                IsHidden = true,
+                WindowStyle = ProcessWindowStyle.Hidden,
                 ErrorDialog = false,
-                EnableRaisingEvents = true
             });
+            process.EnableRaisingEvents = true;
 
             if (forceEnableLogging)
             {
@@ -571,16 +572,16 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 }
             }
 
-            var process = _processFactory.Create(new ProcessOptions
+            var process = _processFactory.Create(new ProcessStartInfo
             {
                 CreateNoWindow = true,
                 UseShellExecute = false,
                 FileName = _ffmpegPath,
                 Arguments = args,
-                IsHidden = true,
+                WindowStyle = ProcessWindowStyle.Hidden,
                 ErrorDialog = false,
-                EnableRaisingEvents = true
             });
+            process.EnableRaisingEvents = true;
 
             _logger.LogDebug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
 
@@ -700,16 +701,16 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 }
             }
 
-            var process = _processFactory.Create(new ProcessOptions
+            var process = _processFactory.Create(new ProcessStartInfo
             {
                 CreateNoWindow = true,
                 UseShellExecute = false,
                 FileName = _ffmpegPath,
                 Arguments = args,
-                IsHidden = true,
-                ErrorDialog = false,
-                EnableRaisingEvents = true
+                WindowStyle = ProcessWindowStyle.Hidden,
+                ErrorDialog = false
             });
+            process.EnableRaisingEvents = true;
 
             _logger.LogInformation(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
 
@@ -949,14 +950,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             private bool _disposed = false;
 
-            public ProcessWrapper(IProcess process, MediaEncoder mediaEncoder)
+            public ProcessWrapper(Process process, MediaEncoder mediaEncoder)
             {
                 Process = process;
                 _mediaEncoder = mediaEncoder;
                 Process.Exited += OnProcessExited;
             }
 
-            public IProcess Process { get; }
+            public Process Process { get; }
 
             public bool HasExited { get; private set; }
 
@@ -964,7 +965,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             void OnProcessExited(object sender, EventArgs e)
             {
-                var process = (IProcess)sender;
+                var process = (Process)sender;
 
                 HasExited = true;
 
@@ -979,7 +980,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 DisposeProcess(process);
             }
 
-            private void DisposeProcess(IProcess process)
+            private void DisposeProcess(Process process)
             {
                 lock (_mediaEncoder._runningProcessesLock)
                 {

+ 7 - 6
MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Concurrent;
+using System.Diagnostics;
 using System.Globalization;
 using System.IO;
 using System.Linq;
@@ -429,14 +430,13 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 encodingParam = " -sub_charenc " + encodingParam;
             }
 
-            var process = _processFactory.Create(new ProcessOptions
+            var process = _processFactory.Create(new ProcessStartInfo
             {
                 CreateNoWindow = true,
                 UseShellExecute = false,
                 FileName = _mediaEncoder.EncoderPath,
                 Arguments = string.Format("{0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath, outputPath),
-                EnableRaisingEvents = true,
-                IsHidden = true,
+                WindowStyle = ProcessWindowStyle.Hidden,
                 ErrorDialog = false
             });
 
@@ -453,6 +453,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 throw;
             }
 
+            process.EnableRaisingEvents = true;
             var ranToCompletion = await process.WaitForExitAsync(300000).ConfigureAwait(false);
 
             if (!ranToCompletion)
@@ -578,14 +579,13 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 outputCodec,
                 outputPath);
 
-            var process = _processFactory.Create(new ProcessOptions
+            var process = _processFactory.Create(new ProcessStartInfo
             {
                 CreateNoWindow = true,
                 UseShellExecute = false,
-                EnableRaisingEvents = true,
                 FileName = _mediaEncoder.EncoderPath,
                 Arguments = processArgs,
-                IsHidden = true,
+                WindowStyle = ProcessWindowStyle.Hidden,
                 ErrorDialog = false
             });
 
@@ -602,6 +602,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 throw;
             }
 
+            process.EnableRaisingEvents = true;
             var ranToCompletion = await process.WaitForExitAsync(300000).ConfigureAwait(false);
 
             if (!ranToCompletion)

+ 0 - 23
MediaBrowser.Model/Diagnostics/IProcess.cs

@@ -1,23 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.IO;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.Diagnostics
-{
-    public interface IProcess : IDisposable
-    {
-        event EventHandler Exited;
-
-        void Kill();
-        bool WaitForExit(int timeMs);
-        Task<bool> WaitForExitAsync(int timeMs);
-        int ExitCode { get; }
-        void Start();
-        StreamWriter StandardInput { get; }
-        StreamReader StandardError { get; }
-        StreamReader StandardOutput { get; }
-        ProcessOptions StartInfo { get; }
-    }
-}

+ 3 - 16
MediaBrowser.Model/Diagnostics/IProcessFactory.cs

@@ -1,24 +1,11 @@
 #pragma warning disable CS1591
 
+using System.Diagnostics;
+
 namespace MediaBrowser.Model.Diagnostics
 {
     public interface IProcessFactory
     {
-        IProcess Create(ProcessOptions options);
-    }
-
-    public class ProcessOptions
-    {
-        public string FileName { get; set; }
-        public string Arguments { get; set; }
-        public string WorkingDirectory { get; set; }
-        public bool CreateNoWindow { get; set; }
-        public bool UseShellExecute { get; set; }
-        public bool EnableRaisingEvents { get; set; }
-        public bool ErrorDialog { get; set; }
-        public bool RedirectStandardError { get; set; }
-        public bool RedirectStandardInput { get; set; }
-        public bool RedirectStandardOutput { get; set; }
-        public bool IsHidden { get; set; }
+        Process Create(ProcessStartInfo options);
     }
 }