1// This is an open source non-commercial project. Dear PVS-Studio, please check
2// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
4#include <assert.h>
5#include <stdint.h>
6#include <stdbool.h>
7#include <time.h>
8#include <limits.h>
9
10#include <uv.h>
11
12#include "nvim/assert.h"
13#include "nvim/os/time.h"
14#include "nvim/os/input.h"
15#include "nvim/event/loop.h"
16#include "nvim/vim.h"
17#include "nvim/main.h"
18
19static uv_mutex_t delay_mutex;
20static uv_cond_t delay_cond;
21
22
23#ifdef INCLUDE_GENERATED_DECLARATIONS
24# include "os/time.c.generated.h"
25#endif
26
27/// Initializes the time module
28void time_init(void)
29{
30 uv_mutex_init(&delay_mutex);
31 uv_cond_init(&delay_cond);
32}
33
34/// Gets a high-resolution (nanosecond), monotonically-increasing time relative
35/// to an arbitrary time in the past.
36///
37/// Not related to the time of day and therefore not subject to clock drift.
38///
39/// @return Relative time value with nanosecond precision.
40uint64_t os_hrtime(void)
41 FUNC_ATTR_WARN_UNUSED_RESULT
42{
43 return uv_hrtime();
44}
45
46/// Gets a millisecond-resolution, monotonically-increasing time relative to an
47/// arbitrary time in the past.
48///
49/// Not related to the time of day and therefore not subject to clock drift.
50/// The value is cached by the loop, it will not change until the next
51/// loop-tick (unless uv_update_time is called).
52///
53/// @return Relative time value with millisecond precision.
54uint64_t os_now(void)
55 FUNC_ATTR_WARN_UNUSED_RESULT
56{
57 return uv_now(&main_loop.uv);
58}
59
60/// Sleeps for `ms` milliseconds.
61///
62/// @param ms Number of milliseconds to sleep
63/// @param ignoreinput If true, only SIGINT (CTRL-C) can interrupt.
64void os_delay(uint64_t ms, bool ignoreinput)
65{
66 if (ignoreinput) {
67 if (ms > INT_MAX) {
68 ms = INT_MAX;
69 }
70 LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, (int)ms, got_int);
71 } else {
72 os_microdelay(ms * 1000u, ignoreinput);
73 }
74}
75
76/// Sleeps for `us` microseconds.
77///
78/// @param us Number of microseconds to sleep.
79/// @param ignoreinput If true, ignore all input (including SIGINT/CTRL-C).
80/// If false, waiting is aborted on any input.
81void os_microdelay(uint64_t us, bool ignoreinput)
82{
83 uint64_t elapsed = 0u;
84 uint64_t base = uv_hrtime();
85 // Convert microseconds to nanoseconds, or UINT64_MAX on overflow.
86 const uint64_t ns = (us < UINT64_MAX / 1000u) ? us * 1000u : UINT64_MAX;
87
88 uv_mutex_lock(&delay_mutex);
89
90 while (elapsed < ns) {
91 // If ignoring input, we simply wait the full delay.
92 // Else we check for input in ~100ms intervals.
93 const uint64_t ns_delta = ignoreinput
94 ? ns - elapsed
95 : MIN(ns - elapsed, 100000000u); // 100ms
96
97 const int rv = uv_cond_timedwait(&delay_cond, &delay_mutex, ns_delta);
98 if (0 != rv && UV_ETIMEDOUT != rv) {
99 assert(false);
100 break;
101 } // Else: Timeout proceeded normally.
102
103 if (!ignoreinput && os_char_avail()) {
104 break;
105 }
106
107 const uint64_t now = uv_hrtime();
108 elapsed += now - base;
109 base = now;
110 }
111
112 uv_mutex_unlock(&delay_mutex);
113}
114
115/// Portable version of POSIX localtime_r()
116///
117/// @return NULL in case of error
118struct tm *os_localtime_r(const time_t *restrict clock,
119 struct tm *restrict result) FUNC_ATTR_NONNULL_ALL
120{
121#ifdef UNIX
122 // POSIX provides localtime_r() as a thread-safe version of localtime().
123 return localtime_r(clock, result); // NOLINT(runtime/threadsafe_fn)
124#else
125 // Windows version of localtime() is thread-safe.
126 // See http://msdn.microsoft.com/en-us/library/bf12f0hc%28VS.80%29.aspx
127 struct tm *local_time = localtime(clock); // NOLINT(runtime/threadsafe_fn)
128 if (!local_time) {
129 return NULL;
130 }
131 *result = *local_time;
132 return result;
133#endif
134}
135
136/// Gets the current Unix timestamp and adjusts it to local time.
137///
138/// @param result Pointer to a 'struct tm' where the result should be placed
139/// @return A pointer to a 'struct tm' in the current time zone (the 'result'
140/// argument) or NULL in case of error
141struct tm *os_localtime(struct tm *result) FUNC_ATTR_NONNULL_ALL
142{
143 time_t rawtime = time(NULL);
144 return os_localtime_r(&rawtime, result);
145}
146
147/// Obtains the current Unix timestamp.
148///
149/// @return Seconds since epoch.
150Timestamp os_time(void)
151 FUNC_ATTR_WARN_UNUSED_RESULT
152{
153 return (Timestamp) time(NULL);
154}
155