Hi Scott,

Scott Cheloha wrote on Tue, Jul 20, 2021 at 05:20:16PM -0500:

> The nanosleep.2 page could use some cleanup.  Here's a bunch of fixes,
> rewrites, etc.
> 
> I've included my notes on the changes below.  I have some (mostly
> stylistic) questions in there, too.

Thanks for explaining what you are doing.  I'm snipping what i agree
with as well as what i have no opinion on and trust you on, but that
doesn't mean that mentioning it was useless.

> Should we reference sigaction(2) here alongside SA_RESTART?  e.g.:
> 
>       ... even if SA_RESTART is set for the interrupting signal
>       (see sigaction(2) for further details).

It isn't absolutely required.  On the one hand, people can use
"man -k any=SA_RESTART" to find out what it is.  On the other hand,
people can maybe be expected to know that sigaction(2) is the system
call to configure signal handling, and even if they ognore it, they
will easily find out with "man signal".

Then again, i can see how saying more precisely what SA_RESTART is
might be helpful especially for novice users, if it can be done
without being too much of a distraction.

In general, i dislike both parenthetic remarks and "see ... for details"
unless a more natural wording cannot be found.

Maybe something like:

  ... even if SA_RESTART was set with sigaction(2) for the
  interrupting signal.

Your call, i would say.

> I'm unsure about the correct voice here.  Should nanosleep actively
> return values?  Or should values "be returned"?

Both are acceptable.  In general, and not just in manual pages,
prefer the active voice when both work equally well.

> I'm unclear on whether I need the indent here in the .Bd block.  I
> think it looks better than unindented code, but maybe I'm flouting
> some practice we have.

Using tabs is permitted inside .Bd -literal and .Bd -unfilled.
They should definitely be used for indenting the bodies of for,
while, and if.

Whether you indent the whole thing depends on what looks better.
When the code is relatively narrow, indenting the whole example
is often a good idea.  When the code contains very long lines,
not indenting the whole example may be better.

If you choose to indent, i have no strong prefernce whether you
do it with tabs or with .Bd -literal -offset indent; maybe i very
slightly prefer the latter, because the global indentation is
a formatting choice rather than part of the displayed code.
But i really wouldn't complain about either.

> ERRORS
> - Simplify the opening sentence.  Yeesh, what a mouthful.

Indeed, ERRORS should not repeat the content of RETURN VALUES.

> Unsure if a second EFAULT case for remainder is a good thing here.
> Strictly speaking, NULL is not a valid part of the process address
> space.  Maybe that's too pedantic?

I don't think you can be too pedantic about where NULL is allowed
and where it isn't.  That's a notorious source of bugs, so precision
about NULL is usually a good thing.

> Also, do we have a more standard "boilerplate" sentence for EFAULT?

Judging from "man -l /usr/share/man/man2/*.2", the most common
wording is:

  [EFAULT]  foo points outside the process's allocated address space.

But i don't really i like that.  The word "allocated" makes me wonder
because it sounds too much like malloc(3) for my taste.
Usually, pointers to automatic and to static objects are acceptable,
too, and are those "allocated"?  Some might say they are not.
Besides, "process's" looks awkward.

These occur, too:

  [EFAULT]  The foo parameter is not in a valid part of the user
            address space.

  [EFAULT]  foo references invalid memory.

  [EFAULT]  The name parameter specifies an area outside the
            process address space.

  [EFAULT]  foo points to an illegal address.

  [EFAULT]  The userspace address uaddr is invalid.

  [EFAULT]  Part of buf points outside the process's allocated
            address space.

  [EFAULT]  The buf parameter points to an invalid address.

  [EFAULT]  The argument foo specifies an invalid address.

  [EFAULT]  The foo parameter specified a bad address.

  [EFAULT]  The foo parameter points to memory not in
            a valid part of the process address space.

  [EFAULT]  The address specified for foo is invalid.

  [EFAULT]  An argument address referenced invalid memory.

  In errno(2):
  14 EFAULT Bad address. The system detected an invalid address in
            attempting to use an argument of a call.

  [EFAULT]  There was an error reading or writing the kevent
            structure.

Quite a mess, i would say.

I think trying to explain over and over again what an invalid address
is, in each and every manual page, is neither reasonable nor feasible -
i'm not convinced the rules are really simple given how many types
of valid memory there are: static, stack, heap, ...

So i'd probably settle for a concise, simple wording, and i think
i like this one best:

  [EFAULT]  foo points to an invalid address.

What is valid can easily depend on the function being called and
even on other function arguments; the same address can easily be
valid for reading a handful of bytes but invalid for reading
kilobytes, let alone megabytes of data.

Please don't say "illegal" (it's not a crime) nor "bad".

> SEE ALSO
> - Cross reference sigaction(2).  We mention SA_RESTART in the
>   description.

Yes, that seems helpful.

Users of nanosleep(2) *should* consider signal handling, and sigaction(2)
is the canonical place to learn about that.

> - I'm pretty sure nanosleep() is a POSIX invention, so note that it
>   really first appeared in POSIX.1b (realtime extensions),

That would be quite unusual; as a rule, the Austin group categorically
refuses to standardize anything that doesn't at least have two
implementations in relevant real-world operating systems.
Consequently, we almost never have .St below HISTORY.

Then again, i believe you know this stuff, so i trust you if you
say POSIX invented this and did not merely adopt it from some
commercial UNIX system.  And if that is true, if standardization
really predates implementation in this case, then using .St
below HISTORY is indeed the right thing to do.

>   If someone can find nanosleep() in the wild before 1993 we can fix
>   the attribution.

Fair enough.  You can also ask millert@ if you have doubts, he often
remembers details about POSIX that many others don't.

See below in-line for some nits.  Getting rid of \\ is mandatory;
the rest are only suggestions.

Yours,
  Ingo


> Index: nanosleep.2
> ===================================================================
> RCS file: /cvs/src/lib/libc/sys/nanosleep.2,v
> retrieving revision 1.16
> diff -u -p -r1.16 nanosleep.2
> --- nanosleep.2       31 Dec 2018 18:54:00 -0000      1.16
> +++ nanosleep.2       20 Jul 2021 22:14:50 -0000
> @@ -41,53 +41,114 @@
>  .Ft int
>  .Fn nanosleep "const struct timespec *timeout" "struct timespec *remainder"
>  .Sh DESCRIPTION
> +The
>  .Fn nanosleep
> -suspends execution of the calling process for the duration specified by
> +function suspends execution of the calling thread for at least the
> +duration specified by
>  .Fa timeout .
> -An unmasked signal will cause it to terminate the sleep early,
> -regardless of the
> +Delivery of an unmasked signal to the calling thread will terminate the

s/will terminate/terminates/ is enough; "will" is needed rarely
in manual pages.

> +sleep early,
> +even if
>  .Dv SA_RESTART
> -value on the interrupting signal.
> +is set for the interrupting signal.
>  .Sh RETURN VALUES
> -If the
> +If
>  .Fn nanosleep
> -function returns because the requested duration has elapsed, the value
> -returned will be zero.
> +sleeps the full
> +.Fa timeout
> +without interruption it returns 0.
> +If
> +.Fa remainder
> +is
> +.Pf non- Dv NULL

Prefixing non- like this is acceptable, but not particularly nice.
I would like this better:

  Unless
  .Fa remainder
  is
  .Dv NULL ,

> +it is set to zero.
>  .Pp
> -If the
> +If
>  .Fn nanosleep
> -function returns due to the delivery of a signal, the value returned
> -will be \-1, and the global variable
> +is interrupted by a signal it returns -1 and the global variable
>  .Va errno
> -will be set to indicate the interruption.
> +is set to
> +.Dv EINTR .
>  If
>  .Fa remainder
> -is non-null, the timespec structure it references is updated to contain the
> -unslept amount (the requested duration minus the duration actually slept).
> -.Sh ERRORS
> -If any of the following conditions occur, the
> +is
> +.Pf non- Dv NULL

Unless...  as above.

