All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nebula.plugin.metrics.time.MonotonicClock Maven / Gradle / Ivy

Go to download

Gradle Metrics plugin: Collects gradle metrics and publishes to ElasticSearch HTTP/Splunk/REST endpoints

There is a newer version: 10.1.8
Show newest version
/*
 * Copyright 2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package nebula.plugin.metrics.time;


import com.google.common.annotations.VisibleForTesting;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * A clock that is guaranteed to not go backwards.
 * 

* It aims to strike a balance between never going backwards (allowing timestamps to represent causality) * and keeping in sync with the system wall clock so that time values make sense in comparison with the system wall clock, * including timestamps generated from other processes. *

* This clock effectively measures time by duration (according to System.nanoTime()), * in between syncs with the system wall clock. * When issuing the first timestamp after the sync interval has expired, * The system wall clock will be read, and the current time set to the max of wall clock time or the most recently issued timestamp. * All other timestamps are calculated as the wall clock time at last sync + elapsed time since. *

* This clock deals relatively well when the system wall clock shift is adjusted by small amounts. * It also deals relatively well when the system wall clock jumps forward by large amounts (this clock will jump with it). * It does not deal as well with large jumps back in time. *

* When the system wall clock jumps back in time, this clock will effectively slow down until it is back in sync. * All syncing timestamps will be the same as the previously issued timestamp. * The rate by which this clock slows, and therefore the time it takes to resync, * is determined by how frequently the clock is read. * If timestamps are only requested at a rate greater than the sync interval, * all timestamps will have the same value until the clocks synchronize (i.e. this clock will pause). * If timestamps are requested more frequently than the sync interval, * timestamps before and after the sync point will under represent the actual elapsed time, * gradually bringing the clocks back into sync. */ public class MonotonicClock implements Clock { private static final long SYNC_INTERVAL_MILLIS = TimeUnit.SECONDS.toMillis(3); private final long syncIntervalMillis; private final TimeSource timeSource; private final AtomicLong syncMillisRef; private final AtomicLong syncNanosRef; private final AtomicLong max = new AtomicLong(0); public MonotonicClock() { this(TimeSource.SYSTEM, SYNC_INTERVAL_MILLIS); } @VisibleForTesting public MonotonicClock(TimeSource timeSource, long syncIntervalMillis) { checkNotNull(timeSource); long nanoTime = timeSource.nanoTime(); long currentTimeMillis = timeSource.currentTimeMillis(); this.timeSource = timeSource; this.syncIntervalMillis = syncIntervalMillis; this.syncNanosRef = new AtomicLong(nanoTime); this.syncMillisRef = new AtomicLong(currentTimeMillis); this.max.set(currentTimeMillis); } public long getCurrentTime() { long nowNanos = timeSource.nanoTime(); long syncNanos = syncNanosRef.get(); long syncMillis = syncMillisRef.get(); long sinceSyncNanos = nowNanos - syncNanos; long sinceSyncMillis = TimeUnit.NANOSECONDS.toMillis(sinceSyncNanos); if (syncIsDue(nowNanos, syncNanos, sinceSyncMillis)) { return sync(syncMillis); } else { return advance(syncMillis + sinceSyncMillis); } } private boolean syncIsDue(long nowNanos, long syncNanos, long sinceSyncMillis) { return sinceSyncMillis >= syncIntervalMillis && syncNanosRef.compareAndSet(syncNanos, nowNanos); } private long sync(long syncMillis) { long newSyncMillis = advance(timeSource.currentTimeMillis()); // CAS due to potentially a later, but overlapping, sync having already completed syncMillisRef.compareAndSet(syncMillis, newSyncMillis); return newSyncMillis; } private long advance(long timestamp) { long prev; long next; do { prev = max.get(); next = Math.max(prev, timestamp); } while (!max.compareAndSet(prev, next)); return next; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy