redjack/libcork — pull request #79 by dcreager (2014-01-30 19:21:14+00:00)
Title: Impossible returns from cork_timestamp_usec

In dubugging some clock functions in flomill, I separate the timestamp into sec and usec components using cork_timestamp_sec and cork_timestamp_usec. In one case, I print these using
```
fprintf(stderr, "Punct at %"PRIu32".%06"PRIu64"\n",
cork_timestamp_sec(self->last_heartbeat),
cork_timestamp_usec(self->last_heartbeat));
```
and I get
```
Punct at 1388183175.1000000
```
when the result should be
```
Punct at 1388183176.000000
```
The value is sent as a tock punctuation and using the to_utc method with a format of
```
"%Y/%m/%d:%H:%M:%S.%6f"
```
on the timestamp extracted from the punctuation gives an output of
```
Tock with timestamp: 2013/12/27:22:26:15.1000000
```
The timestamp in question is obtained by successively adding a timestamp with a value of 200ms to a base value that has been rounded to the nearest integral 200ms. The previous stamps in the series report values with a seconds component of `2013/12/27:22:26:15` and fractional components of `.400`, `.600`, and `.800`. The next timestamp after the malformed one has a value of `2013/12/27:22:26:16.200000` which is correct.

After thinking about the code for a bit, I see that the problem is caused by rounding the fractional portion of the timestamp on its conversion to a range limited form (resolution between 0.1 sec and 1ns) this raises two issues:
1. The rounding of time values is questionable, at best. Time values are often used as fences. We want something to happen at or after time T, but never before. Rounding the value is simply wrong in these cases and rounding on a representational conversion by default is not appropriate.
2. Even if rounding is appropriate, rounding without carry, as is being done in the `cork_timestamp_Xsec()` functions creates problems like the one seen above. A better signature for such conversion functions might be something like:
```
cork_timestamp_decomp( const cork_timestamp ts, const size_t fr_digits, const bool round_it,
uint32_t * secs, uint32_t * fr)
```
If desired, this base function could be the basis of a family of functions / macros that fill in appropriate resolution and rounding parameters. The current functions that return the fractional portion should probably be defined to use `round_it = false` to avoid the problem noted above.

Even without rounding, there is a possibility that the cork_timestamp representation could produce unanticipated results such as the one seen above, due to imprecise alignment between the gamma second resolution of the timestamps and the use of common decimal based representations such as ms, us, and ns. It should be the case that converting an integral number of ms, us, or ns to gs and back should produce the same result if done without rounding. I will produce a test program to check this.

Even so, it is unlikely that performing extended addition or subtraction on gs timestamp values will consistently produce the same results as extended addition or subtraction on the gs values. For example, if we start with uint32_t and cork_timestamp values of 0, and repeatedly add 1 to the uint32_t value and a cork_timestamp with a value of 1ns to the cork_timestamp value, we would expect a departure in the reconverted cork_timestamp value long before we do a billion additions. This suggests that, if we want to generate periodic cork_timestamps at precise intervals less than 1 second apart, we should probably do our computations in the integer world, converting the integer values to timestamp form as needed rather than adding a fractional timestamp periodically.