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_SYNCn_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.

◆ 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.

◆ 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.

◆ 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.

◆ 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.

◆ 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.

Function Documentation

◆ clock_sync_effective_offset()

static 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()

static 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()

static 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()

static 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: