Marking select(2) as restrict

Bruce Evans brde at optusnet.com.au
Mon Feb 26 10:00:27 UTC 2018


On Sun, 25 Feb 2018, Mark Millard wrote:

> On 2018-Feb-25, at 7:45 PM, Bruce Evans <brde at optusnet.com.au> wrote:
>> . . . restrict is not needed for the input-only
>> args since const suffices.
>
> Here you lost me.
>
> With q and r having both the const and the restrict,
> updates to p's "objects" can not change the
> "object(s)" q and r validly can be used to access.
> That can be important.
>
> Without the "restrict" for q and r (but still
> having the const for each) it is valid for updates
> to p's "objects" to change what q and r then can
> validly access.

Yes, I forgot that all const on a pointer arg does is prevent modification
through that pointer.  Modifications can still occur through other args.
memmove() is a canonical example.  Its source arg is const, by the source
is _always_ modified in the overlapping case that is the reason for
existence of memmove().

memcpy() is another canonical example.  Both of its args are declared with
restrict, except in its man page.  Its source arg is still declared as
const.  Without that, either arg could be modified.  With that and restrict
on the other arg, I think it follows that the source arg cannot be modified.
It is unclear if restrict on the source arg is needed too.

It is now clear(er) that POSIX's restricts for sigaction() are correct
and not having them for nanosleep() is a bug.  nanosleep()'s args are
const struct timespec * and struct timespec *.  Nothing prevents these
being aliased, just like for memmove(), and unlike for memcpy(), the
behaviour is not undefined when aliasing occurs.  Aliasing can only
occur if the pointers are equal, since unlike for mem*() they don't point
to arrays (but the prototype doesn't give this information.)  So
nanosleep(&ts, &ts) must work, and working involves clobbering the input
arg.  The implementation must be careful to not write the output before
reading the input (if the pointers are not equal), and callers using the
same timespec for input and output must not depend on the source being
const.  This is just like for the non-restrict select() except it is
easier to avoid problems (e.g., by copying) with a single small object
than with a potentially large array for the input arg.

My argument applies better to pointer args of different types.  Then const
prevents modifications through some pointer args and the different types
prevent aliasing of the non-const args to the const args.

For select() with restrict, I think the compiler cannot assert that
the args don't overlap since (even without the detailed specification
and Example 3), the compiler cannot know if select() modifies its args.
For all that the compiler knows, select() might be a stub that never
modifies or even reads anything.  I think doing no accesses satifies
the constraints of restrict.  It might be valid for the compiler to
assert that the (values pointed to by) the fdset args don't change for
select(nfd, &fdset, &fdset, &fdset, &tv) (because any write access
through an fdset type would give undefined behaviour on the other fdset
args; tv can still change since it has a different type).  But this is
precisely what is wanted for the original example where we only care
about the return value -- then fdset is indeterminate after the call
so we shouldn't use it; the compiler is unlikely to optimize the non-use
of it and the worst that it can do is add a runtime assertion that the
arg didn't change.

Bruce


More information about the freebsd-standards mailing list