Skip to content

Commit d8c1151

Browse files
authored
Merge pull request #818 from devlights/add-mkfifo-write-nonblocking-example
2 parents 708bec0 + 7c224cc commit d8c1151

File tree

5 files changed

+159
-2
lines changed

5 files changed

+159
-2
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: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
- ./app -fname {{.FIFO_FILE}} &
25+
- sleep 1
26+
- cat {{.FIFO_FILE}}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
//go:build linux
2+
3+
package main
4+
5+
import (
6+
"bufio"
7+
"errors"
8+
"flag"
9+
"fmt"
10+
"log"
11+
"os"
12+
"sync"
13+
"syscall"
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_WRONLY|unix.O_NONBLOCK)")
49+
50+
// 名前付きパイプを書込みでノンブロッキングモードで開く場合に O_WRONLY を指定すると
51+
// 読み込み側が開かれていない場合 `ENXIO(no such device or address)` が発生する。
52+
//
53+
// 逆に読み込みでノンブロッキングモードで開く場合はここではエラーとならず
54+
// 実際に読み込む際に書込みが行われていない場合に即EOFが返ることになる。
55+
// ノンブロッキングリードする処理は ../read-nonbloking/ を参照のこと。
56+
//
57+
// 読み込みと書込みでエラーが発生する箇所が異なる点に注意。
58+
//
59+
// REFERENCES
60+
// - https://qiita.com/seriru13/items/39ed2431dfd959ad512e
61+
for {
62+
fd, err = unix.Open(fname, unix.O_WRONLY|unix.O_NONBLOCK, 0666)
63+
if err != nil {
64+
var sysErr syscall.Errno
65+
if errors.As(err, &sysErr) && sysErr == unix.ENXIO {
66+
log.Printf("[ENXIO] %s", sysErr)
67+
<-time.After(200 * time.Millisecond)
68+
continue
69+
}
70+
71+
return err
72+
}
73+
74+
break
75+
}
76+
77+
f = os.NewFile(uintptr(fd), fname)
78+
if f == nil {
79+
return fmt.Errorf("invalid file descriptor")
80+
}
81+
defer f.Close() // ここで f.Close() しているので、上で unix.Close(fd) は不要
82+
83+
log.Println("[After ] unix.Open(unix.O_WRONLY|unix.O_NONBLOCK)")
84+
85+
//
86+
// データを書き込み
87+
//
88+
type (
89+
data struct {
90+
numWrites int
91+
err error
92+
}
93+
)
94+
var (
95+
writer = bufio.NewWriter(f)
96+
results = make(chan data)
97+
timeout = 1500 * time.Millisecond
98+
done = make(chan struct{})
99+
wg sync.WaitGroup
100+
)
101+
102+
wg.Add(1)
103+
go func() {
104+
defer wg.Done()
105+
106+
n, err := writer.WriteString("helloworld\n")
107+
if err == nil {
108+
err = writer.Flush()
109+
}
110+
111+
results <- data{n, err}
112+
}()
113+
114+
select {
115+
case r := <-results:
116+
if r.err != nil {
117+
return r.err
118+
}
119+
120+
log.Printf("Write %d byte(s)", r.numWrites)
121+
case <-time.After(timeout):
122+
log.Println("timeout")
123+
}
124+
125+
close(done)
126+
wg.Wait()
127+
128+
return nil
129+
}

examples/singleapp/mkfifo/write/Taskfile.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ tasks:
2121
run:
2222
cmds:
2323
- ./app -fname {{.FIFO_FILE}} &
24-
- (sleep 2; (date '+%H:%M:%S.%6N'; cat {{.FIFO_FILE}}) | xargs)
24+
- (sleep 2; cat {{.FIFO_FILE}})

examples/singleapp/mkfifo/write/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func run() error {
3131
//
3232
// 名前付きパイプを開く
3333
// os.OpenFile() にて、モード指定で os.ModeNamedPipe を指定する
34-
// os.O_RDOONLYで開くと書込み専用となるが、この場合読み込みが発生するまで
34+
// os.O_WRONLYで開くと書込み専用となるが、この場合読み込みが発生するまで
3535
// ブロックされる。これはUNIXの名前付きパイプの挙動に従った動作である。
3636
//
3737
var (

0 commit comments

Comments
 (0)