Skip to content

Add Network Time Protocol (NTP) support - #488

Open
lo-simon wants to merge 8 commits into
sony:masterfrom
lo-simon:add-NTP-support
Open

lo-simon wants to merge 8 commits into
sony:masterfrom
lo-simon:add-NTP-support

Conversation

@lo-simon

@lo-simon lo-simon commented May 1, 2026

Copy link
Copy Markdown
Collaborator

The following scenarios illustrate the issue that arises when the system clock is rewound. In both cases, the time used is system_clock. To overcome this issue, we will need to use steady_clock to obtain the request's received time at the RDS. On the Node side, we will need to use nmos::strictly_increasing_update for the resource's version, which will always increment.

NTP study - Rewind clock on Node NTP study - Rewind clock on RDS

…ll ensure that, if Node's clock is rewound, RDS still sees a version increment when the resource is required to be updated in RDS
…ures the time always increases even when the system clock is rewound
? bst::chrono::duration_cast<bst::chrono::microseconds>(nmos::tai_clock::now() - nmos::time_point_from_tai(nmos::parse_version(received_time->second))).count() / 1000.0
? bst::chrono::duration_cast<bst::chrono::microseconds>(
// processing_dur = now - received time
( tai_clock::time_point(tai_clock::duration(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count()))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain this? Doesn't the steady_clock have a different epoch than tai_clock?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tai_clock now uses the boost implementation. According to the note in https://github.com/sony/nmos-cpp/blob/master/Development/nmos/api_utils.cpp#L710-L711, on Windows, the boost version of time_point::now (system_clock::now) sometimes returns the same value after a small time increment. Therefore, we are using std::chrono::steady_clock::now() here.

Comment thread README.md

Recent activity on the project (newest first):

- Added support for making existing timing/version semantics safe in environments where NTP rewinds system time.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⬇️ This PR isn't really much to do with NTP at all, it's about handling system clock being stepped back by a correction, which might come from NTP, PTP or anything. I'd change this line and the PR title.

@garethsb garethsb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Co-authored-by: Gareth's 🤖 buddy

This PR tries to stop resource version values going backwards when the system clock is stepped back, for example by an NTP correction.

  1. The registry side isn't fixed. The description says the registry (RDS) uses steady_clock for "the request's received time". But X-Received-Time is read in only one place, the Server-Timing calculation in make_api_finally_handler. Heartbeat expiry uses health_now(), which is based on the system clock via tai_clock. A clock step on the registry therefore still holds off expiry (stepped back) or expires every Node at once (stepped forward). The diagram's registry scenario isn't addressed.
  2. Mismatched clock conversions. X-Received-Time now holds a steady_clock value encoded as a TAI version string. support_api uses the raw .count(), while the finally handler uses duration_cast<nanoseconds>. Both should use the same cast, with a comment saying the value is steady_clock time, not TAI. (That would also answer my earlier question about epochs.)
  3. Some version writes are missed. annotation_api.cpp (IS-13 PATCH), the version updates in the nmos-cpp-node example, and downstream calls still use make_version() or tai_now(). The strictly_increasing_update(resources, …) approach only guarantees increasing versions if every write goes through it.
  4. API break. set_resource_subscription, set_resource_version and set_connection_resource_active all gain a resources parameter. (I know this would affect NVIDIA/nvnmos, nvnmos_impl.cpp calls the old signatures six times.)

For points 3 and 4, a per-resource rule would be simpler. IS-04 only needs each resource's version to increase; it doesn't need to be newer than every other resource's update time. A helper that returns the later of the new time and the resource's current version plus one nanosecond would keep the existing signatures:

inline tai strictly_increasing_version(const nmos::resource& resource, tai update = tai_now())
{
    const auto current = nmos::parse_version(nmos::fields::version(resource.data));
    return update > current ? update : tai_from_time_point(time_point_from_tai(current) + tai_clock::duration(1));
}

set_resource_subscription, set_resource_version and the annotation PATCH could each call this internally. nvnmos and other downstream code would then benefit without changes.

The PR also has small unrelated edits (using web::json::value;, web::json::value::string changed to value::string, a leftover at variable). It includes merge commits from master, so it would be better to rewrite history (maybe rebase on current master) before merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants