Home | History | Annotate | Line # | Download | only in xz
      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