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