Skip to content

Commit b7ebbe6

Browse files
adeshkumar1claude
andcommitted
review: rename finish_with_time → set_end_time, use -1 sentinel
Per review feedback from @dmehala: 1. Rename dd_span_finish_with_time → dd_span_set_end_time to match the C++ Span::set_end_time and the existing dd_span_set_* naming family. 2. Expose DD_TRACE_CURRENT_TIME (-1) as the "use current time" sentinel for start_time_ns, so 0 (1970-01-01) stays a valid timestamp. 3. Drop the sentinel branch in dd_span_set_end_time; callers wanting the current end time use dd_span_finish, and this also removes the implicit-path clamp that was only defending against a future-start caller bug. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a2cb441 commit b7ebbe6

3 files changed

Lines changed: 39 additions & 50 deletions

File tree

binding/c/include/datadog/c/tracer.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ typedef enum {
4444
DD_OPT_INTEGRATION_VERSION = 5
4545
} dd_tracer_option;
4646

47+
// Sentinel for start_time_ns / dd_span_set_end_time meaning "use the
48+
// current time." Chosen so 0 (1970-01-01) remains a valid timestamp.
49+
static const int64_t DD_TRACE_CURRENT_TIME = -1;
50+
4751
// Options for creating a span. Unset fields default to NULL.
4852
typedef struct {
4953
const char* name;
@@ -52,7 +56,7 @@ typedef struct {
5256
const char* service_type;
5357
const char* environment;
5458
const char* version;
55-
int64_t start_time_ns; // UNIX-epoch nanoseconds; 0 = use current time
59+
int64_t start_time_ns; // UNIX-epoch ns; DD_TRACE_CURRENT_TIME for now
5660
} dd_span_options_t;
5761

5862
// Error codes returned by the C binding.
@@ -181,12 +185,14 @@ DD_TRACE_C_API dd_span_t* dd_span_create_child(dd_span_t* span_handle,
181185
// @param span_handle Span handle
182186
DD_TRACE_C_API void dd_span_finish(dd_span_t* span_handle);
183187

184-
// Finish a span using an explicit end time. No-op if span_handle is NULL.
188+
// Set the end time of a span from an explicit wall-clock timestamp. The
189+
// span itself is finished when dd_span_free destroys it. To use the
190+
// current time, call dd_span_finish instead. No-op if span_handle is NULL.
185191
//
186192
// @param span_handle Span handle
187-
// @param end_time_ns Wall-clock end time (UNIX-epoch ns); 0 = current time
188-
DD_TRACE_C_API void dd_span_finish_with_time(dd_span_t* span_handle,
189-
int64_t end_time_ns);
193+
// @param end_time_ns Wall-clock end time (UNIX-epoch ns)
194+
DD_TRACE_C_API void dd_span_set_end_time(dd_span_t* span_handle,
195+
int64_t end_time_ns);
190196

191197
// Get the trace ID as a zero-padded hex string.
192198
//

binding/c/src/tracer.cpp

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ dd::SpanConfig make_span_config(dd_span_options_t options) {
7676
if (options.version != nullptr) {
7777
span_config.version = options.version;
7878
}
79-
if (options.start_time_ns != 0) {
79+
if (options.start_time_ns != DD_TRACE_CURRENT_TIME) {
8080
span_config.start = wall_ns_to_timepoint(options.start_time_ns);
8181
}
8282
return span_config;
@@ -265,33 +265,30 @@ dd_span_t *dd_span_create_child(dd_span_t *span_handle,
265265
}
266266

267267
void dd_span_finish(dd_span_t *span_handle) {
268-
dd_span_finish_with_time(span_handle, 0);
268+
if (span_handle == nullptr) {
269+
return;
270+
}
271+
reinterpret_cast<dd::Span *>(span_handle)
272+
->set_end_time(std::chrono::steady_clock::now());
269273
}
270274

271-
void dd_span_finish_with_time(dd_span_t *span_handle, int64_t end_time_ns) {
275+
void dd_span_set_end_time(dd_span_t *span_handle, int64_t end_time_ns) {
272276
if (span_handle == nullptr) {
273277
return;
274278
}
275279
auto *span = reinterpret_cast<dd::Span *>(span_handle);
280+
// Anchor end.tick to start.tick + wall delta so NTP adjustments during the
281+
// span's lifetime don't skew duration. Clamp to start.tick on negative
282+
// deltas — serialization casts duration to uint64_t.
276283
const auto start = span->start_time();
277-
// Explicit path anchors to start.wall so NTP adjustments during the
278-
// span's lifetime don't skew duration; implicit path uses steady now.
279-
// Both clamp to start.tick so a negative duration never ships
280-
// (serialization casts duration to uint64_t).
281-
std::chrono::steady_clock::time_point end_tick;
282-
if (end_time_ns == 0) {
283-
end_tick = std::chrono::steady_clock::now();
284-
} else {
285-
const auto end_wall = std::chrono::system_clock::time_point(
286-
std::chrono::round<std::chrono::system_clock::duration>(
287-
std::chrono::nanoseconds(end_time_ns)));
288-
end_tick =
289-
start.tick +
290-
(end_wall > start.wall
291-
? std::chrono::duration_cast<dd::Duration>(end_wall - start.wall)
292-
: dd::Duration::zero());
293-
}
294-
span->set_end_time(end_tick > start.tick ? end_tick : start.tick);
284+
const auto end_wall = std::chrono::system_clock::time_point(
285+
std::chrono::round<std::chrono::system_clock::duration>(
286+
std::chrono::nanoseconds(end_time_ns)));
287+
const auto duration =
288+
end_wall > start.wall
289+
? std::chrono::duration_cast<dd::Duration>(end_wall - start.wall)
290+
: dd::Duration::zero();
291+
span->set_end_time(start.tick + duration);
295292
}
296293

297294
int dd_span_get_trace_id(dd_span_t *span_handle, char *buffer,

binding/c/test/test_c_binding.cpp

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ TEST_CASE("span create, tag, finish, free", "[c_binding]") {
104104
dd_span_set_error(span, 1);
105105
dd_span_set_error_message(span, "something broke");
106106

107-
dd_span_finish_with_time(span, end_ns);
107+
dd_span_set_end_time(span, end_ns);
108108
dd_span_free(span);
109109

110110
const auto &sd = ctx.collector->first_span();
@@ -122,31 +122,17 @@ TEST_CASE("span create, tag, finish, free", "[c_binding]") {
122122
std::chrono::milliseconds(1));
123123
}
124124

125-
TEST_CASE("finish clamps negative durations to zero", "[c_binding]") {
125+
TEST_CASE("set_end_time before start clamps to zero duration", "[c_binding]") {
126126
auto ctx = make_tracer();
127127

128-
// Explicit end before explicit start.
129128
const int64_t past_ns = 1'700'000'000'000'000'000LL;
130-
auto *s1 = dd_tracer_create_span(
131-
ctx.tracer, {.name = "explicit", .start_time_ns = past_ns});
132-
dd_span_finish_with_time(s1, past_ns - 1'000'000LL);
133-
dd_span_free(s1);
134-
135-
// Sentinel finish (steady_clock::now()) with a start projected into
136-
// the future — exercises the implicit-path clamp.
137-
const int64_t future_ns =
138-
std::chrono::duration_cast<std::chrono::nanoseconds>(
139-
(std::chrono::system_clock::now() + std::chrono::hours(1))
140-
.time_since_epoch())
141-
.count();
142-
auto *s2 = dd_tracer_create_span(
143-
ctx.tracer, {.name = "implicit", .start_time_ns = future_ns});
144-
dd_span_finish(s2);
145-
dd_span_free(s2);
146-
147-
for (const auto &chunk : ctx.collector->chunks)
148-
for (const auto &span : chunk)
149-
CHECK(span->duration >= std::chrono::nanoseconds(0));
129+
auto *span = dd_tracer_create_span(
130+
ctx.tracer, {.name = "reversed", .start_time_ns = past_ns});
131+
REQUIRE(span != nullptr);
132+
dd_span_set_end_time(span, past_ns - 1'000'000LL);
133+
dd_span_free(span);
134+
135+
CHECK(ctx.collector->first_span().duration >= std::chrono::nanoseconds(0));
150136
}
151137

152138
TEST_CASE("create span with resource", "[c_binding]") {
@@ -291,7 +277,7 @@ TEST_CASE("null arguments do not crash", "[c_binding]") {
291277
dd_span_set_error_message(nullptr, "msg");
292278
dd_span_inject(nullptr, test_header_writer);
293279
dd_span_finish(nullptr);
294-
dd_span_finish_with_time(nullptr, 0);
280+
dd_span_set_end_time(nullptr, 0);
295281
dd_span_set_resource(nullptr, "res");
296282
dd_span_set_service(nullptr, "svc");
297283
}

0 commit comments

Comments
 (0)