Coverage Report

Created: 2026-10-24 08:16

/work/toxcore/mono_time.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: GPL-3.0-or-later
2
 * Copyright © 2016-2026 The TokTok team.
3
 * Copyright © 2014 Tox project.
4
 */
5
#ifndef _XOPEN_SOURCE
6
#define _XOPEN_SOURCE 600
7
#endif /* _XOPEN_SOURCE */
8
9
#if !defined(OS_WIN32) && (defined(_WIN32) || defined(__WIN32__) || defined(WIN32))
10
#define OS_WIN32
11
#endif /* WIN32 */
12
13
#include "mono_time.h"
14
15
#ifdef OS_WIN32
16
#define WIN32_LEAN_AND_MEAN
17
#include <windows.h>
18
#endif /* OS_WIN32 */
19
20
#ifdef __APPLE__
21
#include <mach/clock.h>
22
#include <mach/mach.h>
23
#endif /* __APPLE__ */
24
25
#ifndef OS_WIN32
26
#include <sys/time.h>
27
#endif /* OS_WIN32 */
28
29
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
30
#include <assert.h>
31
#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
32
#include <pthread.h>
33
#include <time.h>
34
35
#include "attributes.h"
36
#include "ccompat.h"
37
#include "mem.h"
38
#include "util.h"
39
40
/** don't call into system billions of times for no reason */
41
struct Mono_Time {
42
    uint64_t cur_time;
43
    uint64_t base_time;
44
45
#ifndef ESP_PLATFORM
46
    /** protect @ref cur_time from concurrent access */
47
    pthread_rwlock_t *_Nonnull time_update_lock;
48
#endif /* ESP_PLATFORM */
49
50
    mono_time_current_time_cb *_Nonnull current_time_callback;
51
    void *_Nullable user_data;
52
};
53
54
static uint64_t timespec_to_u64(struct timespec clock_mono)
55
18.1k
{
56
18.1k
    return UINT64_C(1000) * clock_mono.tv_sec + (clock_mono.tv_nsec / UINT64_C(1000000));
57
18.1k
}
58
59
#ifdef OS_WIN32
60
static uint64_t current_time_monotonic_default(void *_Nonnull user_data)
61
{
62
    LARGE_INTEGER freq;
63
    LARGE_INTEGER count;
64
    if (!QueryPerformanceFrequency(&freq)) {
65
        return 0;
66
    }
67
    if (!QueryPerformanceCounter(&count)) {
68
        return 0;
69
    }
70
    struct timespec sp = {0};
71
    sp.tv_sec = count.QuadPart / freq.QuadPart;
72
    if (freq.QuadPart < 1000000000) {
73
        sp.tv_nsec = (count.QuadPart % freq.QuadPart) * 1000000000 / freq.QuadPart;
74
    } else {
75
        sp.tv_nsec = (long)((count.QuadPart % freq.QuadPart) * (1000000000.0 / freq.QuadPart));
76
    }
77
    return timespec_to_u64(sp);
78
}
79
#else
80
#ifdef __APPLE__
81
static uint64_t current_time_monotonic_default(void *_Nonnull user_data)
82
{
83
    struct timespec clock_mono;
84
    clock_serv_t muhclock;
85
    mach_timespec_t machtime;
86
87
    host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &muhclock);
88
    clock_get_time(muhclock, &machtime);
89
    mach_port_deallocate(mach_task_self(), muhclock);
90
91
    clock_mono.tv_sec = machtime.tv_sec;
92
    clock_mono.tv_nsec = machtime.tv_nsec;
93
    return timespec_to_u64(clock_mono);
