tonerdo / readline

A Pure C# GNU-Readline like library for .NET/.NET Core
MIT License
810 stars 77 forks source link

Issues with .NET Core 3.0.100-preview7-012821 #53

Closed alexrp closed 4 years ago

alexrp commented 5 years ago

When using ReadLine in my .NET Core app (running through dotnet run), I sometimes get this:

Unhandled exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.IO.IOException: The process cannot access the file because it is being used by another process.
   at Interop.ThrowExceptionForIoErrno(ErrorInfo errorInfo, String path, Boolean isDirectory, Func`2 errorRewriter)
   at Interop.CheckIo(Int64 result, String path, Boolean isDirectory, Func`2 errorRewriter)
   at System.IO.StdInReader.ReadKey(Boolean& previouslyProcessed)
   at System.IO.SyncTextReader.ReadKey(Boolean& previouslyProcessed)
   at System.ConsolePal.ReadKey(Boolean intercept)
   at System.ReadLine.GetText(KeyHandler keyHandler)
   at System.ReadLine.Read(String prompt, String default)
   at Flare.Cli.Commands.ReplCommand.Run(Options options, CancellationToken token) in /home/alexrp/flare/flare/src/cli/Commands/ReplCommand.cs:line 37
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Delegate.DynamicInvokeImpl(Object[] args)
   at System.Delegate.DynamicInvoke(Object[] args)
   at System.CommandLine.Invocation.ModelBindingCommandHandler.InvokeAsync(InvocationContext context)
   at System.CommandLine.Invocation.InvocationPipeline.<>c__DisplayClass2_0.<<InvokeAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Invocation.InvocationExtensions.<>c.<<UseParseErrorReporting>b__16_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Invocation.InvocationExtensions.<>c__DisplayClass8_0.<<UseTypoCorrections>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Invocation.InvocationExtensions.<>c.<<UseSuggestDirective>b__7_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Invocation.InvocationExtensions.<>c.<<UseParseDirective>b__6_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Invocation.InvocationExtensions.<>c.<<UseHelp>b__14_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass3_0.<<UseVersionOption>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Invocation.InvocationExtensions.<>c.<<RegisterWithDotnetSuggest>b__17_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Invocation.InvocationExtensions.<>c__DisplayClass5_0.<<UseExceptionHandler>b__0>d.MoveNext()

It's not consistent, and I'm not sure how to reliably reproduce it. I am also using System.CommandLine in this app, so at first I thought there might be some kind of conflict, but I don't believe that is the case as System.CommandLine does not appear to touch Console.In anywhere.

alexrp commented 5 years ago

Perhaps relevant: This is running under WSL 2. Starting a fresh shell or terminal appears to fix it.

Latency commented 5 years ago

File is locked. Be sure to dispose your object upon app termination and that no zombie pids are lingering. Rebuild, rereun. Only the std streams and the original target invocation utilizing the readline library are applicable. I would check to be sure your app isn't throwing an internal exception and that it terminates properly.

Possible race/thread scheduling issue during disposition?

alexrp commented 5 years ago

Wait, why would it matter how the app terminates? It's just the stdin stream; it's not a thing that persists across multiple runs of the app.

Latency commented 5 years ago

Console.In uses the STDIN stream.

The ReadLine method executes synchronously. That is, it blocks until a line is read or the Ctrl+Z keyboard combination is pressed. The In property returns a TextReader object that represents the standard input stream and that has both a synchronous TextReader.ReadLine method and an asynchronous TextReader.ReadLineAsync method. However, when used as the console's standard input stream, the TextReader.ReadLineAsync executes synchronously rather than asynchronously and returns a Task only after the read operation has completed.

Are you calling this using dependancy injection or across application domains?

alexrp commented 5 years ago

Neither. I am simply invoking ReadLine.Read() directly from the main thread.

Latency commented 5 years ago

I'm not sure sure this point without debugging your code.

Check the calling scope using blocks lifetime, in conjunction with any subsequent calls associated for readline.

It appears that you have some threading/async invocations going on which would need to be scheduled or dispatched accordingly.

You claim its on the main thread, but according to the ST, the stream is already open and locked while it tries to open it again.

Can you ensure the stream is closed before attempting to use it again?

alexrp commented 5 years ago

I do not have any other code that's accessing Console.In in any way, hence my confusion.

I am certain it's executing on the main thread - despite the InvokeAsync in the stack trace, my command handler delegate is not async, nor does it return a Task, so System.CommandLine executes it on the main thread.

alexrp commented 4 years ago

This issue seems to have gone away completely at some point, so I'll go ahead and close this.