Console.In.ReadLine (redirected StdIn) freezes all other threads

I am connecting an outdated application (written in COBOL) with our .NET new ones using channels. The idea is simple: an outdated program (my ERP menu) writes some parameters to the stream, the .NET application reads it through the Console.In stream and starts a new stream that opens the requested screen. Here is a snippet from the .NET side of how this idea works:

 <STAThread(), LoaderOptimization(LoaderOptimization.MultiDomain)> Shared Sub Main() If Environment.GetCommandLineArgs(1) = "PIPE" While True Dim pipeParam as String Try pipeParam = Console.In.ReadLine() Catch e as Exception Exit Sub End Try ' Deal with parameters here If pipeParam = "END" Dim newThread as New Threading.Thread(Sub() ' Create a new AppDomain and Loads the menu option, generally a Winforms form. End Sub) newThread.Start() End If End While End If End Sub 

Everything worked perfectly and easily ... until today. I deployed this solution in my client environment (Windows Server 2003), and it happened that none of the requested threads were executed, except when the called process (COBOL) was terminated (i.e. Console.In was forcibly closed) . From now on, all requested winforms will begin to display and behave as expected.

Digging through this strange behavior in logs, I found out that threads usually execute until the IDictionary.ContainsKey() statement (or some other method that requires the execution of native code) is executed. At that moment, the thread froze / slept.

If I restrict the creation of a stream to, say, three, it happens that each created stream hangs to the third, that is, when Console.In.ReadLine no longer executed.

What should I do? Any tips?

Additional info: The closest direction I have found so far was Hans Passan's answer in this question: Interface freezes in a multithreaded C # application (.NET SystemEvents appears in my debbuger thread list, but I could not solve my problem with the proposed solution )

UPDATED NEWS

I could solve this problem while waiting for a subflow to complete the Form loading. This Done signal is passed through AppDomain.SetData() and AppDomain.GetData() . Anyway, after creating the form, the sub-stream no longer hangs when the main one goes to Console.ReadLine . Although the problem is resolved, I am intrigued by this. I am trying to reproduce this in a "simple" case.

Some additional information

  • Starting point .exe compiled into 32-bit. All other libraries are "AnyCpu". The problem arises both on 32-bit (my client) and 64-bit (my developments) machines (both Windows Server 2003).
  • Updated Sub Main() attributes in the above snippet.
  • I tried to put Console.ReadLine in the workflow. Failed to solve (see the figure below).
  • The COBOL application will not be dependent because it runs in a separate OS process. The pipe is my IPC approach. The COBOL application in this case records only the parameters and does not wait for an answer.
  • The stack trace is in the image below (the PRX001235 stream deserializes the xml configuration file before connecting to the database and before loading the form efficiently - in this case, it still seems to be in managed code - sometimes the PRX001235 stream will hang in its own code when trying to connect to the database data):

Threads vs StackTrace's

+4
source share
3 answers

Firstly, you are doing something very unusual, so unusual results are not unexpected. Secondly, what you are doing is strictly verboten in the Windows SDK docs. Which dictates that the thread that created the single-threaded apartment never allows you to make a blocking call.

Obviously, your worker threads are locked in a lock somewhere inside the bowels of the operating system, a lock occupied by the main thread of your program. This will help a lot to see the call stack of one of these blocked threads to identify a possible block. This requires enabling unmanaged code debugging so that you can see the unmanaged stack frames and allowing the Microsoft Symbol server to get debugging characters for Windows code and make sense from the stack trace.

Without a look at him, I have to think:

  • The .ReadLine () console itself performs an internal lock, which makes the console safe to use from multiple threads by serializing Read () calls. Any thread using the Console method can fall into the same lock. This is hardly the culprit, it is unlikely that these workflows also use the console.

  • The strictly verboten angle is related to the relevance of the in-line processing of the apartment, the COM function. The STA is activated by the [STAThread] attribute in your Main () method of the program or by calling Thread.SetApartmentState (). STA needs to use components that are mostly unsafe, such as windows, clipboard, drag and drop, shell dialogs such as OpenFileDialog and many other COM classes, some of which you cannot recognize because they were wrapped with .NET classes, STA Apartment ensures that these components are used in a thread-safe manner; calling the method of such a component from the workflow is automatically marshaled to the thread that created it. The exact equivalent of Control.Invoke (). Such marshaling requires that the thread rolls a loop of messages in order to send a call to the desired thread. And your main thread is not when it is blocked in a call to Console.ReadLine (). The workflow will be stopped during a call until the main thread starts pumping again. There are very high chances of a dead end, although you have not actually completely braked because the ReadLine () call ultimately ends. It is noteworthy that the CLR avoids such deadlocks, it pumps up a message loop when you use the lock keyword, call WaitOne () on the synchronization object, or call Thread.Join (), a general view of blocking calls in .NET programming. However, it does not do this for Console.ReadLine (), at least until bypassing .NET 4.0, as Mark has shown.

  • Very speculative, caused by your observation that you are avoiding the problem while waiting for the form to be created. You may experience this problem on a 64-bit operating system, and your target project has the x86 platform target set instead of AnyCPU. In this case, your program runs at the WOW64 emulation level, a level that allows a 64-bit operating system to run 32-bit programs. Windows Window Manager is a 64-bit subsystem that calls it to send notifications to a 32-bit window through a tone that switches bit-ns. There is a problem with the Form.Load event, which is fired when the window manager sends a WM_SHOWWINDOW message. It puts the emulation layer in a difficult state because it moves several times from the 64-bit to the 32-bit border. This is also the source of the very annoying exception-swallowing problem described in this answer . The odds are very high that this code contains an emulator lock while the Load event is triggered. So calling Console.ReadLine () on the Load event is likely to be very difficult, I would expect this lock to be passed by any 32-bit worker thread when they make an api call. Very speculative, but easily testable if you can change the platform’s goal to AnyCPU.

Not sure if it's worth chasing the cause of this problem, given that the solution is that simple. Just don't call Console.ReadLine () on your main thread. Instead, call it the workflow. It also stops your user interface from freezing when the COBOL program does not respond. However, note that you should now march yourself with Control.Begin / Invoke () if everything you get also updates your user interface.

+4
source

Comparing .NET 3.5 / 2.0 and .NET 4.0 with Reflector: .NET 4.0 always explicitly calls __ConsoleStream.WaitForAvailableConsoleInput(hFile) in __ConsoleStream.ReadFileNative , and I cannot find an equivalent call in .NET 3.5 / 2.0.

WaitForAvailableConsoleInput InternalCall checks for availability and if this avoids waiting on it if it received data or closed.


Just summing up what I understand, this is the current state of this problem: it can be circumvented by ensuring that other threads (AppDomains) forward messages before allowing the main thread to continue calling ReadLine .

I think this may be the (almost) final answer, as it means that there are Windows messages during the server version of ReadLine , and that is enough for me to know that a download is required. This refers to the Windows documentation, in which you must indicate where Windows messaging is required / used.

+2
source

Have you tried setting these streams to a background thread? by setting Thread.IsBackground = true

0
source

Source: https://habr.com/ru/post/1446723/


All Articles