Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
CLOCK_SYNC: client/server clock offset estimation

Data Structures

struct  N_CLOCK_SYNC
 clock synchronization estimator More...

Macros

#define N_CLOCK_SYNC_BURST_INTERVAL   0.5
 accelerated interval used until N_CLOCK_SYNC_BURST_SAMPLES real responses have been collected, a fresh estimator converges in a couple of seconds instead of minutes
#define N_CLOCK_SYNC_BURST_SAMPLES   5
 number of REAL samples below which the burst interval applies
#define N_CLOCK_SYNC_INTERVAL   3.0
 default interval between sync requests in seconds
#define N_CLOCK_SYNC_MAX_RTT   10.0
 responses with an RTT above this are rejected outright (retransmit storms / suspend-resume artefacts, not usable timing data)
#define N_CLOCK_SYNC_SAMPLE_COUNT   11
 number of samples for the median filter
#define N_CLOCK_SYNC_SLEW_SECONDS   1.0
 seconds over which n_clock_sync_server_time slews from the previous offset estimate to a new one (smoothstep).

Functions

static double clock_sync_effective_offset (const N_CLOCK_SYNC *cs, double local_now)
 effective (slewed) offset at local_now, pure function of the stored state so the const getter needs no mutation.
static void clock_sync_store_sample (N_CLOCK_SYNC *cs, double offset, double rtt, double local_now)
 store one (offset, rtt) sample and recompute the estimates.
static int cmp_double (const void *a, const void *b)
 comparison function for qsort on doubles
static double median_of (const double *samples, int count)
 compute the median of an array of doubles
void n_clock_sync_delete (N_CLOCK_SYNC **cs)
 free a clock sync estimator
void n_clock_sync_mark_sent (N_CLOCK_SYNC *cs, double local_now)
 mark that a sync request was just sent
N_CLOCK_SYNC * n_clock_sync_new (void)
 allocate and initialize a new clock sync estimator
int n_clock_sync_process_response (N_CLOCK_SYNC *cs, double client_send_time, double server_time, double local_now)
 record a sync response: client_send_time is the local time the request was sent, server_time is the server's timestamp from the response, local_now is the current local time when the response was received.
void n_clock_sync_seed (N_CLOCK_SYNC *cs, double offset, double rtt)
 Inject a synthetic (offset, rtt) sample, e.g.
double n_clock_sync_server_time (const N_CLOCK_SYNC *cs, double local_now)
 get estimated server time given a local time value.
int n_clock_sync_should_send (const N_CLOCK_SYNC *cs, double local_now)
 check if it's time to send a new sync request (returns TRUE/FALSE).

Detailed Description


Data Structure Documentation

◆ N_CLOCK_SYNC

struct N_CLOCK_SYNC

clock synchronization estimator

Examples
ex_clock_sync.c.

Definition at line 105 of file n_clock_sync.h.

Collaboration diagram for N_CLOCK_SYNC:
Data Fields
double estimated_offset add to local time to get estimated server time (raw best-RTT target, n_clock_sync_server_time applies the slew on top)
double estimated_rtt current estimated round-trip time
double last_sync_time local time of last sync request sent
double offset_change_time local time at which estimated_offset last changed
double offset_prev effective offset at the moment estimated_offset last changed, the slew blends offset_prev -> estimated_offset
double offset_samples[11] circular buffer of offset estimates
double rtt_samples[11] circular buffer of RTT values
int sample_count number of samples collected so far
int sample_index current write position in circular buffer
int synthetic TRUE while the buffer holds only a synthetic n_clock_sync_seed sample, flushed wholesale by the first real response so a low-synthetic-rtt seed can never pin the estimate on links whose real RTT exceeds the synthetic one.

Macro Definition Documentation

◆ N_CLOCK_SYNC_BURST_INTERVAL

#define N_CLOCK_SYNC_BURST_INTERVAL   0.5

accelerated interval used until N_CLOCK_SYNC_BURST_SAMPLES real responses have been collected, a fresh estimator converges in a couple of seconds instead of minutes

Definition at line 83 of file n_clock_sync.h.

Referenced by n_clock_sync_should_send().

◆ N_CLOCK_SYNC_BURST_SAMPLES

#define N_CLOCK_SYNC_BURST_SAMPLES   5

number of REAL samples below which the burst interval applies

Definition at line 88 of file n_clock_sync.h.

Referenced by n_clock_sync_should_send().

◆ N_CLOCK_SYNC_INTERVAL

#define N_CLOCK_SYNC_INTERVAL   3.0

default interval between sync requests in seconds

Definition at line 76 of file n_clock_sync.h.

Referenced by n_clock_sync_new(), and n_clock_sync_should_send().

◆ N_CLOCK_SYNC_MAX_RTT

#define N_CLOCK_SYNC_MAX_RTT   10.0

responses with an RTT above this are rejected outright (retransmit storms / suspend-resume artefacts, not usable timing data)

Definition at line 94 of file n_clock_sync.h.

Referenced by n_clock_sync_process_response().

◆ N_CLOCK_SYNC_SAMPLE_COUNT

#define N_CLOCK_SYNC_SAMPLE_COUNT   11

number of samples for the median filter

Definition at line 71 of file n_clock_sync.h.

Referenced by clock_sync_store_sample(), and median_of().

◆ N_CLOCK_SYNC_SLEW_SECONDS

#define N_CLOCK_SYNC_SLEW_SECONDS   1.0

seconds over which n_clock_sync_server_time slews from the previous offset estimate to a new one (smoothstep).

A consumer that anchors motion on this clock sees a bounded rate change instead of a step.

Definition at line 101 of file n_clock_sync.h.

Referenced by clock_sync_effective_offset().

Function Documentation

◆ clock_sync_effective_offset()

double clock_sync_effective_offset ( const N_CLOCK_SYNC * cs,
double local_now )
static

effective (slewed) offset at local_now, pure function of the stored state so the const getter needs no mutation.

Blends offset_prev -> estimated_offset with a smoothstep over N_CLOCK_SYNC_SLEW_SECONDS from the moment the estimate last changed.

Definition at line 90 of file n_clock_sync.c.

References N_CLOCK_SYNC::estimated_offset, N_CLOCK_SYNC_SLEW_SECONDS, N_CLOCK_SYNC::offset_change_time, and N_CLOCK_SYNC::offset_prev.

Referenced by clock_sync_store_sample(), and n_clock_sync_server_time().

Here is the caller graph for this function:

◆ clock_sync_store_sample()

void clock_sync_store_sample ( N_CLOCK_SYNC * cs,
double offset,
double rtt,
double local_now )
static

store one (offset, rtt) sample and recompute the estimates.

Offset selection is NTP-style: the sample with the LOWEST rtt wins, because its one-way uncertainty (+/-rtt/2) is the smallest. A plain median is poisoned for minutes when the link is congested at startup (e.g. an MMO client receiving its login chunk burst: every early sample carries seconds of queueing delay, and the median needs a majority of clean samples to recover). estimated_rtt stays a median, it feeds timeout heuristics, where typical beats best.

local_now anchors the slew when the adopted offset changes: n_clock_sync_server_time blends from the previous effective offset instead of stepping.

Definition at line 113 of file n_clock_sync.c.

References clock_sync_effective_offset(), N_CLOCK_SYNC::estimated_offset, N_CLOCK_SYNC::estimated_rtt, median_of(), N_CLOCK_SYNC_SAMPLE_COUNT, N_CLOCK_SYNC::offset_change_time, N_CLOCK_SYNC::offset_prev, N_CLOCK_SYNC::offset_samples, N_CLOCK_SYNC::rtt_samples, N_CLOCK_SYNC::sample_count, and N_CLOCK_SYNC::sample_index.

Referenced by n_clock_sync_process_response(), and n_clock_sync_seed().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ cmp_double()

int cmp_double ( const void * a,
const void * b )
static

comparison function for qsort on doubles

Definition at line 40 of file n_clock_sync.c.

Referenced by median_of().

Here is the caller graph for this function:

◆ median_of()

double median_of ( const double * samples,
int count )
static

compute the median of an array of doubles

Definition at line 49 of file n_clock_sync.c.

References cmp_double(), and N_CLOCK_SYNC_SAMPLE_COUNT.

Referenced by clock_sync_store_sample().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ n_clock_sync_delete()

void n_clock_sync_delete ( N_CLOCK_SYNC ** cs)

free a clock sync estimator

Examples
ex_clock_sync.c.

Definition at line 79 of file n_clock_sync.c.

References __n_assert, and Free.

Referenced by run_client().

Here is the caller graph for this function:

◆ n_clock_sync_mark_sent()

void n_clock_sync_mark_sent ( N_CLOCK_SYNC * cs,
double local_now )

mark that a sync request was just sent

Examples
ex_clock_sync.c.

Definition at line 199 of file n_clock_sync.c.

References __n_assert, and N_CLOCK_SYNC::last_sync_time.

Referenced by run_client().

Here is the caller graph for this function:

◆ n_clock_sync_new()

N_CLOCK_SYNC * n_clock_sync_new ( void )

allocate and initialize a new clock sync estimator

Examples
ex_clock_sync.c.

Definition at line 63 of file n_clock_sync.c.

References __n_assert, N_CLOCK_SYNC::estimated_offset, N_CLOCK_SYNC::estimated_rtt, N_CLOCK_SYNC::last_sync_time, Malloc, N_CLOCK_SYNC_INTERVAL, N_CLOCK_SYNC::sample_count, and N_CLOCK_SYNC::sample_index.

Referenced by run_client().

Here is the caller graph for this function:

◆ n_clock_sync_process_response()

int n_clock_sync_process_response ( N_CLOCK_SYNC * cs,
double client_send_time,
double server_time,
double local_now )

record a sync response: client_send_time is the local time the request was sent, server_time is the server's timestamp from the response, local_now is the current local time when the response was received.

Updates estimated_offset and estimated_rtt. Returns TRUE on success, FALSE on error.

Examples
ex_clock_sync.c.

Definition at line 136 of file n_clock_sync.c.

References __n_assert, clock_sync_store_sample(), N_CLOCK_SYNC::estimated_offset, N_CLOCK_SYNC::estimated_rtt, LOG_DEBUG, LOG_ERR, LOG_WARNING, N_CLOCK_SYNC_MAX_RTT, n_log, N_CLOCK_SYNC::sample_count, N_CLOCK_SYNC::sample_index, and N_CLOCK_SYNC::synthetic.

Referenced by run_client().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ n_clock_sync_seed()

void n_clock_sync_seed ( N_CLOCK_SYNC * cs,
double offset,
double rtt )

Inject a synthetic (offset, rtt) sample, e.g.

a server-supplied clock value at login, so the estimator is usable BEFORE the first request/response round-trip completes. Pass the believed offset (server_time - local_now) and a conservative synthetic rtt. RESETS the estimator to this single sample and adopts the offset immediately (no slew, at login there is nothing to slew from). The FIRST real response flushes the seed wholesale, so a seed with a synthetic rtt lower than the link's real RTT can never pin the estimate (offset selection is best-rtt, NTP-style).

Definition at line 170 of file n_clock_sync.c.

References __n_assert, clock_sync_store_sample(), N_CLOCK_SYNC::estimated_offset, LOG_DEBUG, n_log, N_CLOCK_SYNC::offset_change_time, N_CLOCK_SYNC::offset_prev, N_CLOCK_SYNC::sample_count, N_CLOCK_SYNC::sample_index, and N_CLOCK_SYNC::synthetic.

Here is the call graph for this function:

◆ n_clock_sync_server_time()

double n_clock_sync_server_time ( const N_CLOCK_SYNC * cs,
double local_now )

get estimated server time given a local time value.

When the underlying offset estimate changes, the returned time SLEWS from the previous estimate to the new one over N_CLOCK_SYNC_SLEW_SECONDS (smoothstep) instead of stepping, pure function of the stored state, safe to call at any rate.

Examples
ex_clock_sync.c.

Definition at line 184 of file n_clock_sync.c.

References __n_assert, and clock_sync_effective_offset().

Referenced by run_client().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ n_clock_sync_should_send()

int n_clock_sync_should_send ( const N_CLOCK_SYNC * cs,
double local_now )

check if it's time to send a new sync request (returns TRUE/FALSE).

Uses N_CLOCK_SYNC_BURST_INTERVAL until N_CLOCK_SYNC_BURST_SAMPLES real responses have been collected, then N_CLOCK_SYNC_INTERVAL.

Examples
ex_clock_sync.c.

Definition at line 189 of file n_clock_sync.c.

References __n_assert, N_CLOCK_SYNC::last_sync_time, N_CLOCK_SYNC_BURST_INTERVAL, N_CLOCK_SYNC_BURST_SAMPLES, N_CLOCK_SYNC_INTERVAL, N_CLOCK_SYNC::sample_count, and N_CLOCK_SYNC::synthetic.

Referenced by run_client().

Here is the caller graph for this function: