-
Notifications
You must be signed in to change notification settings - Fork 385
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reading files from (piped) standard input, with correct cancellation support #1073
Comments
Related: #275. |
I did a little further digging and found that Console.ReadAsync or Console.ReadLineAsync always blocks the application. (ReadLineAsync does not even have an overload with a CancellationToken) The only solution that I've found is to wrap this in Task.Run. I'm on mobile right now but I can provide a sample later on. If I may say so, I actually did expect that this library would provide something out of the box to model bind standard input to. For now, it seems that this is still a manual process. |
Update: this class seems to be working nicely, immediately respecting the cancellation token. Unfortunately the Console.ReadLineAsync process is still blocked, but that's okay because a little further down the line, the entire program exits anyway. I wouldn't use this outside CLI applications: public class LinesFromConsoleInputReader
{
public async IAsyncEnumerable<string> Read([EnumeratorCancellation] CancellationToken cancellationToken)
{
var cancellationTcs = new TaskCompletionSource<string?>(TaskCreationOptions.RunContinuationsAsynchronously);
await using var registration = cancellationToken.Register(() => cancellationTcs.SetCanceled());
Task<string?> cancellationTask = cancellationTcs.Task;
while (!cancellationToken.IsCancellationRequested)
{
// A double 'await' is necessary here.
// The first await is for the result of Task.WhenAny, which returns the Task that completes first
// The second await extracts the result of the task returned by Task.WhenAny
// In the normal case, this is the task returned from ReadLineAsync, so awaiting that returns a string?
// In the cancelled case, this returns the task completion source which is set to canceled, so awaiting that throws an OperationCanceledException
var line = await await Task.WhenAny(
// Task.Run is necessary here, because Console.In.ReadLineAsync blocks the thread
Task.Run(() => Console.In.ReadLineAsync(), cancellationToken),
cancellationTask);
if (line == null)
yield break;
yield return line;
}
}
} |
Hi, thanks for the great work so far!
I'm still struggling with one concept though. I want to create a CLI app that can receive input from Console.In, so it can be combined with other cli tools. It should also support proper cancellation. This combination (reading from standard input + cancellation) is where I'm stuck.
Main usage
Example preferred usage (from powershell, but that doesn't really matter)
Fallback 1
If possible, I'd also like to provide an alternative so that the files can optionally be passed in as a positional argument, or if that's not possible, as a named argument. Something like this:
MyApp.exe C:\file1.txt C:\file2.txt C:\file3.txt
or perhaps
Fallback 2
Furthermore, if MyApp.exe is called without any arguments, it should read the files from standard input with support for cancellation.
Using just System.CommandLine (not Dragonfruit), what would MyApp look like? Can I use any of the existing Option infrastructure to implement this?
I've tried the following so far:
The above implementation gets very close to what I want, but I cannot seem to get cancellation support working correctly, because the process is blocked at "Console.In.ReadLine".
A simple summary:
I've tried searching for issues or anything in the docs, but I couldn't find anything that answers my questions.
Thanks for your time.
The text was updated successfully, but these errors were encountered: