/* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef NDB_TICK_H #define NDB_TICK_H #include #include void NdbTick_Init(); /** * NDB_TICKS is a high resolution monotonic timer representing * timer 'ticks' from some epoch start like boot time, 1/1 -1970 or * whatever. * Its actual resolution and duration of a 'tick' is platform * dependent. Make no assumption about it representing a specific time. * Functions are provided to compare ticks and calculate time * interval between ticks * * NOTE: Even if the platform specific implementation of 'ticks' * should be in nanoseconds, the 64bit NDB_TICK will not wrap until * ~585 years has passed. So it should be pretty safe.... */ typedef struct NDB_TICKS { Uint64 t; public: NDB_TICKS() { t = 0; } /** * Provide functionality for fetch and reconstruct of tick value. * Usefull when a 'tick' is sent as part of a signal, or when * the clock is used to generate a pseudo random number. */ Uint64 getUint64() const { return t; } explicit NDB_TICKS(Uint64 val) { t = val; } } NDB_TICKS; /** * Returns whether the 'ticks' are provided by a monotonic timer. * Must be called after NdbTick_Init() */ bool NdbTick_IsMonotonic(); /** * Returns number of 'ticks' since some * platforms dependent epoch start. */ const NDB_TICKS NdbTick_getCurrentTicks(void); /** * Add specified number of milliseconds to a 'ticks' value. */ const NDB_TICKS NdbTick_AddMilliseconds(NDB_TICKS ticks, Uint64 ms); static void NdbTick_Invalidate(NDB_TICKS *ticks); static int NdbTick_IsValid(NDB_TICKS ticks); /** * Compare ticks and return an integer greater than, * equal to, or less than 0, if the 'tick value' in t1 * is greater than, equal to, or less than the t2 tick * respectively. */ static int NdbTick_Compare(NDB_TICKS t1, NDB_TICKS t2); /** * Get time elapsed between start and end time. */ static const class NdbDuration NdbTick_Elapsed(NDB_TICKS start, NDB_TICKS end); /** * Returns the current millisecond since some epoch start. * * Treat this function as deprecated. Elapsed time intervals * should be calculated by using the pattern * start/end = NdbTick_getCurrentTicks() and * elapsed = NdbTick_Elapsed...(start,end). * * All usage except in test utilties, should be considdered * a bug. */ static Uint64 NdbTick_CurrentMillisecond(void); class NdbDuration { public: Uint64 seconds() const; Uint64 milliSec() const; Uint64 microSec() const; Uint64 nanoSec() const; private: Uint64 t; static Uint64 tick_frequency; friend const NdbDuration NdbTick_Elapsed(NDB_TICKS start, NDB_TICKS end); friend Uint64 NdbTick_CurrentMillisecond(void); friend const NDB_TICKS NdbTick_AddMilliseconds(NDB_TICKS ticks, Uint64 ms); friend void NdbTick_Init(); NdbDuration(Uint64 ticks) : t(ticks) {} }; //class NdbDuration /****************************************************** * Implementation of NdbTick_foo functions. ******************************************************/ inline void NdbTick_Invalidate(NDB_TICKS *ticks) { ticks->t = 0; } static inline int NdbTick_IsValid(NDB_TICKS ticks) { return(ticks.t != 0); } static inline int NdbTick_Compare(NDB_TICKS t1, NDB_TICKS t2) { assert(NdbTick_IsValid(t1)); assert(NdbTick_IsValid(t2)); return (t1.t > t2.t) ? 1 :(t1.t < t2.t) ? -1 : 0; } static inline const NdbDuration NdbTick_Elapsed(NDB_TICKS start, NDB_TICKS end) { assert(NdbTick_IsValid(start)); assert(NdbTick_IsValid(end)); if (end.t >= start.t) { return NdbDuration(end.t - start.t); } /** * Clock has ticked backwards! * We protect agains backward leaping timers by returning 0 * if detected. This is less harmfull than returning a huge * Uint64 which would be the result of that subtraction. * Even the monotonic clock is known buggy * on some older BIOS and virtualized platforms. */ else if (NdbTick_IsMonotonic()) { /* Don't accept more than 10ms 'noise' if monotonic */ assert(NdbDuration(start.t-end.t).milliSec() <= 10); } return NdbDuration(0); } static inline Uint64 NdbTick_CurrentMillisecond(void) { const Uint64 ticks = NdbTick_getCurrentTicks().t; if (ticks < (UINT_MAX64 / 1000)) return ((ticks*1000) / NdbDuration::tick_frequency); // Best precision else return (ticks / (NdbDuration::tick_frequency/1000)); // Avoids oveflow, } /****************************************************** * Implementation of NdbDuration methods. * * In order to avoid precision loss, we multiply ticks * by the scale factor before dividing by the frequency. ******************************************************/ inline Uint64 NdbDuration::seconds() const { return (t / tick_frequency); } inline Uint64 NdbDuration::milliSec() const { assert(t < (UINT_MAX64 / 1000)); //Overflow? return ((t*1000) / tick_frequency); } /** * To avoid overflow in intermediate results when * multiplying 'tick' (t) with micro- or nanoScale * factor below, we handle the tick conversion in a * 'second' and a 'fraction' (of seconds) part. */ inline Uint64 NdbDuration::microSec() const { static const Uint64 microScale = 1000*1000; const Uint64 seconds = (t / tick_frequency); const Uint64 fraction = (t % tick_frequency); const Uint64 microsec = ((fraction*microScale) / tick_frequency); return microsec + (seconds*microScale); } inline Uint64 NdbDuration::nanoSec() const { static const Uint64 nanoScale = 1000*1000*1000; const Uint64 seconds = (t / tick_frequency); const Uint64 fraction = (t % tick_frequency); const Uint64 nanosec = ((fraction*nanoScale) / tick_frequency); return nanosec + (seconds*nanoScale); } #endif