Following the article https://chronicle.software/unique-timestamp-identifiers/, I have checked the implementation of DistributedUniqueTimeProvider.currentTimeNanos method.
long time = provider.currentTimeNanos();
long time0 = bytes.readVolatileLong(LAST_TIME);
long timeN = timestampFor(time) + hostId;
if (timeN > time0 && bytes.compareAndSwapLong(LAST_TIME, time0, timeN))
return timeN;
return currentTimeNanosLoop();
By default, it uses SystemTimeProvider
If we look inside net.openhft.chronicle.core.time.SystemTimeProvider#currentTimeNanos
long nowNS = System.nanoTime();
long nowMS = currentTimeMillis() * NANOS_PER_MILLI;
long estimate = nowNS + delta;
if (estimate < nowMS) {
delta = nowMS - nowNS;
return nowMS;
} else if (estimate > nowMS + NANOS_PER_MILLI) {
nowMS += NANOS_PER_MILLI;
delta = nowMS - nowNS;
return nowMS;
}
return estimate;
we can see that it uses non-volatile, non-atomic variable
private long delta = 0;
So the question is: Is DistributedUniqueTimeProvider.currentTimeNanos thread-safe after all? If yes, why is that?
The value delta
is an estimate of the difference between the wall clock and the monotonic clock. In the worst case, this could be up to 1 ms in different threads as there is always at least one check of milliseconds vs nanoseconds. However, when used with the DistributedUniqueTimeProvider
which has a full memory barrier and it's own enforcement of a monotonically increasing value, it won't be significant
The reason this memory barrier check is dropped is to reduce the cost of this operation by about 20%