Last active
September 1, 2021 18:23
-
-
Save guysmoilov/b109a45b9f820294dd0069d54116ca25 to your computer and use it in GitHub Desktop.
Simple example of using named pipes in go for IPC
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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