polardbxengine/storage/ndb/include/portlib/NdbTick.h

264 lines
6.8 KiB
C++

/*
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 <assert.h>
#include <ndb_types.h>
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