Skip to content

Commit 59ad607

Browse files
authored
Merge pull request #816 from devlights/add-mkfifo-read-nonblocking-example
2 parents 7147878 + 1d24d2d commit 59ad607

File tree

3 files changed

+145
-0
lines changed

3 files changed

+145
-0
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
tmp-fifo
2+
app
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# https://taskfile.dev
2+
3+
version: '3'
4+
5+
vars:
6+
FIFO_FILE: ./tmp-fifo
7+
8+
tasks:
9+
default:
10+
cmds:
11+
- task: build
12+
- task: create-fifo
13+
- task: run
14+
build:
15+
cmds:
16+
- go build -o app .
17+
create-fifo:
18+
cmds:
19+
- rm -f {{.FIFO_FILE}}
20+
- mkfifo {{.FIFO_FILE}} -m0666
21+
run:
22+
cmds:
23+
# ワザと書込みを遅延させて実行
24+
- (sleep 1; echo "helloworld" > {{.FIFO_FILE}}) &
25+
- ./app -fname {{.FIFO_FILE}}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
//go:build linux
2+
3+
package main
4+
5+
import (
6+
"bufio"
7+
"errors"
8+
"flag"
9+
"fmt"
10+
"io"
11+
"log"
12+
"os"
13+
"sync"
14+
"time"
15+
16+
"golang.org/x/sys/unix"
17+
)
18+
19+
var (
20+
fname string
21+
)
22+
23+
func init() {
24+
log.SetFlags(log.Lmicroseconds)
25+
26+
flag.StringVar(&fname, "fname", "", "FIFO file name")
27+
flag.Parse()
28+
}
29+
30+
func main() {
31+
if err := run(); err != nil {
32+
log.Fatal(err)
33+
}
34+
}
35+
36+
func run() error {
37+
//
38+
// 名前付きパイプをノンブロッキングモードで開く
39+
// 明示的なノンブロッキングモードの指定は os.OpenFile() では行えないため
40+
// golang.org/x/sys/unix を利用する
41+
//
42+
var (
43+
fd int
44+
f *os.File
45+
err error
46+
)
47+
48+
log.Println("[Before] unix.Open(unix.O_RDONLY|unix.O_NONBLOCK)")
49+
50+
fd, err = unix.Open(fname, unix.O_RDONLY|unix.O_NONBLOCK, 0666)
51+
if err != nil {
52+
return err
53+
}
54+
55+
f = os.NewFile(uintptr(fd), fname)
56+
if f == nil {
57+
return fmt.Errorf("invalid file descriptor")
58+
}
59+
defer f.Close() // ここで f.Close() しているので、上で unix.Close(fd) は不要
60+
61+
log.Println("[After ] unix.Open(unix.O_RDONLY|unix.O_NONBLOCK)")
62+
63+
//
64+
// データを読み取り
65+
//
66+
type (
67+
data struct {
68+
value string
69+
err error
70+
}
71+
)
72+
var (
73+
reader = bufio.NewReader(f)
74+
lines = make(chan data)
75+
timeout = 1500 * time.Millisecond
76+
done = make(chan struct{})
77+
wg sync.WaitGroup
78+
)
79+
80+
wg.Add(1)
81+
go func() {
82+
defer wg.Done()
83+
84+
// ノンブロッキングモードで処理しているため、データが存在しない場合は即EOFが返ってくる
85+
for {
86+
line, err := reader.ReadString('\n')
87+
if err != nil && errors.Is(err, io.EOF) {
88+
log.Println("読み取れるデータが存在しない")
89+
90+
select {
91+
case <-done:
92+
return
93+
case <-time.After(200 * time.Millisecond):
94+
continue
95+
}
96+
}
97+
98+
lines <- data{line, err}
99+
return
100+
}
101+
}()
102+
103+
select {
104+
case line := <-lines:
105+
if line.err != nil {
106+
return line.err
107+
}
108+
109+
log.Println(line.value)
110+
case <-time.After(timeout):
111+
log.Println("timeout")
112+
}
113+
114+
close(done)
115+
wg.Wait()
116+
117+
return nil
118+
}

0 commit comments

Comments
 (0)