1/* Test for select timeout.
2 Copyright (C) 2021-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <errno.h>
20#include <intprops.h>
21#include <support/capture_subprocess.h>
22#include <support/check.h>
23#include <support/support.h>
24#include <support/timespec.h>
25#include <support/xunistd.h>
26#include <support/xtime.h>
27#include <support/xsignal.h>
28
29struct child_args
30{
31 int fds[2][2];
32 struct timeval tmo;
33};
34
35static void
36do_test_child (void *clousure)
37{
38 struct child_args *args = (struct child_args *) clousure;
39
40 close (fd: args->fds[0][1]);
41 close (fd: args->fds[1][0]);
42
43 fd_set rfds;
44 FD_ZERO (&rfds);
45 FD_SET (args->fds[0][0], &rfds);
46
47 struct timespec ts = xclock_now (CLOCK_REALTIME);
48 ts = timespec_add (ts, (struct timespec) { args->tmo.tv_sec, 0 });
49
50 int r = select (nfds: args->fds[0][0] + 1, readfds: &rfds, NULL, NULL, timeout: &args->tmo);
51 TEST_COMPARE (r, 0);
52
53 if (support_select_modifies_timeout ())
54 {
55 TEST_COMPARE (args->tmo.tv_sec, 0);
56 TEST_COMPARE (args->tmo.tv_usec, 0);
57 }
58
59 TEST_TIMESPEC_NOW_OR_AFTER (CLOCK_REALTIME, ts);
60
61 xwrite (args->fds[1][1], "foo", 3);
62}
63
64static void
65do_test_child_alarm (void *clousure)
66{
67 struct child_args *args = (struct child_args *) clousure;
68
69 support_create_timer (sec: 0, nsec: 100000000, false, NULL);
70 struct timeval tv = { .tv_sec = args->tmo.tv_sec, .tv_usec = 0 };
71 int r = select (nfds: 0, NULL, NULL, NULL, timeout: &tv);
72 TEST_COMPARE (r, -1);
73 if (args->tmo.tv_sec > INT_MAX)
74 TEST_VERIFY (errno == EINTR || errno == EOVERFLOW);
75 else
76 {
77 TEST_COMPARE (errno, EINTR);
78 if (support_select_modifies_timeout ())
79 TEST_VERIFY (tv.tv_sec < args->tmo.tv_sec);
80 }
81}
82
83static int
84do_test (void)
85{
86 struct child_args args;
87
88 xpipe (args.fds[0]);
89 xpipe (args.fds[1]);
90
91 /* The child select should timeout and write on its pipe end. */
92 args.tmo = (struct timeval) { .tv_sec = 0, .tv_usec = 250000 };
93 {
94 struct support_capture_subprocess result;
95 result = support_capture_subprocess (callback: do_test_child, closure: &args);
96 support_capture_subprocess_check (&result, context: "tst-select-child", status_or_signal: 0,
97 allowed: sc_allow_none);
98 }
99
100 if (support_select_normalizes_timeout ())
101 {
102 /* This is handled as 1 second instead of failing with EINVAL. */
103 args.tmo = (struct timeval) { .tv_sec = 0, .tv_usec = 1000000 };
104 struct support_capture_subprocess result;
105 result = support_capture_subprocess (callback: do_test_child, closure: &args);
106 support_capture_subprocess_check (&result, context: "tst-select-child", status_or_signal: 0,
107 allowed: sc_allow_none);
108 }
109
110 /* Same as before, but simulating polling. */
111 args.tmo = (struct timeval) { .tv_sec = 0, .tv_usec = 0 };
112 {
113 struct support_capture_subprocess result;
114 result = support_capture_subprocess (callback: do_test_child, closure: &args);
115 support_capture_subprocess_check (&result, context: "tst-select-child", status_or_signal: 0,
116 allowed: sc_allow_none);
117 }
118
119 xclose (args.fds[0][0]);
120 xclose (args.fds[1][1]);
121
122 args.tmo = (struct timeval) { .tv_sec = 10, .tv_usec = 0 };
123 {
124 struct support_capture_subprocess result;
125 result = support_capture_subprocess (callback: do_test_child_alarm, closure: &args);
126 support_capture_subprocess_check (&result, context: "tst-select-child", status_or_signal: 0,
127 allowed: sc_allow_none);
128 }
129
130 args.tmo = (struct timeval) { .tv_sec = TYPE_MAXIMUM (time_t),
131 .tv_usec = 0 };
132 {
133 struct support_capture_subprocess result;
134 result = support_capture_subprocess (callback: do_test_child_alarm, closure: &args);
135 support_capture_subprocess_check (&result, context: "tst-select-child", status_or_signal: 0,
136 allowed: sc_allow_none);
137 }
138
139 args.tmo = (struct timeval) { .tv_sec = 0, .tv_usec = 0 };
140 {
141 fd_set rfds;
142 FD_ZERO (&rfds);
143 FD_SET (args.fds[1][0], &rfds);
144
145 int r = select (nfds: args.fds[1][0] + 1, readfds: &rfds, NULL, NULL, timeout: &args.tmo);
146 TEST_COMPARE (r, 1);
147 }
148
149 return 0;
150}
151
152#include <support/test-driver.c>
153

source code of glibc/misc/tst-select.c