Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

POC: Rust Wrapper for 1DS C++ SDK #1225

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from

Conversation

CDFriend
Copy link

@CDFriend CDFriend commented Nov 23, 2023

What is this?

Adds a Rust "wrapper" to the 1DS C++ SDK, which will allow the SDK to be imported into Rust projects as a crate. The crate interacts with the C API of the SDK (instead of C++) as this will ensure a stable binary interface between the Rust layer and C++ layer of the project.

Project Structure

Crates:

  • cpp-client-telemetry-sys: crate directly exposing the C API of the 1DS SDK to Rust.
  • oneds-telemetry: contains some abstractions over the 1DS SDK and allows messages to be sent to the SDK from the log crate.
  • telemetry-sample: example executable using the oneds-telemetry crate.

Limitations

  • Currently only i64 and string telemetry fields are supported.
  • Currently we're only linking with the Windows-x64 static library - additional work on the build script will be required to add support for dynamic linking and other architectures/operating systems.

Build Instructions

  1. Follow instructions in the root of the repository to build the project in Win32-Lib configuration for the x64 arch.
  2. Run cargo build in wrappers/rust

@CDFriend
Copy link
Author

@microsoft-github-policy-service agree company="Microsoft"

}

fn log(&self, record: &Record) {
if unsafe { CONSOLE_ENABLED } && self.enabled(record.metadata()) {

Choose a reason for hiding this comment

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

These fields should probably be changed to use std::sync::atomc::AtomicBool, which will allow this code to be safe.

Using static mut is unsafe because it allows for race conditions, which are undefined behavior.

pub fn to_leaky_c_str(value: &str) -> *const i8 {
let c_str = Box::new(CString::new(value.clone()).unwrap());
let ptr = c_str.as_ptr() as *const i8;
mem::forget(c_str);

Choose a reason for hiding this comment

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

Can this be Box::into_raw(c_str) as *const i8 instead?

}

/// The global `Tracer` provider singleton.
static GLOBAL_LOG_MANAGER: Lazy<RwLock<LogManager>> = Lazy::new(|| RwLock::new(LogManager::new()));

Choose a reason for hiding this comment

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

Is there a reason this uses Lazy instead of just typing this field as RwLock<LogManager>?

* This will leak memory
*/
pub fn to_leaky_c_str(value: &str) -> *const i8 {
let c_str = Box::new(CString::new(value.clone()).unwrap());

Choose a reason for hiding this comment

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

I suspect the value.clone() call doesn't do anything. It could probably be simplified to just value.

}

fn to_str(&self) -> Result<&str, std::str::Utf8Error> {
unsafe { CStr::from_ptr(self.as_ptr()).to_str() }

Choose a reason for hiding this comment

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

Is there a reason this is not just core::str::from_utf8(&self.bytes)? What merits using unsafe code here?

result
}

pub fn evt_upload(handle: &evt_handle_t) -> evt_status_t {

Choose a reason for hiding this comment

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

Because you are directly exposing evt_handle_t outside your crate, and this type has unsafe pointers in it, these functions are actually unsafe to call. They allow a safe caller to violate type safety.

You can fix this in one of several ways:

  1. Don't expose these functions outside this crate.
  2. Don't expose evt_context_t, and instead define an EvtContext wrapper type which contains evt_context_t but does not expose it outside of the crate.
  3. Mark these functions unsafe.

[dependencies]
chrono = { version = "0.4.30" }
log = { version = "0.4.20" }
once_cell = "1.12.0"

Choose a reason for hiding this comment

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

Is there a reason to use once_cell, now that Once and OnceLock are part of the Rust standard library?

pub mod appender;
pub mod types;

#[derive(Clone)]

Choose a reason for hiding this comment

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

Is log_handle a pointer or a handle that was received from the underlying C API? If so, then I don't think this Clone call is correct or safe.

Int(i64),
}

pub type TelemetryPropertyBag = HashMap<&'static str, TelemetryProperty>;

Choose a reason for hiding this comment

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

It's a matter of taste, but I generally prefer to simply see HashMap<&str, TelemetryProperty> instead of a type alias.

TelemetryItem {
name: StringProperty::new(name),
data: TelemetryPropertyBag::new(),
time: Option::None,

Choose a reason for hiding this comment

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

Use None instead of Option::None.

@lalitb
Copy link
Contributor

lalitb commented Jan 4, 2024

@CDFriend This looks to be a good addition. when you have time, can you please make it ready for review :)

@msft-tsharp
Copy link

msft-tsharp commented Jan 23, 2024

@CDFriend This looks to be a good addition. when you have time, can you please make it ready for review :)

@lalitb - I can work on resolving the feedback.

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.

None yet

4 participants