[PATCH weston 2/8] shared: Add helpers to convert between protocol data and timespec

Alexandros Frantzis alexandros.frantzis at collabora.com
Tue Dec 12 12:43:11 UTC 2017


On Tue, Dec 12, 2017 at 12:09:59PM +0200, Pekka Paalanen wrote:
> On Mon,  4 Dec 2017 15:34:02 +0200
> Alexandros Frantzis <alexandros.frantzis at collabora.com> wrote:
> 
> > Add helpers to safely convert between struct timespec values and
> > tv_sec_hi, tv_sec_lo, tv_nsec triplets used for sending high-resolution
> > timestamp data over the wayland protocol. Replace existing conversion
> > code with the helper functions.
> > 
> > Signed-off-by: Alexandros Frantzis <alexandros.frantzis at collabora.com>
> > ---
> >  clients/presentation-shm.c |  9 +------
> >  libweston/compositor.c     |  9 ++++---
> >  shared/timespec-util.h     | 39 +++++++++++++++++++++++++++
> >  tests/presentation-test.c  |  9 +------
> >  tests/timespec-test.c      | 66 ++++++++++++++++++++++++++++++++++++++++++++++
> >  5 files changed, 112 insertions(+), 20 deletions(-)
> 
> Hi,

Hi,

> 
> I have questions below that will have implications to the protocol
> extension spec as well. I would like to require the time values in
> protocol to be normalized.

Ack.

> > 
> > diff --git a/clients/presentation-shm.c b/clients/presentation-shm.c
> > index c9fb66cc..d6a939e5 100644
> > --- a/clients/presentation-shm.c
> > +++ b/clients/presentation-shm.c
> > @@ -39,6 +39,7 @@
> >  #include <wayland-client.h>
> >  #include "shared/helpers.h"
> >  #include "shared/zalloc.h"
> > +#include "shared/timespec-util.h"
> >  #include "shared/os-compatibility.h"
> >  #include "presentation-time-client-protocol.h"
> >  
> > @@ -383,14 +384,6 @@ timespec_to_ms(const struct timespec *ts)
> >  	return (uint32_t)ts->tv_sec * 1000 + ts->tv_nsec / 1000000;
> >  }
> >  
> > -static void
> > -timespec_from_proto(struct timespec *tm, uint32_t tv_sec_hi,
> > -		    uint32_t tv_sec_lo, uint32_t tv_nsec)
> > -{
> > -	tm->tv_sec = ((uint64_t)tv_sec_hi << 32) + tv_sec_lo;
> > -	tm->tv_nsec = tv_nsec;
> > -}
> > -
> >  static int
> >  timespec_diff_to_usec(const struct timespec *a, const struct timespec *b)
> >  {
> > diff --git a/libweston/compositor.c b/libweston/compositor.c
> > index 7d7a17ed..083664fd 100644
> > --- a/libweston/compositor.c
> > +++ b/libweston/compositor.c
> > @@ -341,7 +341,9 @@ weston_presentation_feedback_present(
> >  {
> >  	struct wl_client *client = wl_resource_get_client(feedback->resource);
> >  	struct wl_resource *o;
> > -	uint64_t secs;
> > +	uint32_t tv_sec_hi;
> > +	uint32_t tv_sec_lo;
> > +	uint32_t tv_nsec;
> 
> A suggestion: how about introducing
> 
> struct timespec_proto {
> 	uint32_t sec_hi;
> 	uint32_t sec_lo;
> 	uint32_t nsec;
> };
> 
> and using that in timespec_to_proto()?
> 
> (Not useful for timespec_from_proto() because the three variables are
> already declared.)

I am not opposed to introducing a struct timespec_proto in general, but
using it in only one of the two functions feels inconsistent.

> >  	wl_resource_for_each(o, &output->resource_list) {
> >  		if (wl_resource_get_client(o) != client)
> > @@ -350,10 +352,9 @@ weston_presentation_feedback_present(
> >  		wp_presentation_feedback_send_sync_output(feedback->resource, o);
> >  	}
> >  
> > -	secs = ts->tv_sec;
> > +	timespec_to_proto(ts, &tv_sec_hi, &tv_sec_lo, &tv_nsec);
> >  	wp_presentation_feedback_send_presented(feedback->resource,
> > -						secs >> 32, secs & 0xffffffff,
> > -						ts->tv_nsec,
> > +						tv_sec_hi, tv_sec_lo, tv_nsec,
> >  						refresh_nsec,
> >  						seq >> 32, seq & 0xffffffff,
> >  						flags | feedback->psf_flags);
> > diff --git a/shared/timespec-util.h b/shared/timespec-util.h
> > index a10edf5b..c734accd 100644
> > --- a/shared/timespec-util.h
> > +++ b/shared/timespec-util.h
> > @@ -175,6 +175,30 @@ timespec_to_usec(const struct timespec *a)
> >  	return (int64_t)a->tv_sec * 1000000 + a->tv_nsec / 1000;
> >  }
> >  
> > +/* Convert timespec to protocol data
> > + *
> > + * \param a timespec
> > + * \param tv_sec_hi[out] the high bytes of the seconds part
> > + * \param tv_sec_lo[out] the low bytes of the seconds part
> > + * \param tv_nsec[out] the nanoseconds part
> > + *
> > + * The timespec is normalized before being converted to protocol data.
> > + */
> > +static inline void
> > +timespec_to_proto(const struct timespec *a, uint32_t *tv_sec_hi,
> > +                  uint32_t *tv_sec_lo, uint32_t *tv_nsec)
> > +{
> > +	struct timespec r;
> > +
> > +	timespec_normalize(&r, a);
> > +
> > +	/* We check the size of tv_sec, so that we shift only if the size
> > +	 * is 64-bits, in order to avoid sign extension on 32-bit systems. */
> > +	*tv_sec_hi = sizeof(r.tv_sec) == 8 ? (int64_t)r.tv_sec >> 32 : 0;
> 
> How about just casting tv_sec to uint64_t before shifting and masking
> it to 32-bit pieces? I think that would be more obvious to read.

We can only do this safely if tv_sec is non-negative (due to
int32_t->uint64_t sign extension when time_t is 32 bits), which
I agree is a sensible pre-condition/assertion to have (see below).

> I think it would be better to assert() that the timespec is already:
> 
> - normalized, because otherwise nsec might overflow in a computation,
>   and

If we make normalized input a pre-condition of timespec_to_proto, we are
essentially moving the burden to the caller, which in most cases should
call timespec_normalize themselves before calling timespec_to_proto. I
am not sure if we should burden the callers with this and introduce the
possibility of runtime errors.

Also, I like the idea that the callers don't need to care about protocol
encoding details, and just let timespec_to_proto do the conversion
properly.

> - non-negative, because the protocol cannot carry negative
>   timestamps.

Makes sense.

> 
> > +	*tv_sec_lo = r.tv_sec;
> > +	*tv_nsec = r.tv_nsec;
> > +}
> > +
> >  /* Convert nanoseconds to timespec
> >   *
> >   * \param a timespec
> > @@ -209,6 +233,21 @@ timespec_from_msec(struct timespec *a, int64_t b)
> >  	timespec_from_nsec(a, b * 1000000);
> >  }
> >  
> > +/* Convert protocol data to timespec
> > + *
> > + * \param a[out] timespec
> > + * \param tv_sec_hi the high bytes of seconds part
> > + * \param tv_sec_lo the low bytes of seconds part
> > + * \param tv_nsec the nanoseconds part
> > + */
> > +static inline void
> > +timespec_from_proto(struct timespec *a, uint32_t tv_sec_hi,
> > +                    uint32_t tv_sec_lo, uint32_t tv_nsec)
> > +{
> > +	a->tv_sec = ((uint64_t)tv_sec_hi << 32) + tv_sec_lo;
> 
> How to handle overflows in tv_sec would be a good question...

A good question indeed. Overflow shouldn't be a problem for well
behaving clients (if we are talking about real-time timestamps then not
until 2038 for 32-bits and not ever for 64-bits). The question is how to
deal with misbehaving and/or malicious clients.

There are two aspects to this. The first is 64-bit to 32-bit overflow on
32-bit systems, which is currently handled by implicitly dropping the
high 32-bits. The second is unsigned to signed overflow, which currently
produces negative timestamps. One way to deal with this is to clamp to
the largest positive value if we ever get a negative value. Another way
is to force the sign bit to 0, essentially making negative values cycle
back to non-negative.

> > +	a->tv_nsec = tv_nsec;
> > +}
> 
> Btw. refactoring code to introduce timespec_from_proto() with no
> functional changes should probably be a patch of its own.

Sure, I will split this in v2.

Thanks,
Alexandros


More information about the wayland-devel mailing list