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 | |
19 | static uv_mutex_t delay_mutex; |
20 | static 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 |
28 | void 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. |
40 | uint64_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. |
54 | uint64_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. |
64 | void 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. |
81 | void 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 |
118 | struct 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 |
141 | struct 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. |
150 | Timestamp os_time(void) |
151 | FUNC_ATTR_WARN_UNUSED_RESULT |
152 | { |
153 | return (Timestamp) time(NULL); |
154 | } |
155 | |