001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.util;
018
019import java.util.concurrent.locks.LockSupport;
020
021/**
022 * Implementation of the {@code Clock} interface that tracks the time in a
023 * private long field that is updated by a background thread once every
024 * millisecond. Timers on most platforms do not have millisecond granularity, so
025 * the returned value may "jump" every 10 or 16 milliseconds. To reduce this
026 * problem, this class also updates the internal time value every 1024 calls to
027 * {@code currentTimeMillis()}.
028 */
029public final class CachedClock implements Clock {
030    private static final int UPDATE_THRESHOLD = 1000;
031    private static volatile CachedClock instance;
032    private static final Object INSTANCE_LOCK = new Object();
033    private volatile long millis = System.currentTimeMillis();
034    private short count = 0;
035
036    private CachedClock() {
037        final Thread updater = new Log4jThread(() -> {
038            while (true) {
039                final long time = System.currentTimeMillis();
040                millis = time;
041
042                // avoid explicit dependency on sun.misc.Util
043                LockSupport.parkNanos(1000 * 1000);
044            }
045        }, "CachedClock Updater Thread");
046        updater.setDaemon(true);
047        updater.start();
048    }
049
050    public static CachedClock instance() {
051        // LOG4J2-819: use lazy initialization of threads
052        CachedClock result = instance;
053        if (result == null) {
054            synchronized (INSTANCE_LOCK) {
055                result = instance;
056                if (result == null) {
057                    instance = result = new CachedClock();
058                }
059            }
060        }
061        return result;
062    }
063
064    /**
065     * Returns the value of a private long field that is updated by a background
066     * thread once every millisecond. Timers on most platforms do not
067     * have millisecond granularity, the returned value may "jump" every 10 or
068     * 16 milliseconds. To reduce this problem, this method also updates the
069     * internal time value every 1024 calls.
070     * @return the cached time
071     */
072    @Override
073    public long currentTimeMillis() {
074
075        // The count field is not volatile on purpose to reduce contention on this field.
076        // This means that some threads may not see the increments made to this field
077        // by other threads. This is not a problem: the timestamp does not need to be
078        // updated exactly every 1000 calls.
079        if (++count > UPDATE_THRESHOLD) {
080            millis = System.currentTimeMillis(); // update volatile field: store-store barrier
081            count = 0; // after a memory barrier: this change _is_ visible to other threads
082        }
083        return millis;
084    }
085}