-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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
fmt: Sometimes fmt.Scan failed to read integer on Windows #8825
Comments
I can recreate it on Windows. Makes it easy if I change go command like that: diff -r 9472d0e26c23 src/cmd/go/run.go --- a/src/cmd/go/run.go Sun Sep 28 08:27:05 2014 -0700 +++ b/src/cmd/go/run.go Mon Sep 29 12:48:41 2014 +1000 @@ -10,6 +10,7 @@ "os/exec" "runtime" "strings" + "time" ) var execCmd []string // -exec flag, for run and test @@ -63,6 +64,7 @@ } func runRun(cmd *Command, args []string) { + time.Sleep(3*time.Second) raceInit() var b builder b.init() and then type "1" before the time.Sleep above completes, followed by "enter key" after long pause when the running program already started. Alex |
c.emil.hessman, See my instructions in #2. Alternatively, you can do this: C:\go\root\src\os\exec>hg diff diff -r 3e5d0270f09e src/os/exec/exec_test.go --- a/src/os/exec/exec_test.go Thu Oct 16 15:08:49 2014 -0700 +++ b/src/os/exec/exec_test.go Fri Oct 17 16:58:01 2014 +1100 @@ -720,8 +720,41 @@ } fmt.Print(p) os.Exit(0) + case "alex": + alex() default: fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd) os.Exit(2) } } + +func alex() { + defer os.Exit(0) + + fmt.Println("now press Enter key ...") + x := make([]byte, 10) + n, err := os.Stdin.Read(x) + x = x[:n] + fmt.Printf("n=%d err=%v\n", n, err) + fmt.Printf("x=%v\n", x) + fmt.Printf("x=[") + for _, c := range x { + fmt.Printf("%c ", c) + } + fmt.Println("]") +} + +func TestAlex(t *testing.T) { + fmt.Println(`type "abc" quickly (you have 5 seconds) ...`) + + time.Sleep(5 * time.Second) + + p := helperCommand(t, "alex") + p.Stdin = os.Stdin + p.Stdout = os.Stdout + p.Stderr = os.Stderr + err := p.Run() + if err != nil { + t.Error(err) + } +} C:\go\root\src\os\exec>go test -c && exec.test.exe -test.v -test.run=Alex === RUN TestAlex type "abc" quickly (you have 5 seconds) ... now press Enter key ... abc n=5 err=<nil> x=[101 120 101 99 46] x=[e x e c . ] --- PASS: TestAlex (8.56s) PASS You need to run test binary directly (run exec.test.exe, not "go test ..."). When asked to type "abc" on keyboard, type it, then wait for "now press Enter key" prompt before pressing Enter. As you can see from my output, our test reads "exec.", instead of "abc" from keyboard. I don't know what the problem is. Yet. Alex |
I tried to write an OS-independent test for this but failed. Anyone see what I did wrong? It seems to me that if the problem is in \r handling, it should be possible to reproduce with an appropriate input. Seems too late for Go 1.5 in any event.
|
Maybe this is a dup of #8944, which I can reproduce portably. |
I still don't know what the problem is. Maybe it is something to do with the fact that stdin handling whole lines only. My test starts typing the line before child starts and ends after child is started. Inserting pipe between parent and child's stdin fixes the problem:
But that approach will not work in cmd/go - I think we want "go run ..." child reading from console, not from pipe. Alex |
I don't believe this is related to #8944. The issue in #8944 is specifically about how newlines work in Scanf. This is Scan, not Scanf, and newlines work differently in the two. It seems to be confined to Windows, which argues against it being \r in particular. If it were, we could reproduce it with \r on other systems, but we cannot. My guess is that this has to do with a difference in how Windows does console input but I doubt that is a helpful remark. |
My sentiment exactly @robpike Alex |
I have built little program https://play.golang.org/p/W4f9GPsoID that demonstrates this. If I run it (and press "abc", when prompted, followed by Enter), I see this:
But if I change program a little:
and run in the same way, I see
I also built similar Pascal program https://play.golang.org/p/ipEm9iGnnE, and it behaves in the same way
It even have same bug if I change buffer size from 40 to 41
So both programs are broken in exactly the same way. I am not even sure, if it is possible to enter characters into console buffer before program is started and expect all characters to be read by the program properly. Alex |
this issue still exists in go 1.9.3 |
os.Stdin of child process should not be "console". But Go handle it as "console". (see newFile in os/file_windows.go) So child use ReadConsole instead of ReadFile. ReadConsole read 2 bytes for each input characters. Then, it result should contains 0x00. |
Yes. I do not know how to fix it, and no one else tried to fix it.
Sorry @mattn but I don't understand you. Please try again. Alex |
This is part of string readStdin returun "issue". This seems reading invalid memory. |
Yes, there is a problem here. And I cannot explain it. Alex |
@alexbrainman This seems not be caused by exec.Command or pipe. Below is more smaller code to reproduce. package main
import (
"fmt"
"os"
"syscall"
"time"
)
func readStdin(b []byte) (int, error) {
h, err := syscall.GetStdHandle(syscall.STD_INPUT_HANDLE)
if err != nil {
return 0, err
}
var done uint32
err = syscall.ReadFile(syscall.Handle(h), b, &done, nil)
if err != nil {
return 0, err
}
return int(done), nil
}
const testdata = "abc"
func test() int {
fmt.Println("now press Enter key ...")
// TODO: change x size to 41 makes test pass. Why?
x := make([]byte, 40)
n, err := readStdin(x)
if err != nil {
fmt.Fprintf(os.Stderr, "readStdin fail %v\n", err)
return -1
}
x = x[:n]
have := string(x)
want := testdata + "\r\n"
if want != have {
fmt.Fprintf(os.Stderr, "have %q, but want %q\n", have, want)
return -1
}
return 0
}
func main() {
fmt.Printf("type %q quickly (you have 5 seconds) ...\n", testdata)
time.Sleep(5 * time.Second)
os.Exit(test())
} And C code can reproduce this. #include <windows.h>
#include <stdio.h>
#define testdata "abc"
int
main(int argc, char* argv[]) {
char b[40];
DWORD done = 0;
int i;
printf("type %s quickly (you have 5 seconds) ...\n", testdata);
Sleep(5000);
puts("now press Enter key ...");
HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
if (!ReadFile(h, b, sizeof(b), &done, NULL)) {
fprintf(stderr, "read error %d\n", GetLastError());
return -1;
}
b[done] = 0;
if (strcmp(testdata, b) != 0) {
for (i = 0; i < done; i++) {
printf("%02x ", b[i]);
}
puts("");
}
return 0;
}
So, I'm thinking this is not a bug of Go, and this issue is closable. |
Closing as this is a Windows problem unrelated to Go. |
I am thinking the same.
I suspect so. But before we give up on this, I will ask @jstarks - can you investigate what I described here #8825 (comment) ? Where is the problem? Thank you. Alex |
by bupjae:
Attachments:
The text was updated successfully, but these errors were encountered: