@@ -74,6 +74,13 @@ pub mod ffi {
7474 extern "Rust" {
7575 type Profile ;
7676 type ProfileExporter ;
77+ type CancellationToken ;
78+
79+ // CancellationToken factory and methods
80+ fn new_cancellation_token ( ) -> Box < CancellationToken > ;
81+ fn clone_token ( self : & CancellationToken ) -> Box < CancellationToken > ;
82+ fn cancel ( self : & CancellationToken ) ;
83+ fn is_cancelled ( self : & CancellationToken ) -> bool ;
7784
7885 // Static factory methods for Profile
7986 #[ Self = "Profile" ]
@@ -162,6 +169,35 @@ pub mod ffi {
162169 internal_metadata : & str ,
163170 info : & str ,
164171 ) -> Result < ( ) > ;
172+
173+ /// Sends a profile to Datadog with cancellation support.
174+ ///
175+ /// This is the same as `send_profile`, but allows cancelling the operation from another
176+ /// thread using a cancellation token.
177+ ///
178+ /// # Arguments
179+ /// * `profile` - Profile to send (will be reset after sending)
180+ /// * `files_to_compress` - Additional files to compress and attach (e.g., heap dumps)
181+ /// * `additional_tags` - Per-profile tags (in addition to exporter-level tags)
182+ /// * `process_tags` - Process-level tags as comma-separated string (e.g.,
183+ /// "runtime:native,profiler_version:1.0") Pass empty string "" if not needed
184+ /// * `internal_metadata` - Internal metadata as JSON string (e.g., `{"key": "value"}`) See
185+ /// Datadog-internal "RFC: Attaching internal metadata to pprof profiles" Pass empty
186+ /// string "" if not needed
187+ /// * `info` - System/environment info as JSON string (e.g., `{"os": "linux", "arch":
188+ /// "x86_64"}`) See Datadog-internal "RFC: Pprof System Info Support" Pass empty string ""
189+ /// if not needed
190+ /// * `cancel` - Cancellation token to cancel the send operation
191+ fn send_profile_with_cancellation (
192+ self : & ProfileExporter ,
193+ profile : & mut Profile ,
194+ files_to_compress : Vec < AttachmentFile > ,
195+ additional_tags : Vec < Tag > ,
196+ process_tags : & str ,
197+ internal_metadata : & str ,
198+ info : & str ,
199+ cancel : & CancellationToken ,
200+ ) -> Result < ( ) > ;
165201 }
166202}
167203
@@ -245,6 +281,50 @@ impl<'a> TryFrom<&ffi::Tag<'a>> for exporter::Tag {
245281 }
246282}
247283
284+ // ============================================================================
285+ // CancellationToken - Wrapper around tokio_util::sync::CancellationToken
286+ // ============================================================================
287+
288+ pub struct CancellationToken {
289+ inner : tokio_util:: sync:: CancellationToken ,
290+ }
291+
292+ /// Creates a new cancellation token.
293+ pub fn new_cancellation_token ( ) -> Box < CancellationToken > {
294+ Box :: new ( CancellationToken {
295+ inner : tokio_util:: sync:: CancellationToken :: new ( ) ,
296+ } )
297+ }
298+
299+ impl CancellationToken {
300+ /// Clones the cancellation token.
301+ ///
302+ /// A cloned token is connected to the original token - either can be used
303+ /// to cancel or check cancellation status. The useful part is that they have
304+ /// independent lifetimes and can be dropped separately.
305+ ///
306+ /// This is useful for multi-threaded scenarios where one thread performs the
307+ /// send operation while another thread can cancel it.
308+ pub fn clone_token ( & self ) -> Box < CancellationToken > {
309+ Box :: new ( CancellationToken {
310+ inner : self . inner . clone ( ) ,
311+ } )
312+ }
313+
314+ /// Cancels the token.
315+ ///
316+ /// Note that cancellation is a terminal state; calling cancel multiple times
317+ /// has no additional effect.
318+ pub fn cancel ( & self ) {
319+ self . inner . cancel ( ) ;
320+ }
321+
322+ /// Returns true if the token has been cancelled.
323+ pub fn is_cancelled ( & self ) -> bool {
324+ self . inner . is_cancelled ( )
325+ }
326+ }
327+
248328// ============================================================================
249329// Profile - Wrapper around internal::Profile
250330// ============================================================================
@@ -455,6 +535,62 @@ impl ProfileExporter {
455535 process_tags : & str ,
456536 internal_metadata : & str ,
457537 info : & str ,
538+ ) -> anyhow:: Result < ( ) > {
539+ self . send_profile_impl (
540+ profile,
541+ files_to_compress,
542+ additional_tags,
543+ process_tags,
544+ internal_metadata,
545+ info,
546+ None ,
547+ )
548+ }
549+
550+ /// Sends a profile to Datadog with cancellation support.
551+ ///
552+ /// # Arguments
553+ /// * `profile` - Profile to send (will be reset after sending)
554+ /// * `files_to_compress` - Additional files to compress and attach
555+ /// * `additional_tags` - Per-profile tags (in addition to exporter-level tags)
556+ /// * `process_tags` - Process-level tags as comma-separated string. Empty string if not needed.
557+ /// * `internal_metadata` - Internal metadata as JSON string. Empty string if not needed.
558+ /// Example: `{"custom_field": "value", "version": "1.0"}`
559+ /// * `info` - System/environment info as JSON string. Empty string if not needed. Example:
560+ /// `{"os": "linux", "arch": "x86_64", "kernel": "5.15.0"}`
561+ /// * `cancel` - Cancellation token to cancel the send operation
562+ pub fn send_profile_with_cancellation (
563+ & self ,
564+ profile : & mut Profile ,
565+ files_to_compress : Vec < ffi:: AttachmentFile > ,
566+ additional_tags : Vec < ffi:: Tag > ,
567+ process_tags : & str ,
568+ internal_metadata : & str ,
569+ info : & str ,
570+ cancel : & CancellationToken ,
571+ ) -> anyhow:: Result < ( ) > {
572+ self . send_profile_impl (
573+ profile,
574+ files_to_compress,
575+ additional_tags,
576+ process_tags,
577+ internal_metadata,
578+ info,
579+ Some ( & cancel. inner ) ,
580+ )
581+ }
582+
583+ /// Internal implementation shared by send_profile and send_profile_with_cancellation
584+ #[ allow( clippy:: too_many_arguments) ]
585+ fn send_profile_impl (
586+ & self ,
587+ profile : & mut Profile ,
588+ files_to_compress : Vec < ffi:: AttachmentFile > ,
589+ additional_tags : Vec < ffi:: Tag > ,
590+ process_tags : & str ,
591+ internal_metadata : & str ,
592+ info : & str ,
593+ cancel : Option < & tokio_util:: sync:: CancellationToken > ,
458594 ) -> anyhow:: Result < ( ) > {
459595 // Reset the profile and get the old one to export
460596 let old_profile = profile. inner . reset_and_return_previous ( ) ?;
@@ -506,7 +642,7 @@ impl ProfileExporter {
506642 internal_metadata_json,
507643 info_json,
508644 ) ?;
509- let response = self . inner . send ( request, None ) ?;
645+ let response = self . inner . send ( request, cancel ) ?;
510646
511647 // Check response status
512648 if !response. status ( ) . is_success ( ) {
0 commit comments