blob: 464523b929ad285dd834bc0c2efd3bdd17026eaf [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! This module (and its corresponding C++ files) defines a way to convert
//! safely between C++ and Rust handle types.
//!
//! TRY TO AVOID USING THESE TYPES! It is vastly preferable to get your mojo
//! handles via an existing message pipe. These types are meant to be used
//! over FFI in situations where that isn't feasible.
//!
//! For each type (untyped, message pipe, data pipe, etc), the C++ side defines
//! a wrapper for that ScopedHandle type. We need separate wrapper types for
//! each because cxx doesn't handle templated types.
//!
//! To give ownership of a handle from C++ to Rust, wrap the scoped handle in
//! the corresponding wrapper type, create a unique pointer, and pass it into
//! Rust (via an FFI function that you define yourself). Then you can simply
//! call that wrapper's `into_whatever_handle` method to convert it to the Rust
//! type.
//!
//! To give ownership of a handle from Rust to C++, call that wrapper's
//! `from_whatever_handle` method, then hand it to C++ (again, via your own FFI
//! function).
//!
//! For an example of doing this, see //mojo/public/rust/bindings/test/cxx.rs.
use crate::message_pipe::MessageEndpoint;
use crate::mojo_types::UntypedHandle;
#[cxx::bridge(namespace = "mojo::rust")]
mod ffi {
unsafe extern "C++" {
include!("mojo/public/rust/system/scoped_handle_interop.h");
pub type ScopedHandleWrapper;
#[Self = ScopedHandleWrapper]
fn Release(wrapper: UniquePtr<ScopedHandleWrapper>) -> usize;
#[Self = ScopedHandleWrapper]
fn Create(handle: usize) -> UniquePtr<ScopedHandleWrapper>;
pub type ScopedMessagePipeHandleWrapper;
#[Self = ScopedMessagePipeHandleWrapper]
fn Release(wrapper: UniquePtr<ScopedMessagePipeHandleWrapper>) -> usize;
#[Self = ScopedMessagePipeHandleWrapper]
fn Create(handle: usize) -> UniquePtr<ScopedMessagePipeHandleWrapper>;
}
}
// Re-export all the types in the bridge, since they're the only thing in this
// file so there's no point having them in a sub-module.
pub use ffi::*;
// We have to use inherent implementations rather than `From` due to the
// orphaning rule: this crate doesn't define `UniquePtr` or `UntypedHandle`.
impl ScopedHandleWrapper {
/// Returns None if the given handle is 0.
pub fn into_untyped_handle(handle: cxx::UniquePtr<Self>) -> Option<UntypedHandle> {
let raw = Self::Release(handle);
if raw == 0 {
return None;
}
// SAFETY: The ScopedHandle type owns its underlying handle value, which
// is either live or 0, and we know it's not 0. `Release` gives up that
// ownership.
return Some(unsafe { UntypedHandle::wrap_raw_value(raw) });
}
pub fn from_untyped_handle(handle: UntypedHandle) -> cxx::UniquePtr<Self> {
Self::Create(handle.into_raw_value())
}
}
impl ScopedMessagePipeHandleWrapper {
/// Returns None if the given handle is 0.
pub fn into_message_endpoint(handle: cxx::UniquePtr<Self>) -> Option<MessageEndpoint> {
let raw = Self::Release(handle);
if raw == 0 {
return None;
}
// SAFETY: The ScopedHandle type owns its underlying handle value, which
// is either live or 0, and we know it's not 0. `Release` gives up that
// ownership.
let untyped = unsafe { UntypedHandle::wrap_raw_value(raw) };
Some(MessageEndpoint::from(untyped))
}
pub fn from_message_endpoint(endpoint: MessageEndpoint) -> cxx::UniquePtr<Self> {
Self::Create(UntypedHandle::from(endpoint).into_raw_value())
}
}