xref: /freebsd-src/contrib/xz/src/xz/mytime.c (revision 26743408e9ff53ac0e041407c359ed3c17c15596)
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
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
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
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
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
158 mytime_set_flush_time(void)
159 {
160 	next_flush = mytime_now() + opt_flush_timeout;
161 	return;
162 }
163 
164 
165 extern int
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