1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #ifndef SPRINGTIME_H
4 #define SPRINGTIME_H
5 
6 #include "System/creg/creg_cond.h"
7 
8 #include <boost/cstdint.hpp>
9 #include <cassert>
10 
11 // glibc's chrono is non monotonic/not steady atm (it depends on set timezone and can change at runtime!)
12 // we don't want to specially handle all the problems caused by this, so just use boost version instead
13 // not possible either: boost::chrono uses extremely broken HPE timers on Windows 7 (but so does std::)
14 //
15 // #define FORCE_CHRONO_TIMERS
16 // #define FORCE_BOOST_CHRONO
17 
18 #if (__cplusplus > 199711L) && !defined(FORCE_BOOST_CHRONO)
19 	#define SPRINGTIME_USING_STDCHRONO
20 	#undef gt
21 	#include <chrono>
22 	namespace chrono { using namespace std::chrono; }
23 #else
24 	#define SPRINGTIME_USING_BOOST
25 	#undef gt
26 	#include <boost/chrono/include.hpp>
27 	namespace chrono { using namespace boost::chrono; };
28 #endif
29 
30 
31 
32 
33 
34 namespace spring_clock {
35 	// NOTE:
36 	//   1e-x are double-precision literals but T can be float
37 	//   floats only provide ~6 decimal digits of precision so
38 	//   ToSecs is inaccurate in that case
39 	//   these cannot be written as integer divisions or tests
40 	//   will fail because of intermediate conversions to FP32
ToSecs(const boost::int64_t ns)41 	template<typename T> static T ToSecs     (const boost::int64_t ns) { return (ns * 1e-9); }
ToMilliSecs(const boost::int64_t ns)42 	template<typename T> static T ToMilliSecs(const boost::int64_t ns) { return (ns * 1e-6); }
ToMicroSecs(const boost::int64_t ns)43 	template<typename T> static T ToMicroSecs(const boost::int64_t ns) { return (ns * 1e-3); }
ToNanoSecs(const boost::int64_t ns)44 	template<typename T> static T ToNanoSecs (const boost::int64_t ns) { return (ns       ); }
45 
46 	// specializations
47 	template<> boost::int64_t ToSecs     <boost::int64_t>(const boost::int64_t ns) { return (ns / boost::int64_t(1e9)); }
48 	template<> boost::int64_t ToMilliSecs<boost::int64_t>(const boost::int64_t ns) { return (ns / boost::int64_t(1e6)); }
49 	template<> boost::int64_t ToMicroSecs<boost::int64_t>(const boost::int64_t ns) { return (ns / boost::int64_t(1e3)); }
50 
FromSecs(const T s)51 	template<typename T> static boost::int64_t FromSecs     (const T  s) { return ( s * boost::int64_t(1e9)); }
FromMilliSecs(const T ms)52 	template<typename T> static boost::int64_t FromMilliSecs(const T ms) { return (ms * boost::int64_t(1e6)); }
FromMicroSecs(const T us)53 	template<typename T> static boost::int64_t FromMicroSecs(const T us) { return (us * boost::int64_t(1e3)); }
FromNanoSecs(const T ns)54 	template<typename T> static boost::int64_t FromNanoSecs (const T ns) { return (ns                      ); }
55 
56 	void PushTickRate(bool hres = false);
57 	void PopTickRate();
58 
59 	// number of ticks since clock epoch
60 	boost::int64_t GetTicks();
61 	const char* GetName();
62 }
63 
64 
65 
66 // class Timer
67 struct spring_time {
68 private:
69 	CR_DECLARE_STRUCT(spring_time)
70 
71 	typedef boost::int64_t int64;
72 
73 public:
spring_timespring_time74 	spring_time(): x(0) {}
spring_timespring_time75 	template<typename T> explicit spring_time(const T millis): x(spring_clock::FromMilliSecs(millis)) {}
76 
77 	spring_time& operator+=(const spring_time st)       { x += st.x; return *this; }
78 	spring_time& operator-=(const spring_time st)       { x -= st.x; return *this; }
79 	spring_time& operator%=(const spring_time mt)       { x %= mt.x; return *this;    }
80 	spring_time   operator-(const spring_time st) const { return spring_time_native(x - st.x); }
81 	spring_time   operator+(const spring_time st) const { return spring_time_native(x + st.x); }
82 	spring_time   operator%(const spring_time mt) const { return spring_time_native(x % mt.x); }
83 	bool          operator<(const spring_time st) const { return (x <  st.x); }
84 	bool          operator>(const spring_time st) const { return (x >  st.x); }
85 	bool         operator<=(const spring_time st) const { return (x <= st.x); }
86 	bool         operator>=(const spring_time st) const { return (x >= st.x); }
87 
88 	// short-hands
toSecsispring_time89 	int64 toSecsi()        const { return (toSecs     <int64>()); }
toMilliSecsispring_time90 	int64 toMilliSecsi()   const { return (toMilliSecs<int64>()); }
toMicroSecsispring_time91 	int64 toMicroSecsi()   const { return (toMicroSecs<int64>()); }
toNanoSecsispring_time92 	int64 toNanoSecsi()    const { return (toNanoSecs <int64>()); }
93 
toSecsfspring_time94 	float toSecsf()      const { return (toSecs     <float>()); }
toMilliSecsfspring_time95 	float toMilliSecsf() const { return (toMilliSecs<float>()); }
toMicroSecsfspring_time96 	float toMicroSecsf() const { return (toMicroSecs<float>()); }
toNanoSecsfspring_time97 	float toNanoSecsf()  const { return (toNanoSecs <float>()); }
98 
99 	// wrappers
toSecsspring_time100 	template<typename T> T toSecs()      const { return spring_clock::ToSecs     <T>(x); }
toMilliSecsspring_time101 	template<typename T> T toMilliSecs() const { return spring_clock::ToMilliSecs<T>(x); }
toMicroSecsspring_time102 	template<typename T> T toMicroSecs() const { return spring_clock::ToMicroSecs<T>(x); }
toNanoSecsspring_time103 	template<typename T> T toNanoSecs()  const { return spring_clock::ToNanoSecs <T>(x); }
104 
105 
isDurationspring_time106 	bool isDuration() const { return (x != 0); }
isTimespring_time107 	bool isTime() const { return (x > 0); }
108 
109 	void sleep();
110 	void sleep_until();
111 
112 
113 	static spring_time gettime(bool init = false) { assert(xs != 0 || init); return spring_time_native(spring_clock::GetTicks()); }
getstarttimespring_time114 	static spring_time getstarttime() { assert(xs != 0); return spring_time_native(xs); }
getelapsedtimespring_time115 	static spring_time getelapsedtime() { return (gettime() - getstarttime()); }
116 
setstarttimespring_time117 	static void setstarttime(const spring_time t) { assert(xs == 0); xs = t.x; assert(xs != 0); }
118 
fromNanoSecsspring_time119 	static spring_time fromNanoSecs (const int64 ns) { return spring_time_native(spring_clock::FromNanoSecs( ns)); }
fromMicroSecsspring_time120 	static spring_time fromMicroSecs(const int64 us) { return spring_time_native(spring_clock::FromMicroSecs(us)); }
fromMilliSecsspring_time121 	static spring_time fromMilliSecs(const int64 ms) { return spring_time_native(spring_clock::FromMilliSecs(ms)); }
fromSecsspring_time122 	static spring_time fromSecs     (const int64  s) { return spring_time_native(spring_clock::FromSecs     ( s)); }
123 
124 private:
125 	// convert integer to spring_time (n is interpreted as number of nanoseconds)
spring_time_nativespring_time126 	static spring_time spring_time_native(const int64 n) { spring_time s; s.x = n; return s; }
127 
128 	void Serialize(creg::ISerializer& s);
129 
130 private:
131 	int64 x;
132 
133 	// initial time (the "Spring epoch", program start)
134 	// all other time-points *must* be larger than this
135 	// if the clock is monotonically increasing
136 	static int64 xs;
137 };
138 
139 
140 
141 static const spring_time spring_notime(0);
142 static const spring_time spring_nulltime(0);
143 
144 //#define spring_gettime()      spring_time::gettime()
145 #define spring_gettime()      spring_time::getelapsedtime()
146 #define spring_getstarttime() spring_time::getstarttime()
147 #define spring_now()          spring_time::getelapsedtime()
148 
149 #define spring_tomsecs(t) ((t).toMilliSecsi())
150 #define spring_istime(t) ((t).isTime())
151 #define spring_sleep(t) ((t).sleep())
152 
153 #define spring_msecs(msecs) spring_time(msecs)
154 #define spring_secs(secs) spring_time((secs) * 1000)
155 
156 
157 
158 
159 
160 #define spring_difftime(now, before)  (now - before)
161 #define spring_diffsecs(now, before)  ((now - before).toSecsi())
162 #define spring_diffmsecs(now, before) ((now - before).toMilliSecsi())
163 
164 #endif // SPRINGTIME_H
165