1  /* Test for select timeout.
       2     Copyright (C) 2021-2023 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
      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
      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
      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>