blob: 7f226f45b4d84b2e826d1ebc3d76a7d2e0463840 [file] [edit]
/*
* Copyright (C) 2026 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include <wtf/UnbarrieredMonotonicTime.h>
#include <wtf/ContinuousTime.h>
#include <wtf/Lock.h>
#include <wtf/MonotonicTime.h>
#include <wtf/PrintStream.h>
#include <wtf/WallTime.h>
#if OS(DARWIN)
#include <mach/mach_time.h>
#endif
namespace WTF {
#if USE(HARDWARE_UNBARRIERED_MONOTONIC_TIME)
UnbarrieredMonotonicTime::Calibration UnbarrieredMonotonicTime::s_calibration;
void UnbarrieredMonotonicTime::calibrate()
{
constexpr double nanosecondsPerSecond = 1000'000'000;
static Lock lock;
Locker locker { lock };
if (s_calibration.nanosecondsPerTick)
return; // Already calibrated.
auto readCounterFrequency = [] -> uint64_t {
uint64_t val;
__asm__ volatile("mrs %0, CNTFRQ_EL0" : "=r"(val) : : "memory");
return val;
};
// We're going straight to mach_absolute_time() instead of MonotonicTime::now()
// because we want to miminize the time difference between sampling it and sampling
// CNTVCT_EL0. MonotonicTime::now() can be deterministically computed from the
// value of mach_absolute_time().
//
// Note that the value of mach_absolute_time() relies on when the kernel samples
// CNTVCT_EL0 and update it. Effectively, mach_absolute_time may be of a lower
// resolution than CNTVCT_EL0 depending on when the kernel updates it. So, for our
// calibration, to minimize the difference between the 2 values, we'll sample
// them until we see mach_absolute_time()'s value change 2 times. This allows
// us to catch the CNTVCT_EL0 value right as mach_absolute_time() changes.
//
// We look for 2 transitions of mach_absolute_time() because the first transition
// may race against us and occur just as we enter the sampling loop. Waiting for
// the 2nd transition reduces the impact of such a race.
uint64_t counter = readCounter();
uint64_t startAbsTime = mach_absolute_time();
uint64_t absTime = startAbsTime;
for (int i = 0; i < 2; ++i) {
do {
counter = readCounter();
absTime = mach_absolute_time();
} while (absTime == startAbsTime);
}
// Now that we have the closely paired samples of mach_absolute_time() and CNTVCT_EL0,
// determinstically compute the calibration values.
mach_timebase_info_data_t info;
kern_return_t kr = mach_timebase_info(&info);
ASSERT_UNUSED(kr, kr == KERN_SUCCESS);
s_calibration.startCounter = counter;
s_calibration.startNanoseconds = absTime * info.numer / (double)info.denom;
uint64_t ticksPerSecond = readCounterFrequency();
s_calibration.nanosecondsPerTick = nanosecondsPerSecond / ticksPerSecond;
}
#endif
void UnbarrieredMonotonicTime::dump(PrintStream& out) const
{
out.print("UnbarrieredMonotonic(", m_value, " sec)");
}
} // namespace WTF