Skip to content

Instantly share code, notes, and snippets.

@medasx
Last active April 25, 2022 16:38
Show Gist options
  • Save medasx/2978e8bbf26d4b545738d3c6c0d30a04 to your computer and use it in GitHub Desktop.
Save medasx/2978e8bbf26d4b545738d3c6c0d30a04 to your computer and use it in GitHub Desktop.
Non-blocking stdin - Using `syscall` to duplicate stdin fd and open as non-blocking file.
package main
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"io/fs"
"os"
"syscall"
"time"
)
func setNonblock(f *os.File) error {
c, err := f.SyscallConn()
if err != nil {
return err
}
var err2 error
err = c.Control(func(fd uintptr) {
err2 = syscall.SetNonblock(int(fd), true)
})
if err != nil {
return err
}
return err2
}
func nonBlockingFile(f *os.File) (*os.File, error) {
if err := setNonblock(f); err != nil {
return nil, err
}
fd, err := syscall.Dup(int(f.Fd()))
if err != nil {
return nil, err
}
f2 := os.NewFile(uintptr(fd), f.Name())
return f2, nil
}
func read(ctx context.Context, f *os.File) (*bytes.Buffer, error) {
r, err := nonBlockingFile(f)
if err != nil {
return nil, err
}
go func() {
defer r.Close()
<-ctx.Done()
}()
buff := bytes.NewBuffer([]byte{})
for {
_, err := io.Copy(buff, r)
if err != nil {
if errors.Is(err, fs.ErrClosed) {
break
}
panic(err)
}
}
return buff, nil
}
func main() {
ctx1, _ := context.WithTimeout(context.Background(), time.Second*2)
buf1, err := read(ctx1, os.Stdin)
if err != nil {
panic(err)
}
ctx2, _ := context.WithTimeout(context.Background(), time.Second*2)
buf2, err := read(ctx2, os.Stdin)
if err != nil {
panic(err)
}
fmt.Println(buf1.String())
fmt.Println(buf2.String())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment