/*
* %CopyrightBegin%
*
* Copyright Ericsson AB 2006-2011. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* %CopyrightEnd%
*/
#ifndef ERL_TIME_H__
#define ERL_TIME_H__
/* timer wheel size NEED to be a power of 2 */
#ifdef SMALL_MEMORY
#define ERTS_TIW_SIZE (1 << 13)
#else
#define ERTS_TIW_SIZE (1 << 16)
#endif
#if defined(DEBUG) || 0
#define ERTS_TIME_ASSERT(B) ERTS_ASSERT(B)
#else
#define ERTS_TIME_ASSERT(B) ((void) 1)
#endif
typedef enum {
ERTS_NO_TIME_WARP_MODE,
ERTS_SINGLE_TIME_WARP_MODE,
ERTS_MULTI_TIME_WARP_MODE
} ErtsTimeWarpMode;
typedef struct ErtsTimerWheel_ ErtsTimerWheel;
typedef ErtsMonotonicTime * ErtsNextTimeoutRef;
extern SysTimeval erts_first_emu_time;
void erts_monitor_time_offset(Eterm id, Eterm ref);
int erts_demonitor_time_offset(Eterm ref);
int erts_init_time_sup(int, ErtsTimeWarpMode);
void erts_late_init_time_sup(void);
ErtsNextTimeoutRef erts_get_next_timeout_reference(ErtsTimerWheel *);
void erts_init_time(int time_correction, ErtsTimeWarpMode time_warp_mode);
void erts_bump_timers(ErtsTimerWheel *, ErtsMonotonicTime);
Uint erts_timer_wheel_memory_size(void);
#ifdef DEBUG
void erts_p_slpq(void);
#endif
/* time_sup */
#if (defined(HAVE_GETHRVTIME) || defined(HAVE_CLOCK_GETTIME_CPU_TIME))
# ifndef HAVE_ERTS_NOW_CPU
# define HAVE_ERTS_NOW_CPU
# ifdef HAVE_GETHRVTIME
# define erts_start_now_cpu() sys_start_hrvtime()
# define erts_stop_now_cpu() sys_stop_hrvtime()
# endif
# endif
void erts_get_now_cpu(Uint* megasec, Uint* sec, Uint* microsec);
#endif
typedef UWord erts_approx_time_t;
erts_approx_time_t erts_get_approx_time(void);
int erts_has_time_correction(void);
int erts_check_time_adj_support(int time_correction,
ErtsTimeWarpMode time_warp_mode);
ErtsTimeWarpMode erts_time_warp_mode(void);
typedef enum {
ERTS_TIME_OFFSET_PRELIMINARY,
ERTS_TIME_OFFSET_FINAL,
ERTS_TIME_OFFSET_VOLATILE
} ErtsTimeOffsetState;
ErtsTimeOffsetState erts_time_offset_state(void);
ErtsTimeOffsetState erts_finalize_time_offset(void);
struct process;
Eterm erts_get_monotonic_start_time(struct process *c_p);
Eterm erts_get_monotonic_end_time(struct process *c_p);
Eterm erts_monotonic_time_source(struct process*c_p);
Eterm erts_system_time_source(struct process*c_p);
#ifdef SYS_CLOCK_RESOLUTION
#define ERTS_CLKTCK_RESOLUTION ((ErtsMonotonicTime) (SYS_CLOCK_RESOLUTION*1000))
#else
#define ERTS_CLKTCK_RESOLUTION (erts_time_sup__.r.o.clktck_resolution)
#endif
#define ERTS_TIMER_WHEEL_MSEC (ERTS_TIW_SIZE/(ERTS_CLKTCK_RESOLUTION/1000))
struct erts_time_sup_read_only__ {
ErtsMonotonicTime monotonic_time_unit;
#if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT
ErtsMonotonicTime start;
struct {
ErtsMonotonicTime native;
ErtsMonotonicTime nsec;
ErtsMonotonicTime usec;
ErtsMonotonicTime msec;
ErtsMonotonicTime sec;
} start_offset;
#endif
#ifndef SYS_CLOCK_RESOLUTION
ErtsMonotonicTime clktck_resolution;
#endif
};
typedef struct {
union {
struct erts_time_sup_read_only__ o;
char align__[(((sizeof(struct erts_time_sup_read_only__) - 1)
/ ASSUMED_CACHE_LINE_SIZE) + 1)
* ASSUMED_CACHE_LINE_SIZE];
} r;
} ErtsTimeSupData;
extern ErtsTimeSupData erts_time_sup__;
ERTS_GLB_INLINE Uint64
erts_time_unit_conversion(Uint64 value,
Uint32 from_time_unit,
Uint32 to_time_unit);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
ERTS_GLB_INLINE Uint64
erts_time_unit_conversion(Uint64 value,
Uint32 from_time_unit,
Uint32 to_time_unit)
{
Uint64 high, low, result;
if (value <= ~((Uint64) 0)/to_time_unit)
return (value*to_time_unit)/from_time_unit;
low = value & ((Uint64) 0xffffffff);
high = (value >> 32) & ((Uint64) 0xffffffff);
low *= to_time_unit;
high *= to_time_unit;
high += (low >> 32) & ((Uint64) 0xffffffff);
low &= ((Uint64) 0xffffffff);
result = high % from_time_unit;
high /= from_time_unit;
high <<= 32;
result <<= 32;
result += low;
result /= from_time_unit;
result += high;
return result;
}
#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
/*
* Range of monotonic time internally
*/
#define ERTS_MONOTONIC_BEGIN \
ERTS_MONOTONIC_TIME_UNIT
#define ERTS_MONOTONIC_END \
((ERTS_MONOTONIC_TIME_MAX / ERTS_MONOTONIC_TIME_UNIT) \
* ERTS_MONOTONIC_TIME_UNIT)
#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT
/*
* If the monotonic time unit is a compile time constant,
* it is assumed (and need) to be a power of 10.
*/
#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT < 1000*1000
# error Compile time time unit needs to be at least 1000000
#endif
#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT == 1000*1000*1000
/* Nano-second time unit */
#define ERTS_MONOTONIC_TO_SEC__(NSEC) ((NSEC) / (1000*1000*1000))
#define ERTS_MONOTONIC_TO_MSEC__(NSEC) ((NSEC) / (1000*1000))
#define ERTS_MONOTONIC_TO_USEC__(NSEC) ((NSEC) / 1000)
#define ERTS_MONOTONIC_TO_NSEC__(NSEC) (NSEC)
#define ERTS_SEC_TO_MONOTONIC__(SEC) (((ErtsMonotonicTime) (SEC))*(1000*1000*1000))
#define ERTS_MSEC_TO_MONOTONIC__(MSEC) (((ErtsMonotonicTime) (MSEC))*(1000*1000))
#define ERTS_USEC_TO_MONOTONIC__(USEC) (((ErtsMonotonicTime) (USEC))*1000)
#define ERTS_NSEC_TO_MONOTONIC__(NSEC) ((ErtsMonotonicTime) (NSEC))
#elif ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT == 1000*1000
/* Micro-second time unit */
#define ERTS_MONOTONIC_TO_SEC__(USEC) ((USEC) / (1000*1000))
#define ERTS_MONOTONIC_TO_MSEC__(USEC) ((USEC) / 1000)
#define ERTS_MONOTONIC_TO_USEC__(USEC) (USEC)
#define ERTS_MONOTONIC_TO_NSEC__(USEC) ((USEC)*1000)
#define ERTS_SEC_TO_MONOTONIC__(SEC) (((ErtsMonotonicTime) (SEC))*(1000*1000))
#define ERTS_MSEC_TO_MONOTONIC__(MSEC) (((ErtsMonotonicTime) (MSEC))*1000)
#define ERTS_USEC_TO_MONOTONIC__(USEC) ((ErtsMonotonicTime) (USEC))
#define ERTS_NSEC_TO_MONOTONIC__(NSEC) (((ErtsMonotonicTime) (NSEC))/1000)
#else
#error Missing implementation for monotonic time unit
#endif
#define ERTS_MONOTONIC_TIME_UNIT \
((ErtsMonotonicTime) ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT)
/*
* NOTE! ERTS_MONOTONIC_TIME_START_EXTERNAL *need* to be a multiple
* of ERTS_MONOTONIC_TIME_UNIT.
*/
#ifdef ARCH_32
/*
* Want to use a big-num of arity 2 as long as possible (584 years
* in the nano-second time unit case).
*/
#define ERTS_MONOTONIC_TIME_START_EXTERNAL \
(((((((ErtsMonotonicTime) 1) << 32)-1) \
/ ERTS_MONOTONIC_TIME_UNIT) \
* ERTS_MONOTONIC_TIME_UNIT) \
+ ERTS_MONOTONIC_TIME_UNIT)
#else /* ARCH_64 */
#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT <= 10*1000*1000
/*
* Using micro second time unit or lower. Start at zero since
* time will remain an immediate for a very long time anyway
* (1827 years in the 10 micro second case)...
*/
#define ERTS_MONOTONIC_TIME_START_EXTERNAL ((ErtsMonotonicTime) 0)
#else /* ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT > 10*1000*1000 */
/*
* Want to use an immediate as long as possible (36 years in the
* nano-second time unit case).
*/
#define ERTS_MONOTONIC_TIME_START_EXTERNAL \
((((ErtsMonotonicTime) MIN_SMALL) \
/ ERTS_MONOTONIC_TIME_UNIT) \
* ERTS_MONOTONIC_TIME_UNIT)
#endif /* ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT > 1000*1000 */
#endif /* ARCH_64 */
/*
* Offsets from internal monotonic time to external monotonic time
*/
#define ERTS_MONOTONIC_OFFSET_NATIVE \
(ERTS_MONOTONIC_TIME_START_EXTERNAL - ERTS_MONOTONIC_BEGIN)
#define ERTS_MONOTONIC_OFFSET_NSEC \
ERTS_MONOTONIC_TO_NSEC__(ERTS_MONOTONIC_OFFSET_NATIVE)
#define ERTS_MONOTONIC_OFFSET_USEC \
ERTS_MONOTONIC_TO_USEC__(ERTS_MONOTONIC_OFFSET_NATIVE)
#define ERTS_MONOTONIC_OFFSET_MSEC \
ERTS_MONOTONIC_TO_MSEC__(ERTS_MONOTONIC_OFFSET_NATIVE)
#define ERTS_MONOTONIC_OFFSET_SEC \
ERTS_MONOTONIC_TO_SEC__(ERTS_MONOTONIC_OFFSET_NATIVE)
#define ERTS_MONOTONIC_TO_CLKTCKS__(MON) \
((MON) / (ERTS_MONOTONIC_TIME_UNIT/ERTS_CLKTCK_RESOLUTION))
#define ERTS_CLKTCKS_TO_MONOTONIC__(TCKS) \
((TCKS) * (ERTS_MONOTONIC_TIME_UNIT/ERTS_CLKTCK_RESOLUTION))
#else /* !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT */
/*
* Initialized in erts_init_sys_time_sup()
*/
#define ERTS_MONOTONIC_TIME_UNIT (erts_time_sup__.r.o.monotonic_time_unit)
/*
* Offsets from internal monotonic time to external monotonic time
*
* Initialized in erts_init_time_sup()...
*/
#define ERTS_MONOTONIC_TIME_START_EXTERNAL (erts_time_sup__.r.o.start)
#define ERTS_MONOTONIC_OFFSET_NATIVE (erts_time_sup__.r.o.start_offset.native)
#define ERTS_MONOTONIC_OFFSET_NSEC (erts_time_sup__.r.o.start_offset.nsec)
#define ERTS_MONOTONIC_OFFSET_USEC (erts_time_sup__.r.o.start_offset.usec)
#define ERTS_MONOTONIC_OFFSET_MSEC (erts_time_sup__.r.o.start_offset.msec)
#define ERTS_MONOTONIC_OFFSET_SEC (erts_time_sup__.r.o.start_offset.sec)
#define ERTS_CONV_FROM_MON_UNIT___(M, TO) \
((ErtsMonotonicTime) \
erts_time_unit_conversion((Uint64) (M), \
(Uint32) ERTS_MONOTONIC_TIME_UNIT, \
(Uint32) (TO)))
#define ERTS_CONV_TO_MON_UNIT___(M, FROM) \
((ErtsMonotonicTime) \
erts_time_unit_conversion((Uint64) (M), \
(Uint32) (FROM), \
(Uint32) ERTS_MONOTONIC_TIME_UNIT)) \
#define ERTS_MONOTONIC_TO_SEC__(M) \
ERTS_CONV_FROM_MON_UNIT___((M), 1)
#define ERTS_MONOTONIC_TO_MSEC__(M) \
ERTS_CONV_FROM_MON_UNIT___((M), 1000)
#define ERTS_MONOTONIC_TO_USEC__(M) \
ERTS_CONV_FROM_MON_UNIT___((M), 1000*1000)
#define ERTS_MONOTONIC_TO_NSEC__(M) \
ERTS_CONV_FROM_MON_UNIT___((M), 1000*1000*1000)
#define ERTS_SEC_TO_MONOTONIC__(SEC) \
ERTS_CONV_TO_MON_UNIT___((SEC), 1)
#define ERTS_MSEC_TO_MONOTONIC__(MSEC) \
ERTS_CONV_TO_MON_UNIT___((MSEC), 1000)
#define ERTS_USEC_TO_MONOTONIC__(USEC) \
ERTS_CONV_TO_MON_UNIT___((USEC), 1000*1000)
#define ERTS_NSEC_TO_MONOTONIC__(NSEC) \
ERTS_CONV_TO_MON_UNIT___((NSEC), 1000*1000*1000)
#define ERTS_MONOTONIC_TO_CLKTCKS__(MON) \
ERTS_CONV_FROM_MON_UNIT___((MON), ERTS_CLKTCK_RESOLUTION)
#define ERTS_CLKTCKS_TO_MONOTONIC__(TCKS) \
ERTS_CONV_TO_MON_UNIT___((TCKS), ERTS_CLKTCK_RESOLUTION)
#endif /* !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT */
#define ERTS_MONOTONIC_TIME_END_EXTERNAL \
(ERTS_MONOTONIC_TIME_START_EXTERNAL < 0 \
? (ERTS_MONOTONIC_TIME_START_EXTERNAL \
+ (ERTS_MONOTONIC_END - ERTS_MONOTONIC_BEGIN)) \
: (ERTS_MONOTONIC_END - ERTS_MONOTONIC_TIME_START_EXTERNAL))
#define ERTS_MSEC_TO_CLKTCKS__(MON) \
((MON) * (ERTS_CLKTCK_RESOLUTION/1000))
#define ERTS_CLKTCKS_TO_MSEC__(TCKS) \
((TCKS) / (ERTS_CLKTCK_RESOLUTION/1000))
#define ERTS_MONOTONIC_TO_SEC(X) \
(ERTS_TIME_ASSERT((X) >= 0), \
ERTS_MONOTONIC_TO_SEC__((X)))
#define ERTS_MONOTONIC_TO_MSEC(X) \
(ERTS_TIME_ASSERT((X) >= 0), \
ERTS_MONOTONIC_TO_MSEC__((X)))
#define ERTS_MONOTONIC_TO_USEC(X) \
(ERTS_TIME_ASSERT((X) >= 0), \
ERTS_MONOTONIC_TO_USEC__((X)))
#define ERTS_MONOTONIC_TO_NSEC(X) \
(ERTS_TIME_ASSERT((X) >= 0), \
ERTS_MONOTONIC_TO_NSEC__((X)))
#define ERTS_SEC_TO_MONOTONIC(X) \
(ERTS_TIME_ASSERT((X) >= 0), \
ERTS_SEC_TO_MONOTONIC__((X)))
#define ERTS_MSEC_TO_MONOTONIC(X) \
(ERTS_TIME_ASSERT((X) >= 0), \
ERTS_MSEC_TO_MONOTONIC__((X)))
#define ERTS_USEC_TO_MONOTONIC(X) \
(ERTS_TIME_ASSERT((X) >= 0), \
ERTS_USEC_TO_MONOTONIC__((X)))
#define ERTS_NSEC_TO_MONOTONIC(X) \
(ERTS_TIME_ASSERT((X) >= 0), \
ERTS_NSEC_TO_MONOTONIC__((X)))
#define ERTS_MONOTONIC_TO_CLKTCKS(X) \
(ERTS_TIME_ASSERT((X) >= 0), \
ERTS_MONOTONIC_TO_CLKTCKS__((X)))
#define ERTS_CLKTCKS_TO_MONOTONIC(X) \
(ERTS_TIME_ASSERT((X) >= 0), \
ERTS_CLKTCKS_TO_MONOTONIC__((X)))
#define ERTS_MSEC_TO_CLKTCKS(X) \
(ERTS_TIME_ASSERT((X) >= 0), \
ERTS_MSEC_TO_CLKTCKS__((X)))
#define ERTS_CLKTCKS_TO_MSEC(X) \
(ERTS_TIME_ASSERT((X) >= 0), \
ERTS_CLKTCKS_TO_MSEC__((X)))
#endif /* ERL_TIME_H__ */
/* timer-wheel api */
#if defined(ERTS_WANT_TIMER_WHEEL_API) && !defined(ERTS_GOT_TIMER_WHEEL_API)
#define ERTS_GOT_TIMER_WHEEL_API
#include "erl_thr_progress.h"
#include "erl_process.h"
void erts_sched_init_time_sup(ErtsSchedulerData *esdp);
#define ERTS_TWHEEL_SLOT_AT_ONCE -1
#define ERTS_TWHEEL_SLOT_INACTIVE -2
/*
** Timer entry:
*/
typedef struct erl_timer {
struct erl_timer* next; /* next entry tiw slot or chain */
struct erl_timer* prev; /* prev entry tiw slot or chain */
union {
struct {
void (*timeout)(void*); /* called when timeout */
void (*cancel)(void*); /* called when cancel (may be NULL) */
void* arg; /* argument to timeout/cancel procs */
} func;
ErtsThrPrgrLaterOp cleanup;
} u;
ErtsMonotonicTime timeout_pos; /* Timeout in absolute clock ticks */
int slot;
} ErtsTWheelTimer;
typedef void (*ErlTimeoutProc)(void*);
typedef void (*ErlCancelProc)(void*);
void erts_twheel_set_timer(ErtsTimerWheel *tiw,
ErtsTWheelTimer *p, ErlTimeoutProc timeout,
ErlCancelProc cancel, void *arg,
ErtsMonotonicTime timeout_pos);
void erts_twheel_cancel_timer(ErtsTimerWheel *tiw, ErtsTWheelTimer *p);
ErtsTimerWheel *erts_create_timer_wheel(ErtsSchedulerData *esdp);
ErtsMonotonicTime erts_check_next_timeout_time(ErtsSchedulerData *);
ERTS_GLB_INLINE void erts_twheel_init_timer(ErtsTWheelTimer *p);
ERTS_GLB_INLINE ErtsMonotonicTime erts_next_timeout_time(ErtsNextTimeoutRef);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
ERTS_GLB_INLINE void erts_twheel_init_timer(ErtsTWheelTimer *p)
{
p->slot = ERTS_TWHEEL_SLOT_INACTIVE;
}
ERTS_GLB_INLINE ErtsMonotonicTime erts_next_timeout_time(ErtsNextTimeoutRef nxt_tmo_ref)
{
return *((ErtsMonotonicTime *) nxt_tmo_ref);
}
#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
void
erts_twheel_debug_foreach(ErtsTimerWheel *tiw,
void (*tclbk)(void *),
void (*func)(void *,
ErtsMonotonicTime,
void *),
void *arg);
#endif /* timer wheel api */