94
}
95
#else // !__APPLE__
96
static uint64_t current_time_monotonic_default(void *_Nonnull user_data)
97
0
{
98
0
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
99
    // This assert should always fail. If it does, the fuzzing harness didn't
100
    // override the mono time callback.
101
0
    assert(user_data == nullptr);
102
0
#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
103
0
    struct timespec clock_mono;
104
0
    clock_gettime(CLOCK_MONOTONIC, &clock_mono);
105
0
    return timespec_to_u64(clock_mono);
106
0
}
107
#endif /* !__APPLE__ */
108
#endif /* !OS_WIN32 */
109
110
Mono_Time *mono_time_new(const Memory *mem, mono_time_current_time_cb *current_time_callback, void *user_data)
111
4.87k
{
112
4.87k
    Mono_Time *mono_time = (Mono_Time *)mem_alloc(mem, sizeof(Mono_Time));
113
114
4.87k
    if (mono_time == nullptr) {
115
6
        return nullptr;
116
6
    }
117
118
4.87k
#ifndef ESP_PLATFORM
119
4.87k
    pthread_rwlock_t *const rwlock = (pthread_rwlock_t *)mem_alloc(mem, sizeof(pthread_rwlock_t));
120
121
4.87k
    if (rwlock == nullptr) {
122
6
        mem_delete(mem, mono_time);
123
6
        return nullptr;
124
6
    }
125
126
4.86k
    if (pthread_rwlock_init(rwlock, nullptr) != 0) {
127
0
        mem_delete(mem, rwlock);
128
0
        mem_delete(mem, mono_time);
129
0
        return nullptr;
130
0
    }
131
132
4.86k
    mono_time->time_update_lock = rwlock;
133
4.86k
#endif /* ESP_PLATFORM */
134
135
4.86k
    mono_time_set_current_time_callback(mono_time, current_time_callback, user_data);
136
137
4.86k
    mono_time->cur_time = 0;
138
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
139
    // Maximum reproducibility. Never return time = 0.
140
    mono_time->base_time = 1000000000;
141
#else
142
    // Never return time = 0 in case time() returns 0 (e.g. on microcontrollers
143
    // without battery-powered RTC or ones where NTP didn't initialise it yet).
144
2.43k
    mono_time->base_time = max_u64(1, (uint64_t)time(nullptr)) * UINT64_C(1000) - current_time_monotonic(mono_time);
145
#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
146
147
4.86k
    mono_time_update(mono_time);
148
149
4.86k
    return mono_time;
150
4.86k
}
mono_time_new
Line
Count
Source
111
2.43k
{
112
2.43k
    Mono_Time *mono_time = (Mono_Time *)mem_alloc(mem, sizeof(Mono_Time));
113
114
2.43k
    if (mono_time == nullptr) {
115
3
        return nullptr;
116
3
    }
117
118
2.43k
#ifndef ESP_PLATFORM
119
2.43k
    pthread_rwlock_t *const rwlock = (pthread_rwlock_t *)mem_alloc(mem, sizeof(pthread_rwlock_t));
120
121
2.43k
    if (rwlock == nullptr) {
122
3
        mem_delete(mem, mono_time);
123
3
        return nullptr;
124
3
    }
125
126
2.43k
    if (pthread_rwlock_init(rwlock, nullptr) != 0) {
127
0
        mem_delete(mem, rwlock);
128
0
        mem_delete(mem, mono_time);
129
0
        return nullptr;
130
0
    }
131
132
2.43k
    mono_time->time_update_lock = rwlock;
133
2.43k
#endif /* ESP_PLATFORM */
134
135
2.43k
    mono_time_set_current_time_callback(mono_time, current_time_callback, user_data);
136
137
2.43k
    mono_time->cur_time = 0;
138
2.43k
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
139
    // Maximum reproducibility. Never return time = 0.
140
2.43k
    mono_time->base_time = 1000000000;
141
#else
142
    // Never return time = 0 in case time() returns 0 (e.g. on microcontrollers
143
    // without battery-powered RTC or ones where NTP didn't initialise it yet).
144
    mono_time->base_time = max_u64(1, (uint64_t)time(nullptr)) * UINT64_C(1000) - current_time_monotonic(mono_time);
145
#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
146
147
2.43k
    mono_time_update(mono_time);
148
149
2.43k
    return mono_time;
150
2.43k
}
mono_time_new
Line
Count
Source
111
2.43k
{
112
2.43k
    Mono_Time *mono_time = (Mono_Time *)mem_alloc(mem, sizeof(Mono_Time));
113
114
2.43k
    if (mono_time == nullptr) {
115
3
        return nullptr;
116
3
    }
117
118
2.43k
#ifndef ESP_PLATFORM
119
2.43k
    pthread_rwlock_t *const rwlock = (pthread_rwlock_t *)mem_alloc(mem, sizeof(pthread_rwlock_t));
120
121
2.43k
    if (rwlock == nullptr) {
122
3
        mem_delete(mem, mono_time);
123
3
        return nullptr;
124
3
    }
125
126
2.43k
    if (pthread_rwlock_init(rwlock, nullptr) != 0) {
127
0
        mem_delete(mem, rwlock);
128
0
        mem_delete(mem, mono_time);
129
0
        return nullptr;
130
0
    }
131
132
2.43k
    mono_time->time_update_lock = rwlock;
133
2.43k
#endif /* ESP_PLATFORM */
134
135
2.43k
    mono_time_set_current_time_callback(mono_time, current_time_callback, user_data);
136
137
2.43k
    mono_time->cur_time = 0;
138
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
139
    // Maximum reproducibility. Never return time = 0.
140
    mono_time->base_time = 1000000000;
141
#else
142
    // Never return time = 0 in case time() returns 0 (e.g. on microcontrollers
143
    // without battery-powered RTC or ones where NTP didn't initialise it yet).
144
2.43k
    mono_time->base_time = max_u64(1, (uint64_t)time(nullptr)) * UINT64_C(1000) - current_time_monotonic(mono_time);
145
2.43k
#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
146
147
2.43k
    mono_time_update(mono_time);
148
149
2.43k
    return mono_time;
150
2.43k
}
151
152
void mono_time_free(const Memory *mem, Mono_Time *mono_time)
153
2.38k
{
154
2.38k
    if (mono_time == nullptr) {
155
0
        return;
156
0
    }
157
2.38k
#ifndef ESP_PLATFORM
158
2.38k
    pthread_rwlock_destroy(mono_time->time_update_lock);
159
2.38k
    mem_delete(mem, mono_time->time_update_lock);
160
2.38k
#endif /* ESP_PLATFORM */
161
2.38k
    mem_delete(mem, mono_time);
162
2.38k
}
163
164
void mono_time_update(Mono_Time *mono_time)
165
140k
{
166
140k
    const uint64_t cur_time =
167
140k
        mono_time->base_time + mono_time->current_time_callback(mono_time->user_data);
168
169
140k
#ifndef ESP_PLATFORM
170
140k
    pthread_rwlock_wrlock(mono_time->time_update_lock);
171
140k
#endif /* ESP_PLATFORM */
172
140k
    mono_time->cur_time = cur_time;
173
140k
#ifndef ESP_PLATFORM
174
140k
    pthread_rwlock_unlock(mono_time->time_update_lock);
175
140k
#endif /* ESP_PLATFORM */
176
140k
}
177
178
uint64_t mono_time_get_ms(const Mono_Time *mono_time)
179
31.0M
{
180
#if !defined(ESP_PLATFORM) && !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
181
    // Fuzzing is only single thread for now, no locking needed */
182
    pthread_rwlock_rdlock(mono_time->time_update_lock);
183
#endif /* !ESP_PLATFORM */
184
31.0M
    const uint64_t cur_time = mono_time->cur_time;
185
#if !defined(ESP_PLATFORM) && !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
186
    pthread_rwlock_unlock(mono_time->time_update_lock);
187
#endif /* !ESP_PLATFORM */
188
31.0M
    return cur_time;
189
31.0M
}
190
191
uint64_t mono_time_get(const Mono_Time *mono_time)
192
31.0M
{
193
31.0M
    return mono_time_get_ms(mono_time) / UINT64_C(1000);
194
31.0M
}
195
196
bool mono_time_is_timeout(const Mono_Time *mono_time, uint64_t timestamp, uint64_t timeout)
197
28.4M
{
198
28.4M
    return timestamp + timeout <= mono_time_get(mono_time);
199
28.4M
}
200
201
void mono_time_set_current_time_callback(Mono_Time *mono_time,
202
        mono_time_current_time_cb *current_time_callback, void *user_data)
203
2.78k
{
204
2.78k
    if (current_time_callback == nullptr) {
205
564
        mono_time->current_time_callback = current_time_monotonic_default;
206
564
        mono_time->user_data = mono_time;
207
2.22k
    } else {
208
2.22k
        mono_time->current_time_callback = current_time_callback;
209
2.22k
        mono_time->user_data = user_data;
210
2.22k
    }
211
2.78k
}
212
213
/** @brief Return current monotonic time in milliseconds (ms).
214
 *
215
 * The starting point is unspecified and in particular is likely not comparable
216
 * to the return value of `mono_time_get_ms()`.
217
 */
218
uint64_t current_time_monotonic(const Mono_Time *mono_time)
219
375k
{
220
375k
    return mono_time->current_time_callback(mono_time->user_data);
221
375k
}