> +it is set to the unslept portion of the
> +.Fa timeout .
> +.Pp
> +Otherwise,
>  .Fn nanosleep
> -function shall return \-1 and set
> +returns -1 and the global variable
>  .Va errno
> -to the corresponding value.
> +is set to indicate the error.
> +.Sh EXAMPLES
> +Count down from ten:
> +.Bd -literal
> +     struct timespec one_second;
> +     int i;
> +
> +     one_second.tv_sec = 1;
> +     one_second.tv_nsec = 0;
> +
> +     for (i = 10; i >= 0; i--) {
> +             if (nanosleep(&one_second, NULL) == -1)
> +                     err(1, "nanosleep");
> +             printf("%4d\\n", i);

This must be \en, not \\n.

Using \\ outside macro definitions (i.e., outside the bodies of
roff(7) .de and similar requests) is almost always wrong even in
low-level roff code, and \\ must never be used in manual pages.

> +     }
> +.Ed
> +.Pp
> +Check a condition once every one hundred milliseconds:
> +.Bd -literal
> +     struct timespec hundred_ms;
> +     extern int done;
> +
> +     hundred_ms.tv_sec = 0;
> +     hundred_ms.tv_nsec = 100 * 1000 * 1000;
> +
> +     while (!done) {
> +             if (nanosleep(&hundred_ms, NULL) == -1)
> +                     err(1, "nanosleep");
> +     }
> +.Ed
> +.Pp
> +Sleep for at least sixty seconds and print the time remaining whenever
> +a signal interrupts the sleep:
> +.Bd -literal
> +     struct timespec timeout;
> +
> +     timeout.tv_sec = 60;
> +     timeout.tv_nsec = 0;
> +
> +     while (nanosleep(&timeout, &timeout) == -1) {
> +             if (errno != EINTR)
> +                     err(1, "nanosleep");
> +             printf("time left: %lld.%09ld seconds\\n",

Again, \en.

> +                 (long long)timeout.tv_sec, timeout.tv_nsec);
> +     }
> +.Ed
> +.Sh ERRORS
> +.Fn nanosleep
> +will fail if:
>  .Bl -tag -width Er
>  .It Bq Er EINTR
> -.Fn nanosleep
> -was interrupted by the delivery of a signal.
> +The call was interrupted by the delivery of a signal.
>  .It Bq Er EINVAL
>  .Fa timeout
>  specified a nanosecond value less than zero or greater than or equal to
> -1000 million,
> +one billion,
>  or a second value less than zero.

Regarding the following, why not be very explicit, considering that
the rules differ for both cases?

>  .It Bq Er EFAULT
> -Either
>  .Fa timeout
> -or
> +pointed to memory that is not a valid part of the process address space.

  .Fa timeout
  was
  .Dv NULL
  or pointed to an invalid address. 

> +.It Bq Er EFAULT
>  .Fa remainder
> -points to memory that is not a valid part of the process address space.
> +was
> +.Pf non- Dv NULL
> +and pointed to memory that is not a valid part of the process address space.

  .Fa remainder
  was not
  .Dv NULL
  and pointed to an invalid address.

>  .El
>  .Sh SEE ALSO
>  .Xr sleep 1 ,
> +.Xr sigaction 2 ,
>  .Xr sleep 3
>  .Sh STANDARDS
>  The
> @@ -97,17 +158,23 @@ function conforms to
>  .Sh HISTORY
>  The predecessor of this system call,
>  .Fn sleep ,
> -appeared in
> -.At v3 ,
> -but was removed when
> +first appeared in
> +.At v3 .
> +It was removed in
> +.At v7
> +and replaced with a C library implementation based on
>  .Xr alarm 3
> -was introduced into
> -.At v7 .
> +and
> +.Xr signal 3 .
> +.Pp
>  The
>  .Fn nanosleep
> -system call has been available since
> +function first appeared in
> +.St -p1003.1b-93 .
> +.Pp
> +This implementation of
> +.Fn nanosleep
> +first appeared in
>  .Nx 1.3
>  and was ported to
> -.Ox 2.1
> -and
> -.Fx 3.0 .
> +.Ox 2.1 .

Reply via email to