Bash: infinite sleep (infinite blocking)

前端 未结 11 574
日久生厌
日久生厌 2020-12-04 08:27

I use startx to start X which will evaluate my .xinitrc. In my .xinitrc I start my window manager using /usr/bin/mywm. No

11条回答
  •  既然无缘
    2020-12-04 08:57

    Let me explain why sleep infinity works though it is not documented. jp48's answer is also useful.

    The most important thing: By specifying inf or infinity (both case-insensitive), you can sleep for the longest time your implementation permits (i.e. the smaller value of HUGE_VAL and TYPE_MAXIMUM(time_t)).

    Now let's dig into the details. The source code of sleep command can be read from coreutils/src/sleep.c. Essentially, the function does this:

    double s; //seconds
    xstrtod (argv[i], &p, &s, cl_strtod); //`p` is not essential (just used for error check).
    xnanosleep (s);
    

    Understanding xstrtod (argv[i], &p, &s, cl_strtod)

    xstrtod()

    According to gnulib/lib/xstrtod.c, the call of xstrtod() converts string argv[i] to a floating point value and stores it to *s, using a converting function cl_strtod().

    cl_strtod()

    As can be seen from coreutils/lib/cl-strtod.c, cl_strtod() converts a string to a floating point value, using strtod().

    strtod()

    According to man 3 strtod, strtod() converts a string to a value of type double. The manpage says

    The expected form of the (initial portion of the) string is ... or (iii) an infinity, or ...

    and an infinity is defined as

    An infinity is either "INF" or "INFINITY", disregarding case.

    Although the document tells

    If the correct value would cause overflow, plus or minus HUGE_VAL (HUGE_VALF, HUGE_VALL) is returned

    , it is not clear how an infinity is treated. So let's see the source code gnulib/lib/strtod.c. What we want to read is

    else if (c_tolower (*s) == 'i'
             && c_tolower (s[1]) == 'n'
             && c_tolower (s[2]) == 'f')
      {
        s += 3;
        if (c_tolower (*s) == 'i'
            && c_tolower (s[1]) == 'n'
            && c_tolower (s[2]) == 'i'
            && c_tolower (s[3]) == 't'
            && c_tolower (s[4]) == 'y')
          s += 5;
        num = HUGE_VAL;
        errno = saved_errno;
      }
    

    Thus, INF and INFINITY (both case-insensitive) are regarded as HUGE_VAL.

    HUGE_VAL family

    Let's use N1570 as the C standard. HUGE_VAL, HUGE_VALF and HUGE_VALL macros are defined in §7.12-3

    The macro
        HUGE_VAL
    expands to a positive double constant expression, not necessarily representable as a float. The macros
        HUGE_VALF
        HUGE_VALL
    are respectively float and long double analogs of HUGE_VAL.

    HUGE_VAL, HUGE_VALF, and HUGE_VALL can be positive infinities in an implementation that supports infinities.

    and in §7.12.1-5

    If a floating result overflows and default rounding is in effect, then the function returns the value of the macro HUGE_VAL, HUGE_VALF, or HUGE_VALL according to the return type

    Understanding xnanosleep (s)

    Now we understand all essence of xstrtod(). From the explanations above, it is crystal-clear that xnanosleep(s) we've seen first actually means xnanosleep(HUGE_VALL).

    xnanosleep()

    According to the source code gnulib/lib/xnanosleep.c, xnanosleep(s) essentially does this:

    struct timespec ts_sleep = dtotimespec (s);
    nanosleep (&ts_sleep, NULL);
    

    dtotimespec()

    This function converts an argument of type double to an object of type struct timespec. Since it is very simple, let me cite the source code gnulib/lib/dtotimespec.c. All of the comments are added by me.

    struct timespec
    dtotimespec (double sec)
    {
      if (! (TYPE_MINIMUM (time_t) < sec)) //underflow case
        return make_timespec (TYPE_MINIMUM (time_t), 0);
      else if (! (sec < 1.0 + TYPE_MAXIMUM (time_t))) //overflow case
        return make_timespec (TYPE_MAXIMUM (time_t), TIMESPEC_HZ - 1);
      else //normal case (looks complex but does nothing technical)
        {
          time_t s = sec;
          double frac = TIMESPEC_HZ * (sec - s);
          long ns = frac;
          ns += ns < frac;
          s += ns / TIMESPEC_HZ;
          ns %= TIMESPEC_HZ;
    
          if (ns < 0)
            {
              s--;
              ns += TIMESPEC_HZ;
            }
    
          return make_timespec (s, ns);
        }
    }
    

    Since time_t is defined as an integral type (see §7.27.1-3), it is natural we assume the maximum value of type time_t is smaller than HUGE_VAL (of type double), which means we enter the overflow case. (Actually this assumption is not needed since, in all cases, the procedure is essentially the same.)

    make_timespec()

    The last wall we have to climb up is make_timespec(). Very fortunately, it is so simple that citing the source code gnulib/lib/timespec.h is enough.

    _GL_TIMESPEC_INLINE struct timespec
    make_timespec (time_t s, long int ns)
    {
      struct timespec r;
      r.tv_sec = s;
      r.tv_nsec = ns;
      return r;
    }
    

提交回复
热议问题