Bug 1858509 add thread-safety annotations around MediaSourceDemuxer::mMonitor r=alwu
[gecko.git] / mozglue / misc / TimeStamp_posix.cpp
blobba32a230eb0968a13a46c8dbd9545f7c4e25d068
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 //
8 // Implement TimeStamp::Now() with POSIX clocks.
9 //
10 // The "tick" unit for POSIX clocks is simply a nanosecond, as this is
11 // the smallest unit of time representable by struct timespec. That
12 // doesn't mean that a nanosecond is the resolution of TimeDurations
13 // obtained with this API; see TimeDuration::Resolution;
16 #include <sys/syscall.h>
17 #include <time.h>
18 #include <unistd.h>
19 #include <string.h>
21 #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
22 defined(__OpenBSD__)
23 # include <sys/param.h>
24 # include <sys/sysctl.h>
25 #endif
27 #if defined(__DragonFly__) || defined(__FreeBSD__)
28 # include <sys/user.h>
29 #endif
31 #if defined(__NetBSD__)
32 # undef KERN_PROC
33 # define KERN_PROC KERN_PROC2
34 # define KINFO_PROC struct kinfo_proc2
35 #else
36 # define KINFO_PROC struct kinfo_proc
37 #endif
39 #if defined(__DragonFly__)
40 # define KP_START_SEC kp_start.tv_sec
41 # define KP_START_USEC kp_start.tv_usec
42 #elif defined(__FreeBSD__)
43 # define KP_START_SEC ki_start.tv_sec
44 # define KP_START_USEC ki_start.tv_usec
45 #else
46 # define KP_START_SEC p_ustart_sec
47 # define KP_START_USEC p_ustart_usec
48 #endif
50 #include "mozilla/Sprintf.h"
51 #include "mozilla/TimeStamp.h"
53 #if !defined(__wasi__)
54 # include <pthread.h>
55 #endif
57 // Estimate of the smallest duration of time we can measure.
58 static uint64_t sResolution;
59 static uint64_t sResolutionSigDigs;
61 #ifdef CLOCK_MONOTONIC_COARSE
62 static bool sSupportsMonotonicCoarseClock = false;
63 #endif
65 #if !defined(__wasi__)
66 static const uint16_t kNsPerUs = 1000;
67 #endif
69 static const uint64_t kNsPerMs = 1000000;
70 static const uint64_t kNsPerSec = 1000000000;
71 static const double kNsPerMsd = 1000000.0;
72 static const double kNsPerSecd = 1000000000.0;
74 static uint64_t TimespecToNs(const struct timespec& aTs) {
75 uint64_t baseNs = uint64_t(aTs.tv_sec) * kNsPerSec;
76 return baseNs + uint64_t(aTs.tv_nsec);
79 static uint64_t ClockTimeNs(const clockid_t aClockId = CLOCK_MONOTONIC) {
80 struct timespec ts;
81 #ifdef CLOCK_MONOTONIC_COARSE
82 MOZ_RELEASE_ASSERT(
83 aClockId == CLOCK_MONOTONIC ||
84 (sSupportsMonotonicCoarseClock && aClockId == CLOCK_MONOTONIC_COARSE));
85 #else
86 MOZ_RELEASE_ASSERT(aClockId == CLOCK_MONOTONIC);
87 #endif
88 // this can't fail: we know &ts is valid, and TimeStamp::Startup()
89 // checks that CLOCK_MONOTONIC / CLOCK_MONOTONIC_COARSE are
90 // supported (and aborts if the former is not).
91 clock_gettime(aClockId, &ts);
93 // tv_sec is defined to be relative to an arbitrary point in time,
94 // but it would be madness for that point in time to be earlier than
95 // the Epoch. So we can safely assume that even if time_t is 32
96 // bits, tv_sec won't overflow while the browser is open. Revisit
97 // this argument if we're still building with 32-bit time_t around
98 // the year 2037.
99 return TimespecToNs(ts);
102 static uint64_t ClockResolutionNs() {
103 // NB: why not rely on clock_getres()? Two reasons: (i) it might
104 // lie, and (ii) it might return an "ideal" resolution that while
105 // theoretically true, could never be measured in practice. Since
106 // clock_gettime() likely involves a system call on your platform,
107 // the "actual" timing resolution shouldn't be lower than syscall
108 // overhead.
110 uint64_t start = ClockTimeNs();
111 uint64_t end = ClockTimeNs();
112 uint64_t minres = (end - start);
114 // 10 total trials is arbitrary: what we're trying to avoid by
115 // looping is getting unlucky and being interrupted by a context
116 // switch or signal, or being bitten by paging/cache effects
117 for (int i = 0; i < 9; ++i) {
118 start = ClockTimeNs();
119 end = ClockTimeNs();
121 uint64_t candidate = (start - end);
122 if (candidate < minres) {
123 minres = candidate;
127 if (0 == minres) {
128 // measurable resolution is either incredibly low, ~1ns, or very
129 // high. fall back on clock_getres()
130 struct timespec ts;
131 if (0 == clock_getres(CLOCK_MONOTONIC, &ts)) {
132 minres = TimespecToNs(ts);
136 if (0 == minres) {
137 // clock_getres probably failed. fall back on NSPR's resolution
138 // assumption
139 minres = 1 * kNsPerMs;
142 return minres;
145 namespace mozilla {
147 double BaseTimeDurationPlatformUtils::ToSeconds(int64_t aTicks) {
148 return double(aTicks) / kNsPerSecd;
151 double BaseTimeDurationPlatformUtils::ToSecondsSigDigits(int64_t aTicks) {
152 // don't report a value < mResolution ...
153 int64_t valueSigDigs = sResolution * (aTicks / sResolution);
154 // and chop off insignificant digits
155 valueSigDigs = sResolutionSigDigs * (valueSigDigs / sResolutionSigDigs);
156 return double(valueSigDigs) / kNsPerSecd;
159 int64_t BaseTimeDurationPlatformUtils::TicksFromMilliseconds(
160 double aMilliseconds) {
161 double result = aMilliseconds * kNsPerMsd;
162 if (result > double(INT64_MAX)) {
163 return INT64_MAX;
165 if (result < INT64_MIN) {
166 return INT64_MIN;
169 return result;
172 int64_t BaseTimeDurationPlatformUtils::ResolutionInTicks() {
173 return static_cast<int64_t>(sResolution);
176 static bool gInitialized = false;
178 void TimeStamp::Startup() {
179 if (gInitialized) {
180 return;
183 struct timespec dummy;
184 if (clock_gettime(CLOCK_MONOTONIC, &dummy) != 0) {
185 MOZ_CRASH("CLOCK_MONOTONIC is absent!");
188 #ifdef CLOCK_MONOTONIC_COARSE
189 if (clock_gettime(CLOCK_MONOTONIC_COARSE, &dummy) == 0) {
190 sSupportsMonotonicCoarseClock = true;
192 #endif
194 sResolution = ClockResolutionNs();
196 // find the number of significant digits in sResolution, for the
197 // sake of ToSecondsSigDigits()
198 for (sResolutionSigDigs = 1; !(sResolutionSigDigs == sResolution ||
199 10 * sResolutionSigDigs > sResolution);
200 sResolutionSigDigs *= 10)
203 gInitialized = true;
206 void TimeStamp::Shutdown() {}
208 TimeStamp TimeStamp::Now(bool aHighResolution) {
209 #ifdef CLOCK_MONOTONIC_COARSE
210 if (!aHighResolution && sSupportsMonotonicCoarseClock) {
211 return TimeStamp(ClockTimeNs(CLOCK_MONOTONIC_COARSE));
213 #endif
214 return TimeStamp(ClockTimeNs(CLOCK_MONOTONIC));
217 #if defined(XP_LINUX) || defined(ANDROID)
219 // Calculates the amount of jiffies that have elapsed since boot and up to the
220 // starttime value of a specific process as found in its /proc/*/stat file.
221 // Returns 0 if an error occurred.
223 static uint64_t JiffiesSinceBoot(const char* aFile) {
224 char stat[512];
226 FILE* f = fopen(aFile, "r");
227 if (!f) {
228 return 0;
231 int n = fread(&stat, 1, sizeof(stat) - 1, f);
233 fclose(f);
235 if (n <= 0) {
236 return 0;
239 stat[n] = 0;
241 long long unsigned startTime = 0; // instead of uint64_t to keep GCC quiet
242 char* s = strrchr(stat, ')');
244 if (!s) {
245 return 0;
248 int rv = sscanf(s + 2,
249 "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u "
250 "%*u %*u %*u %*d %*d %*d %*d %*d %*d %llu",
251 &startTime);
253 if (rv != 1 || !startTime) {
254 return 0;
257 return startTime;
260 // Computes the interval that has elapsed between the thread creation and the
261 // process creation by comparing the starttime fields in the respective
262 // /proc/*/stat files. The resulting value will be a good approximation of the
263 // process uptime. This value will be stored at the address pointed by aTime;
264 // if an error occurred 0 will be stored instead.
266 static void* ComputeProcessUptimeThread(void* aTime) {
267 uint64_t* uptime = static_cast<uint64_t*>(aTime);
268 long hz = sysconf(_SC_CLK_TCK);
270 *uptime = 0;
272 if (!hz) {
273 return nullptr;
276 char threadStat[40];
277 SprintfLiteral(threadStat, "/proc/self/task/%d/stat",
278 (pid_t)syscall(__NR_gettid));
280 uint64_t threadJiffies = JiffiesSinceBoot(threadStat);
281 uint64_t selfJiffies = JiffiesSinceBoot("/proc/self/stat");
283 if (!threadJiffies || !selfJiffies) {
284 return nullptr;
287 *uptime = ((threadJiffies - selfJiffies) * kNsPerSec) / hz;
288 return nullptr;
291 // Computes and returns the process uptime in us on Linux & its derivatives.
292 // Returns 0 if an error was encountered.
294 uint64_t TimeStamp::ComputeProcessUptime() {
295 uint64_t uptime = 0;
296 pthread_t uptime_pthread;
298 if (pthread_create(&uptime_pthread, nullptr, ComputeProcessUptimeThread,
299 &uptime)) {
300 MOZ_CRASH("Failed to create process uptime thread.");
301 return 0;
304 pthread_join(uptime_pthread, NULL);
306 return uptime / kNsPerUs;
309 #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
310 defined(__OpenBSD__)
312 // Computes and returns the process uptime in us on various BSD flavors.
313 // Returns 0 if an error was encountered.
315 uint64_t TimeStamp::ComputeProcessUptime() {
316 struct timespec ts;
317 int rv = clock_gettime(CLOCK_REALTIME, &ts);
319 if (rv == -1) {
320 return 0;
323 int mib[] = {
324 CTL_KERN,
325 KERN_PROC,
326 KERN_PROC_PID,
327 getpid(),
328 # if defined(__NetBSD__) || defined(__OpenBSD__)
329 sizeof(KINFO_PROC),
331 # endif
333 u_int mibLen = sizeof(mib) / sizeof(mib[0]);
335 KINFO_PROC proc;
336 size_t bufferSize = sizeof(proc);
337 rv = sysctl(mib, mibLen, &proc, &bufferSize, nullptr, 0);
339 if (rv == -1) {
340 return 0;
343 uint64_t startTime = ((uint64_t)proc.KP_START_SEC * kNsPerSec) +
344 (proc.KP_START_USEC * kNsPerUs);
345 uint64_t now = ((uint64_t)ts.tv_sec * kNsPerSec) + ts.tv_nsec;
347 if (startTime > now) {
348 return 0;
351 return (now - startTime) / kNsPerUs;
354 #else
356 uint64_t TimeStamp::ComputeProcessUptime() { return 0; }
358 #endif
360 } // namespace mozilla