1 // SPDX-License-Identifier: 0BSD
2
3 ///////////////////////////////////////////////////////////////////////////////
4 //
5 /// \file mytime.c
6 /// \brief Time handling functions
7 //
8 // Author: Lasse Collin
9 //
10 ///////////////////////////////////////////////////////////////////////////////
11
12 #include "private.h"
13
14 #if defined(MYTHREAD_VISTA) || defined(_MSC_VER)
15 // Nothing
16 #elif defined(HAVE_CLOCK_GETTIME) \
17 && (!defined(__MINGW32__) || defined(MYTHREAD_POSIX))
18 # include <time.h>
19 #else
20 # include <sys/time.h>
21 #endif
22
23 uint64_t opt_flush_timeout = 0;
24
25 // start_time holds the time when the (de)compression was started.
26 // It's from mytime_now() and thus only useful for calculating relative
27 // time differences (elapsed time). start_time is initialized by calling
28 // mytime_set_start_time() and modified by mytime_sigtstp_handler().
29 //
30 // When mytime_sigtstp_handler() is used, start_time is made volatile.
31 // I'm not sure if that is really required since access to it is guarded
32 // by signals_block()/signals_unblock() since accessing an uint64_t isn't
33 // atomic on all systems. But since the variable isn't accessed very
34 // frequently making it volatile doesn't hurt.
35 #ifdef USE_SIGTSTP_HANDLER
36 static volatile uint64_t start_time;
37 #else
38 static uint64_t start_time;
39 #endif
40
41 static uint64_t next_flush;
42
43
44 /// \brief Get the current time as milliseconds
45 ///
46 /// It's relative to some point but not necessarily to the UNIX Epoch.
47 static uint64_t
mytime_now(void)48 mytime_now(void)
49 {
50 #if defined(MYTHREAD_VISTA) || defined(_MSC_VER)
51 // Since there is no SIGALRM on Windows, this function gets
52 // called frequently when the progress indicator is in use.
53 // Progress indicator doesn't need high-resolution time.
54 // GetTickCount64() has very low overhead but needs at least WinVista.
55 //
56 // MinGW-w64 provides the POSIX functions clock_gettime() and
57 // gettimeofday() in a manner that allow xz to run on older
58 // than WinVista. If the threading method needs WinVista anyway,
59 // there's no reason to avoid a WinVista API here either.
60 return GetTickCount64();
61
62 #elif defined(HAVE_CLOCK_GETTIME) \
63 && (!defined(__MINGW32__) || defined(MYTHREAD_POSIX))
64 // MinGW-w64: clock_gettime() is defined in winpthreads but we need
65 // nothing else from winpthreads (unless, for some odd reason, POSIX
66 // threading has been selected). By avoiding clock_gettime(), we
67 // avoid the dependency on libwinpthread-1.dll or the need to link
68 // against the static version. The downside is that the fallback
69 // method, gettimeofday(), doesn't provide monotonic time.
70 struct timespec tv;
71
72 # ifdef HAVE_CLOCK_MONOTONIC
73 // If CLOCK_MONOTONIC was available at compile time but for some
74 // reason isn't at runtime, fallback to CLOCK_REALTIME which
75 // according to POSIX is mandatory for all implementations.
76 static clockid_t clk_id = CLOCK_MONOTONIC;
77 while (clock_gettime(clk_id, &tv))
78 clk_id = CLOCK_REALTIME;
79 # else
80 clock_gettime(CLOCK_REALTIME, &tv);
81 # endif
82
83 return (uint64_t)tv.tv_sec * 1000 + (uint64_t)(tv.tv_nsec / 1000000);
84
85 #else
86 struct timeval tv;
87 gettimeofday(&tv, NULL);
88 return (uint64_t)tv.tv_sec * 1000 + (uint64_t)(tv.tv_usec / 1000);
89 #endif
90 }
91
92
93 #ifdef USE_SIGTSTP_HANDLER
94 extern void
mytime_sigtstp_handler(int sig lzma_attribute ((__unused__)))95 mytime_sigtstp_handler(int sig lzma_attribute((__unused__)))
96 {
97 // Measure how long the process stays in the stopped state and add
98 // that amount to start_time. This way the progress indicator
99 // won't count the stopped time as elapsed time and the estimated
100 // remaining time won't be confused by the time spent in the
101 // stopped state.
102 //
103 // FIXME? Is raising SIGSTOP the correct thing to do? POSIX.1-2017
104 // says that orphan processes shouldn't stop on SIGTSTP. So perhaps
105 // the most correct thing to do could be to revert to the default
106 // handler for SIGTSTP, unblock SIGTSTP, and then raise(SIGTSTP).
107 // It's quite a bit more complicated than just raising SIGSTOP though.
108 //
109 // The difference between raising SIGTSTP vs. SIGSTOP can be seen on
110 // the shell command line too by running "echo $?" after stopping
111 // a process but perhaps that doesn't matter.
112 const uint64_t t = mytime_now();
113 raise(SIGSTOP);
114 start_time += mytime_now() - t;
115 return;
116 }
117 #endif
118
119
120 extern void
mytime_set_start_time(void)121 mytime_set_start_time(void)
122 {
123 #ifdef USE_SIGTSTP_HANDLER
124 // Block the signals when accessing start_time so that we cannot
125 // end up with a garbage value. start_time is volatile but access
126 // to it isn't atomic at least on 32-bit systems.
127 signals_block();
128 #endif
129
130 start_time = mytime_now();
131
132 #ifdef USE_SIGTSTP_HANDLER
133 signals_unblock();
134 #endif
135
136 return;
137 }
138
139
140 extern uint64_t
mytime_get_elapsed(void)141 mytime_get_elapsed(void)
142 {
143 #ifdef USE_SIGTSTP_HANDLER
144 signals_block();
145 #endif
146
147 const uint64_t t = mytime_now() - start_time;
148
149 #ifdef USE_SIGTSTP_HANDLER
150 signals_unblock();
151 #endif
152
153 return t;
154 }
155
156
157 extern void
mytime_set_flush_time(void)158 mytime_set_flush_time(void)
159 {
160 next_flush = mytime_now() + opt_flush_timeout;
161 return;
162 }
163
164
165 extern int
mytime_get_flush_timeout(void)166 mytime_get_flush_timeout(void)
167 {
168 if (opt_flush_timeout == 0 || opt_mode != MODE_COMPRESS)
169 return -1;
170
171 const uint64_t now = mytime_now();
172 if (now >= next_flush)
173 return 0;
174
175 const uint64_t remaining = next_flush - now;
176 return remaining > INT_MAX ? INT_MAX : (int)remaining;
177 }
178