Skip to content

Commit

Permalink
(chocolateyGH-8) ReadLine Timeout
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ferventcoder committed Sep 15, 2015
1 parent ee99517 commit 271993d
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/chocolatey/chocolatey.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@
<Compile Include="infrastructure.app\domain\CommandNameType.cs" />
<Compile Include="infrastructure\events\EventManager.cs" />
<Compile Include="infrastructure\events\IEvent.cs" />
<Compile Include="infrastructure\commandline\ReadLineTimeout.cs" />
<Compile Include="infrastructure\registration\SimpleInjectorContainer.cs" />
<Compile Include="infrastructure\results\IResult.cs" />
<Compile Include="infrastructure\results\PackageResult.cs" />
Expand Down
14 changes: 11 additions & 3 deletions src/chocolatey/infrastructure/adapters/Console.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
namespace chocolatey.infrastructure.adapters
{
using System.IO;
using commandline;

public sealed class Console : IConsole
{
Expand All @@ -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());
}
}
}
}
12 changes: 11 additions & 1 deletion src/chocolatey/infrastructure/adapters/IConsole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public interface IConsole
/// <filterpriority>1</filterpriority>
string ReadLine();

string ReadLine(int timeoutMilliseconds);

/// <summary>
/// Gets the standard error output stream.
/// </summary>
Expand All @@ -47,7 +49,15 @@ public interface IConsole
/// </returns>
/// <filterpriority>1</filterpriority>
TextWriter Error { get; }

/// <summary>
/// Writes the specified string value to the standard output stream.
/// </summary>
/// <param name="value">The value to write.</param>
/// <exception cref="T:System.IO.IOException">An I/O error occurred.</exception>
/// <filterpriority>1</filterpriority>
void Write(object value);
}

// ReSharper restore InconsistentNaming
}
}
81 changes: 81 additions & 0 deletions src/chocolatey/infrastructure/commandline/ReadLineTimeout.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Because sometimes you to timeout a readline instead of blocking infinitely.
/// </summary>
/// <remarks>
/// Based on http://stackoverflow.com/a/18342182/18475
/// </remarks>
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();
}
}
}

0 comments on commit 271993d

Please sign in to comment.