blob: efafafc46577b326cb90c5824e6cdad57ddf13ad [file]
// Copyright 2025 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 defines the user-visible interface for using a sequenced task
//! runner from Rust code. In the future it will likely be expanded to cover
//! other types of task runner.
//!
//! # Examples
//!
//! ```
//! let ptr: SequencedTaskRunnerHandle = SequencedTaskRunnerHandle::get_current_default().unwrap();
//! let do_something = || println!("I did something!");
//! ptr.post_task(do_something);
//! ```
chromium::import! {
"//base:scoped_refptr";
"//base:callback";
"//base:run_loop";
}
#[cxx::bridge(namespace = "base")]
pub mod ffi {
unsafe extern "C++" {
include!("base/functional/callback.rs.h");
include!("base/task/sequenced_task_runner_rust_shim.h");
// Tell cxx that we already have bindings for this type
type RustOnceClosure = callback::RustOnceClosure;
type SequencedTaskRunner;
fn PostTaskFromRust(
runner: Pin<&mut SequencedTaskRunner>,
task: Box<RustOnceClosure>,
) -> bool;
fn AddRef(runner: &SequencedTaskRunner);
// TODO(crbug.com/472552387): Tweak `cxx` to make this `allow` obsolete.
#[allow(clippy::missing_safety_doc)]
/// # Safety
///
/// The caller must assert that it owns 1 ref-count of this
/// object, and no code will ever dereference this instance of `self`
/// afterwards unless they know the ref-count is at least 1 (e.g.
/// because they have a different ref-counted pointer to the
/// same object).
unsafe fn Release(runner: &SequencedTaskRunner);
// We need a shim here because the normal `GetCurrentDefault` function
// returns a scoped_refptr, and we can't pass arbitrary generic/templated
// types across the bridge.
fn GetCurrentDefaultSequencedTaskRunnerForRust() -> *mut SequencedTaskRunner;
}
}
use scoped_refptr::{CxxRefCounted, CxxRefCountedThreadSafe, ScopedRefPtr};
// SAFETY:
// The C++ implementation guarantees that ref-counting is the only mechanism
// managing the lifetime of a `SequencedTaskRunner`.
unsafe impl CxxRefCounted for ffi::SequencedTaskRunner {
fn add_ref(&self) {
ffi::AddRef(self);
}
// SAFETY: The trait imposes the same requirements as `Release`.
unsafe fn release(&self) {
unsafe {
ffi::Release(self);
}
}
}
// SAFETY: The C++ implementation of this class is designed to be thread-safe.
unsafe impl CxxRefCountedThreadSafe for ffi::SequencedTaskRunner {}
unsafe impl Send for ffi::SequencedTaskRunner {}
unsafe impl Sync for ffi::SequencedTaskRunner {}
/// This type is the Rust representation of a C++
/// `scoped_refptr<base::SequencedTaskRunner>`. It can interoperate with
/// existing `scoped_refptr`s in C++ code, and keeps the task runner alive
/// at least until it goes out of scope.
#[derive(Clone)]
pub struct SequencedTaskRunnerHandle(ScopedRefPtr<ffi::SequencedTaskRunner>);
impl SequencedTaskRunnerHandle {
/// Get the current default task runner. This function corresponds to
/// `base::SequencedTaskRunner::GetCurrentDefault` in C++.
pub fn get_current_default() -> Option<Self> {
let default_ptr = ffi::GetCurrentDefaultSequencedTaskRunnerForRust();
// SAFETY: The ffi function above returns a pointer that owns one ref-count
unsafe { ScopedRefPtr::wrap_ref_counted(default_ptr) }.map(SequencedTaskRunnerHandle)
}
/// Post a task to the sequenced task runner. This function corresponds to
/// `base::SequencedTaskRunner::PostTask`, but without the location
/// argument (crbug.com/469133195).
pub fn post_task<F: FnOnce() + Send + 'static>(&self, task: F) -> bool {
ffi::PostTaskFromRust(self.0.as_pin(), Box::new(task.into()))
}
/// Retrieve the contained ScopedRefPtr. This should generally only be used
/// for ffi purposes.
pub fn as_scoped_refptr(&self) -> &ScopedRefPtr<ffi::SequencedTaskRunner> {
&self.0
}
/// Run all tasks which have been queued up so far
pub fn run_all_current_tasks_for_testing(&self) {
let run_loop = run_loop::RunLoop::new();
self.post_task(run_loop.get_quit_closure());
run_loop.run();
}
pub fn run_all_current_tasks_on_default_runner_for_testing() {
Self::get_current_default()
.expect("Must be called in a context with a default task runner")
.run_all_current_tasks_for_testing();
}
}