Skip to content

Instantly share code, notes, and snippets.

@guysmoilov
Last active September 1, 2021 18:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guysmoilov/b109a45b9f820294dd0069d54116ca25 to your computer and use it in GitHub Desktop.
Save guysmoilov/b109a45b9f820294dd0069d54116ca25 to your computer and use it in GitHub Desktop.
Simple example of using named pipes in go for IPC
import (
"os"
"os/exec"
"path"
"syscall"
"testing"
"time"
)
func TestFifo(t *testing.T) {
pipeName := path.Join(os.TempDir(), "testpipe-*")
err := syscall.Mkfifo(pipeName, 0660)
if err != nil {
t.Fatal(err)
}
defer os.Remove(pipeName)
cmd := exec.Command("cat", pipeName)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
// We have to open the reading process before attempting to open the pipe for writing (below) -
// Otherwise the open will wait infinitely for a reader to connect to the other end, since we open the pipe
// in blocking mode (os.O_WRONLY)
err = cmd.Start()
if err != nil {
t.Fatal(err)
}
pipe, err := os.OpenFile(pipeName, os.O_WRONLY, 0660|os.ModeNamedPipe)
defer pipe.Close()
if err != nil {
t.Fatal(err)
}
writeToPipe(t, pipe, "1\n")
time.Sleep(time.Second)
writeToPipe(t, pipe, "2\n")
time.Sleep(time.Second)
writeToPipe(t, pipe, "3\n")
time.Sleep(time.Second)
err = pipe.Close()
if err != nil {
t.Fatal(err)
}
err = cmd.Wait()
if err != nil {
t.Fatal(err)
}
}
func TestNonblockingFifo(t *testing.T) {
pipeName := path.Join(os.TempDir(), "testpipe-*")
err := syscall.Mkfifo(pipeName, 0660)
if err != nil {
t.Fatal(err)
}
defer os.Remove(pipeName)
cmdTimeout, cancelCmd := context.WithTimeout(context.Background(), time.Second * 10)
defer cancelCmd()
cmd := exec.CommandContext(cmdTimeout, "cat", pipeName)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
writeTimeout, cancelWrite := context.WithTimeout(context.Background(), time.Second)
defer cancelWrite()
go func() {
var pipe *os.File
var err error
for pipe == nil && err == nil {
select {
case <-writeTimeout.Done():
t.Fatal("timeout")
default:
pipe, err = os.OpenFile(pipeName, os.O_WRONLY|syscall.O_NONBLOCK, 0660|os.ModeNamedPipe)
if asPathErr, ok := err.(*os.PathError); ok {
if asPathErr.Unwrap() == syscall.ENXIO {
fmt.Println("pipe not open yet")
err = nil
}
}
}
}
if err != nil {
cancelCmd()
t.Fatal(err)
}
fmt.Println("Pipe opened!")
defer pipe.Close()
writeToPipe(t, pipe, "1\n")
time.Sleep(time.Second)
writeToPipe(t, pipe, "2\n")
time.Sleep(time.Second)
writeToPipe(t, pipe, "3\n")
time.Sleep(time.Second)
err = pipe.Close()
if err != nil {
cancelCmd()
t.Fatal(err)
}
}()
err = cmd.Start()
if err != nil {
t.Fatal(err)
}
err = cmd.Wait()
if err != nil {
t.Fatal(err)
}
}
func writeToPipe(t *testing.T, file *os.File, arg string) {
_, err := file.WriteString(arg)
if err != nil {
t.Fatal(err)
}
return
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment