1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build zos && s390x
6// +build zos,s390x
7
8package unix_test
9
10// Modified from Linux tests for epoll.
11
12import (
13	"os"
14	"testing"
15
16	"golang.org/x/sys/unix"
17)
18
19func TestEpollIn(t *testing.T) {
20	efd, err := unix.EpollCreate1(0) // no CLOEXEC equivalent on z/OS
21	if err != nil {
22		t.Fatalf("EpollCreate1: %v", err)
23	}
24	// no need to defer a close on efd, as it's not a real file descriptor on zos
25
26	r, w, err := os.Pipe()
27	if err != nil {
28		t.Fatal(err)
29	}
30	defer r.Close()
31	defer w.Close()
32
33	fd := int(r.Fd())
34	ev := unix.EpollEvent{Events: unix.EPOLLIN, Fd: int32(fd)}
35
36	err = unix.EpollCtl(efd, unix.EPOLL_CTL_ADD, fd, &ev)
37	if err != nil {
38		t.Fatalf("EpollCtl: %v", err)
39	}
40
41	if _, err := w.Write([]byte("HELLO GOPHER")); err != nil {
42		t.Fatal(err)
43	}
44
45	events := make([]unix.EpollEvent, 128)
46	n, err := unix.EpollWait(efd, events, 1)
47	if err != nil {
48		t.Fatalf("EpollWait: %v", err)
49	}
50
51	if n != 1 {
52		t.Errorf("EpollWait: wrong number of events: got %v, expected 1", n)
53	}
54
55	got := int(events[0].Fd)
56	if got != fd {
57		t.Errorf("EpollWait: wrong Fd in event: got %v, expected %v", got, fd)
58	}
59
60	if events[0].Events&unix.EPOLLIN == 0 {
61		t.Errorf("Expected EPOLLIN flag to be set, got %b", events[0].Events)
62	}
63}
64
65func TestEpollHup(t *testing.T) {
66	efd, err := unix.EpollCreate1(0)
67	if err != nil {
68		t.Fatalf("EpollCreate1: %v", err)
69	}
70	// no need to defer a close on efd, as it's not a real file descriptor on zos
71
72	r, w, err := os.Pipe()
73	if err != nil {
74		t.Fatal(err)
75	}
76	defer r.Close()
77
78	fd := int(r.Fd())
79	// EPOLLHUP should be reported even if not explicitly requested
80	ev := unix.EpollEvent{Events: unix.EPOLLIN, Fd: int32(fd)}
81
82	err = unix.EpollCtl(efd, unix.EPOLL_CTL_ADD, fd, &ev)
83	if err != nil {
84		t.Fatalf("EpollCtl: %v", err)
85	}
86
87	events := make([]unix.EpollEvent, 128)
88	n, err := unix.EpollWait(efd, events, 1)
89	if err != nil {
90		t.Fatalf("EpollWait: %v", err)
91	}
92
93	if events[0].Events&unix.EPOLLHUP != 0 {
94		t.Errorf("EPOLLHUP flag aset without hangup event; got n=%d, flags=%b", n, events[0].Events)
95	}
96
97	w.Close()
98
99	events = make([]unix.EpollEvent, 128)
100	n, err = unix.EpollWait(efd, events, 1)
101	if err != nil {
102		t.Fatalf("EpollWait: %v", err)
103	}
104
105	if n < 1 || events[0].Events&unix.EPOLLHUP == 0 {
106		t.Errorf("Expected EPOLLHUP flag to be set, got n=%d, flags=%b", n, events[0].Events)
107	}
108
109}
110
111func TestEpollInManyFds(t *testing.T) {
112	efd, err := unix.EpollCreate1(4) // Like on Linux, size arg is ignored.
113	if err != nil {
114		t.Fatalf("EpollCreate: %v", err)
115	}
116	// no need to defer a close on efd, as it's not a real file descriptor on zos
117
118	rFds := make([]int, 10)
119	wPipes := make([]*os.File, 10)
120
121	for i := 0; i < 10; i++ {
122		r, w, err := os.Pipe()
123		if err != nil {
124			t.Fatal(err)
125		}
126		defer r.Close()
127		defer w.Close()
128
129		rFds[i] = int(r.Fd())
130		wPipes[i] = w
131	}
132
133	// Monitor all 10 read pipes
134	for _, fd := range rFds {
135		ev := unix.EpollEvent{Events: unix.EPOLLIN, Fd: int32(fd)}
136		err = unix.EpollCtl(efd, unix.EPOLL_CTL_ADD, fd, &ev)
137		if err != nil {
138			t.Fatalf("EpollCtl: %v", err)
139		}
140	}
141
142	// Write to only 5 odd-numbered pipes
143	for i, w := range wPipes {
144		if i%2 == 0 {
145			continue
146		}
147		if _, err := w.Write([]byte("HELLO")); err != nil {
148			t.Fatal(err)
149		}
150	}
151
152	events := make([]unix.EpollEvent, 128)
153	n, err := unix.EpollWait(efd, events, 1)
154	if err != nil {
155		t.Fatalf("EpollWait: %v", err)
156	}
157
158	if n != 5 {
159		t.Errorf("EpollWait: wrong number of events: got %v, expected 5", n)
160	}
161
162	// Check level triggering here
163	if _, err := wPipes[0].Write([]byte("HELLO")); err != nil {
164		t.Fatal(err)
165	}
166
167	// Now, a total of 6 pipes have been written to - level triggered notifis should number 6
168	events = make([]unix.EpollEvent, 128)
169	n, err = unix.EpollWait(efd, events, 1)
170	if err != nil {
171		t.Fatalf("EpollWait: %v", err)
172	}
173
174	if n != 6 {
175		t.Errorf("EpollWait: wrong number of events: got %v, expected 6", n)
176	}
177
178}
179
180func TestMultipleEpolls(t *testing.T) {
181	efd1, err := unix.EpollCreate1(4)
182	if err != nil {
183		t.Fatalf("EpollCreate: %v", err)
184	}
185	// no need to defer a close on efd1, as it's not a real file descriptor on zos
186
187	efd2, err := unix.EpollCreate1(4)
188	if err != nil {
189		t.Fatalf("EpollCreate: %v", err)
190	}
191	// no need to defer a close on efd2, as it's not a real file descriptor on zos
192
193	rFds := make([]int, 10)
194	wPipes := make([]*os.File, 10)
195
196	for i := 0; i < 10; i++ {
197		r, w, err := os.Pipe()
198		if err != nil {
199			t.Fatal(err)
200		}
201		defer r.Close()
202		defer w.Close()
203
204		rFds[i] = int(r.Fd())
205		wPipes[i] = w
206	}
207
208	// Monitor first 7 read pipes on epoll1, last 3 on epoll2
209	for i, fd := range rFds {
210		var efd int
211		if i < 7 {
212			efd = efd1
213		} else {
214			efd = efd2
215		}
216		ev := unix.EpollEvent{Events: unix.EPOLLIN, Fd: int32(fd)}
217		err = unix.EpollCtl(efd, unix.EPOLL_CTL_ADD, fd, &ev)
218		if err != nil {
219			t.Fatalf("EpollCtl: %v", err)
220		}
221	}
222
223	// Write to all 10 pipes
224	for _, w := range wPipes {
225		if _, err := w.Write([]byte("HELLO")); err != nil {
226			t.Fatal(err)
227		}
228	}
229
230	events := make([]unix.EpollEvent, 128)
231	n, err := unix.EpollWait(efd1, events, 1)
232	if err != nil {
233		t.Fatalf("EpollWait: %v", err)
234	}
235	if n != 7 {
236		t.Errorf("EpollWait: wrong number of events on ep1: got %v, expected 7", n)
237	}
238
239	events = make([]unix.EpollEvent, 128)
240	n, err = unix.EpollWait(efd2, events, 1)
241	if err != nil {
242		t.Fatalf("EpollWait: %v", err)
243	}
244	if n != 3 {
245		t.Errorf("EpollWait: wrong number of events on ep2: got %v, expected 3", n)
246	}
247}
248
249func TestEpollErrors(t *testing.T) {
250	efd, err := unix.EpollCreate1(4)
251	if err != nil {
252		t.Fatalf("EpollCreate: %v", err)
253	}
254	// no need to defer a close on efd, as it's not a real file descriptor on zos
255
256	r, w, err := os.Pipe()
257	if err != nil {
258		t.Fatal(err)
259	}
260	defer r.Close()
261	defer w.Close()
262
263	fd := int(r.Fd())
264
265	ev := unix.EpollEvent{Events: unix.EPOLLIN, Fd: int32(fd)}
266	if err = unix.EpollCtl(efd+1, unix.EPOLL_CTL_ADD, fd, &ev); err != unix.EBADF {
267		t.Errorf("EpollCtl: got %v when EpollCtl ADD called with invalid epfd, expected EBADF", err)
268	}
269
270	if err = unix.EpollCtl(efd, unix.EPOLL_CTL_ADD, fd, &ev); err != nil {
271		t.Fatalf("EpollCtl: %v", err)
272	}
273
274	if err = unix.EpollCtl(efd, unix.EPOLL_CTL_MOD, -2, &ev); err != unix.ENOENT {
275		t.Errorf("EpollCtl: got %v when EpollCtl MOD called with invalid fd, expected ENOENT", err)
276	}
277}
278