Skip to content
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

WSL2 process never releases and gets frozen forever when stdin is redirected #4424

Closed
vromaniks opened this issue Aug 20, 2019 · 6 comments
Closed
Assignees

Comments

@vromaniks
Copy link

vromaniks commented Aug 20, 2019

Please note this bug is already known (#2592), it was fixed in 1803, but with latest (I suppose) Insider Preview the problem is become still actual.

How to reproduce:
Convert distro (ubuntu1804 for example) to WSL2.
Run "ubuntu1804 run env" from your app and redirect stdin (by setting RedirectStandardInput = true for .NET or .hStdInput for Win32Api). Wait for process to finish (WaitForSingleObject or WaitForExit()).
Expected result: process ends (as it happens with WSL1).
Actual result: process never ends. On Windows side descriptor never releases.
On Linux side "init " process becomes zombie (Z)

Here's simple example which shows the problem in Java:

Environment:

Microsoft Windows [Version 10.0.18963.1000]
java version "1.8.0_211"
Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)

STR
Following example shoud start ubuntu run env, echo output and quit. However, ubuntu.exe waiting for input.

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

public class UbuntuHang {
  public static void main(String[] args) throws IOException, InterruptedException {
    ProcessBuilder builder = new ProcessBuilder("ubuntu", "run", "env");
    Process process = builder.start();

    try (BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
      String s;
      while ((s = in.readLine()) != null) {
        System.out.println(s);
      }
    }

    System.exit(process.waitFor());
  }
}
@ghost
Copy link

ghost commented Sep 13, 2019

Some more info:

  • It is blocked on ReadFile (at least in my case).
  • I wasn't able to reproduce it using Win32API nor using .NET. I created process using pipe as stdout, read data from other side of pipe until 0, I then waited for process handler. Everything worked as expected. It seems to be java-spefic issue.
  • When you read data on another thread, main thread is blocked on WaitForSingleObject trying to wait for process exit code (and background thread is blocked on ReadFile)
  • At the same time, I have [defunct] (Zombie) init in Linux. So, my process finished, but init (I assume every ubuntu1804.exe has init pair in Linux) waits for someone to read it's exit code. And on Windows side I am blocked exactly at WaitForSingleObject inside of Java.

We can try to debug OpenJDK as last resort, but any help is appriciated.

SergeyZh pushed a commit to JetBrains/intellij-community that referenced this issue Sep 30, 2019
…use of microsoft/WSL#4424

GitOrigin-RevId: 7ad25362a788ad57ffb8d4063de35dfb0273e882
@throwable-one
Copy link

throwable-one commented Oct 3, 2019

Reproduced with plain C + Win32API.

/**
 * This app shows how WSL2 process handler never releases.
 * 
 * To so so, you must redirect STDIN, STDOUT and STDERR and wait for process handle.
 * For this example we run "env" command.
 * 
 * For WSL1, everything works. For WSL2 it gets frozen forever.
 * 
 * In this example I have:
 * * Ubuntu1804 as WSL2
 * * Debian as WSL1
 **/
#include <Windows.h>
#include <stdio.h>

#define PIPE_SIZE (4096 * 4096 ) //Big enough to keep env output

#define WSL_COMMAND L"Debian run env"
#define WSL2_COMMAND L"Ubuntu1804 run env"

int main()
{
	HANDLE hChildStd_IN_Rd = NULL;
	HANDLE hChildStd_IN_Wr = NULL;
	HANDLE hChildStd_OUT_Rd = NULL;
	HANDLE hChildStd_ERR_Rd = NULL;
	HANDLE hChildStd_OUT_Wr = NULL;
	HANDLE hChildStd_ERR_Wr = NULL;

	SECURITY_ATTRIBUTES saAttr;

	// Create pipes to redirect streams

	saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
	saAttr.bInheritHandle = TRUE;
	saAttr.lpSecurityDescriptor = NULL;


	if (!CreatePipe(&hChildStd_OUT_Rd, &hChildStd_OUT_Wr, &saAttr, PIPE_SIZE))
		return -1;
	if (! CreatePipe(&hChildStd_ERR_Rd, &hChildStd_ERR_Wr, &saAttr, PIPE_SIZE))
		return -1;
	if (! CreatePipe(&hChildStd_IN_Rd, &hChildStd_IN_Wr, &saAttr, PIPE_SIZE))
		return -1;

	// Change WSL2 to WSL here, and see how it works.
	WCHAR szCmdline[] = WSL2_COMMAND;
	PROCESS_INFORMATION piProcInfo = {0};
	STARTUPINFO siStartInfo = {0};
	siStartInfo.cb = sizeof(STARTUPINFO);
	siStartInfo.hStdError = hChildStd_ERR_Wr;
	siStartInfo.hStdOutput = hChildStd_OUT_Wr;
	siStartInfo.hStdInput = hChildStd_IN_Rd; //Important!
	siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

	// Create the child process. 

	CreateProcessW(NULL,
	               szCmdline,
	               NULL,
	               NULL,
	               TRUE,
	               0,
	               NULL,
	               NULL,
	               &siStartInfo,
	               &piProcInfo);

	WaitForSingleObject(piProcInfo.hProcess, MAXINT); //Frozen forever for WSL2
	DWORD exitCode;
	GetExitCodeProcess(piProcInfo.hProcess, &exitCode);
	printf("%ld", exitCode); // Prints 0 for WSL1
}

@throwable-one
Copy link

For those, who prefer C#/.NET

using System;
using System.Diagnostics;

namespace dotnent_test
{
    internal class MyClass
    {
        public static void Main(string[] args)
        {
            var process = new Process
            {
                StartInfo = new ProcessStartInfo
                {
                    FileName = "ubuntu1804", // Make sure you have WSL2 here!!
                    Arguments = "run \"env\"",
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    RedirectStandardInput = true, // Important!
                    CreateNoWindow = true
                }
            };

            process.Start();

            while (!process.StandardOutput.EndOfStream)
            {
                var line = process.StandardOutput.ReadLine();
                Console.WriteLine(line);
            }

            while (!process.StandardError.EndOfStream)
            {
                var line = process.StandardError.ReadLine();
                Console.WriteLine(line);
            }

            process.WaitForExit(); //NEVER ends for WSL2
        }
    }
}

@benhillis
Copy link
Member

benhillis commented Oct 7, 2019

Thanks for posting, I have identified an issue when stdin is a pipe handle that does not get closed.

@vromaniks vromaniks changed the title Starting from java causes WSL wait for input WSL2 process never releases and gets frozen forever when stdin is redirected Oct 24, 2019
@throwable-one
Copy link

@benhillis it seems this issue is fixed in latest EAP. is it true?

@benhillis
Copy link
Member

Yes it should be.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants