Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions examples/ffi/exporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,18 @@ int main(int argc, char *argv[]) {
auto cancel = ddog_CancellationToken_new();
auto cancel_for_background_thread = ddog_CancellationToken_clone(&cancel);

// Eagerly initialize the tokio runtime. This is optional, but required
// to avoid race conditions if another thread might be using the
// the cancellation token at the same time as the profile is being sent
// (as is the case here).
ddog_VoidResult init_result = ddog_prof_Exporter_init_runtime(exporter);
if (init_result.tag != DDOG_VOID_RESULT_OK) {
print_error("Failed to initialize exporter runtime: ", init_result.err);
ddog_Error_drop(&init_result.err);
ddog_prof_Exporter_drop(exporter);
return 1;
}

// As an example of CancellationToken usage, here we create a background
// thread that sleeps for some time and then cancels a request early (e.g.
// before the timeout in ddog_prof_Exporter_send_blocking is hit).
Expand Down
30 changes: 30 additions & 0 deletions libdd-profiling-ffi/src/exporter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,38 @@ unsafe fn parse_json(
}
}

/// Initializes the tokio runtime for the exporter.
///
/// This function creates the tokio runtime used by `ddog_prof_Exporter_send_blocking`.
/// It can be called ahead of time to ensure the runtime is ready before sending.
///
/// # Thread Affinity
///
/// **Important**: The runtime has thread affinity. This function should be called from
/// the same thread that will later call `ddog_prof_Exporter_send_blocking`.
///
/// # Arguments
/// * `exporter` - Borrows the exporter.
///
/// # Safety
/// The `exporter` must point to a valid ProfileExporter that has not been dropped.
#[no_mangle]
#[must_use]
#[named]
pub unsafe extern "C" fn ddog_prof_Exporter_init_runtime(
mut exporter: *mut Handle<ProfileExporter>,
) -> VoidResult {
wrap_with_void_ffi_result!({
let exporter = exporter.to_inner_mut()?;
exporter.init_runtime()?
})
}

/// Builds a request and sends it, returning the HttpStatus.
///
/// Note: If the runtime has not been initialized via `ddog_prof_Exporter_init_runtime`,
/// it will be lazily initialized on first call.
///
/// # Arguments
/// * `exporter` - Borrows the exporter.
/// * `profile` - Takes ownership of the profile.
Expand Down
28 changes: 21 additions & 7 deletions libdd-profiling/src/exporter/profile_exporter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,26 @@ impl ProfileExporter {
/// - Using the async [`send`] method directly from within a tokio runtime
///
/// [`send`]: ProfileExporter::send
/// Initializes the tokio runtime for blocking operations.
///
/// This method lazily creates a single-threaded tokio runtime. It can be called
/// before `send_blocking` to ensure the runtime is ready ahead of time.
///
/// # Thread Affinity
///
/// **Important**: The runtime uses `new_current_thread()`, which has thread affinity.
/// This method should be called from the same thread that will later call `send_blocking`.
pub fn init_runtime(&mut self) -> anyhow::Result<()> {
if self.runtime.is_none() {
self.runtime = Some(
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?,
);
}
Ok(())
}

#[allow(clippy::too_many_arguments)]
pub fn send_blocking(
&mut self,
Expand All @@ -172,13 +192,7 @@ impl ProfileExporter {
process_tags: Option<&str>,
cancel: Option<&CancellationToken>,
) -> anyhow::Result<reqwest::StatusCode> {
if self.runtime.is_none() {
self.runtime = Some(
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?,
);
}
self.init_runtime()?;

Ok(self
.runtime
Expand Down
Loading