From f5b69f601c5109e0283c4360a187f89d2e1f6399 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Tue, 18 Aug 2015 11:15:51 -0500 Subject: [PATCH] (GH-8) ReadLine Timeout Implement a Console.ReadLine with a timeout so that when confirmation is selected it doesn't block the thread eternally. This allows folks to put prompts in chocolatey automation scripts with a default selection and other fun prompting actions. --- src/chocolatey/chocolatey.csproj | 1 + .../infrastructure/adapters/Console.cs | 14 +++- .../infrastructure/adapters/IConsole.cs | 12 ++- .../commandline/ReadLineTimeout.cs | 81 +++++++++++++++++++ 4 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 src/chocolatey/infrastructure/commandline/ReadLineTimeout.cs diff --git a/src/chocolatey/chocolatey.csproj b/src/chocolatey/chocolatey.csproj index 0cf1733747..16715dd371 100644 --- a/src/chocolatey/chocolatey.csproj +++ b/src/chocolatey/chocolatey.csproj @@ -203,6 +203,7 @@ + diff --git a/src/chocolatey/infrastructure/adapters/Console.cs b/src/chocolatey/infrastructure/adapters/Console.cs index 4bff68365b..010f8be3a2 100644 --- a/src/chocolatey/infrastructure/adapters/Console.cs +++ b/src/chocolatey/infrastructure/adapters/Console.cs @@ -16,6 +16,7 @@ namespace chocolatey.infrastructure.adapters { using System.IO; + using commandline; public sealed class Console : IConsole { @@ -24,9 +25,16 @@ public string ReadLine() return System.Console.ReadLine(); } - public TextWriter Error + public string ReadLine(int timeoutMilliseconds) { - get { return System.Console.Error; } + return ReadLineTimeout.read(timeoutMilliseconds); + } + + public TextWriter Error { get { return System.Console.Error; } } + + public void Write(object value) + { + System.Console.Write(value.to_string()); } } -} \ No newline at end of file +} diff --git a/src/chocolatey/infrastructure/adapters/IConsole.cs b/src/chocolatey/infrastructure/adapters/IConsole.cs index 4649af2097..ca597da338 100644 --- a/src/chocolatey/infrastructure/adapters/IConsole.cs +++ b/src/chocolatey/infrastructure/adapters/IConsole.cs @@ -39,6 +39,8 @@ public interface IConsole /// 1 string ReadLine(); + string ReadLine(int timeoutMilliseconds); + /// /// Gets the standard error output stream. /// @@ -47,7 +49,15 @@ public interface IConsole /// /// 1 TextWriter Error { get; } + + /// + /// Writes the specified string value to the standard output stream. + /// + /// The value to write. + /// An I/O error occurred. + /// 1 + void Write(object value); } // ReSharper restore InconsistentNaming -} \ No newline at end of file +} diff --git a/src/chocolatey/infrastructure/commandline/ReadLineTimeout.cs b/src/chocolatey/infrastructure/commandline/ReadLineTimeout.cs new file mode 100644 index 0000000000..8ee1c610f1 --- /dev/null +++ b/src/chocolatey/infrastructure/commandline/ReadLineTimeout.cs @@ -0,0 +1,81 @@ +// Copyright © 2011 - Present RealDimensions Software, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace chocolatey.infrastructure.commandline +{ + using System; + using System.Threading; + + /// + /// Because sometimes you to timeout a readline instead of blocking infinitely. + /// + /// + /// Based on http://stackoverflow.com/a/18342182/18475 + /// + public class ReadLineTimeout : IDisposable + { + private readonly AutoResetEvent _backgroundResponseReset; + private readonly AutoResetEvent _foregroundResponseReset; + private string _input; + private readonly Thread _responseThread; + + private bool _isDisposing; + + private ReadLineTimeout() + { + _backgroundResponseReset = new AutoResetEvent(false); + _foregroundResponseReset = new AutoResetEvent(false); + _responseThread = new Thread(console_read) + { + IsBackground = true + }; + _responseThread.Start(); + } + + private void console_read() + { + while (true) + { + _backgroundResponseReset.WaitOne(); + _input = Console.ReadLine(); + _foregroundResponseReset.Set(); + } + } + + public static string read(int timeoutMilliseconds) + { + using (var readLine = new ReadLineTimeout()) + { + readLine._backgroundResponseReset.Set(); + + return readLine._foregroundResponseReset.WaitOne(timeoutMilliseconds) ? + readLine._input + : null; + } + } + + public void Dispose() + { + if (_isDisposing) return; + + _isDisposing = true; + _responseThread.Abort(); + _backgroundResponseReset.Close(); + _backgroundResponseReset.Dispose(); + _foregroundResponseReset.Close(); + _foregroundResponseReset.Dispose(); + } + } +}