1 /* Test for select timeout.
2    Copyright (C) 2021-2022 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 
29 struct child_args
30 {
31   int fds[2][2];
32   struct timeval tmo;
33 };
34 
35 static void
do_test_child(void * clousure)36 do_test_child (void *clousure)
37 {
38   struct child_args *args = (struct child_args *) clousure;
39 
40   close (args->fds[0][1]);
41   close (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 (args->fds[0][0] + 1, &rfds, NULL, NULL, &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 
64 static void
do_test_child_alarm(void * clousure)65 do_test_child_alarm (void *clousure)
66 {
67   struct child_args *args = (struct child_args *) clousure;
68 
69   support_create_timer (0, 100000000, false, NULL);
70   struct timeval tv = { .tv_sec = args->tmo.tv_sec, .tv_usec = 0 };
71   int r = select (0, NULL, NULL, NULL, &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 
83 static int
do_test(void)84 do_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 (do_test_child, &args);
96     support_capture_subprocess_check (&result, "tst-select-child", 0,
97 				      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 (do_test_child, &args);
106       support_capture_subprocess_check (&result, "tst-select-child", 0,
107 					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 (do_test_child, &args);
115     support_capture_subprocess_check (&result, "tst-select-child", 0,
116 				      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 (do_test_child_alarm, &args);
126     support_capture_subprocess_check (&result, "tst-select-child", 0,
127 				      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 (do_test_child_alarm, &args);
135     support_capture_subprocess_check (&result, "tst-select-child", 0,
136 				      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 (args.fds[1][0] + 1, &rfds, NULL, NULL, &args.tmo);
146     TEST_COMPARE (r, 1);
147   }
148 
149   return 0;
150 }
151 
152 #include <support/test-driver.c>
153