| // 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 defines the user-visible interface for using a base::RunLoop |
| //! object from Rust. |
| |
| #[cxx::bridge(namespace = "base")] |
| mod ffi { |
| unsafe extern "C++" { |
| include!("base/run_loop_rust_shim.h"); |
| |
| #[namespace = "base"] |
| type RunLoop; |
| |
| // We need a shim because cxx won't let us allocate on the stack. |
| fn CreateRunLoop() -> UniquePtr<RunLoop>; |
| |
| // Call run_loop.Run(). We need a shim because cxx doesn't support |
| // functions with default arguments, and for the same reason as |
| // `QuitRunLoop` below. |
| fn RunRunLoop(run_loop: &UniquePtr<RunLoop>); |
| |
| // Quit the given `RunLoop`. We need a shim because the function takes |
| // a mutable reference, but since it's thread safe we need to be able to |
| // call it with a shared reference. |
| fn QuitRunLoop(run_loop: &UniquePtr<RunLoop>); |
| |
| // Run until the loop is idle. |
| fn RunUntilIdle(run_loop: &UniquePtr<RunLoop>); |
| } |
| } |
| |
| use cxx::UniquePtr; |
| use std::sync::Arc; |
| |
| pub struct RunLoop { |
| run_loop: Arc<UniquePtr<ffi::RunLoop>>, |
| } |
| |
| impl Default for RunLoop { |
| fn default() -> Self { |
| Self::new() |
| } |
| } |
| |
| impl RunLoop { |
| // Create a new RunLoop object. |
| pub fn new() -> Self { |
| RunLoop { run_loop: Arc::new(ffi::CreateRunLoop()) } |
| } |
| |
| // Run the loop until this loop's `quit_closure()` is called. If it has already |
| // been called for this loop, return immediately instead. |
| // |
| // Each loop can only be run once (further runs would just return immediately), |
| // so this function takes the `RunLoop` by value to reflect that. |
| pub fn run(self) { |
| ffi::RunRunLoop(&self.run_loop); |
| } |
| |
| // Return a closure that will quit `self` when executed. |
| pub fn get_quit_closure(&self) -> impl Fn() + Send + 'static { |
| let self_weak = Arc::downgrade(&self.run_loop); |
| move || { |
| if let Some(run_loop) = self_weak.upgrade() { |
| ffi::QuitRunLoop(&run_loop) |
| } |
| } |
| } |
| |
| /// Run the current RunLoop until it doesn't find any tasks or messages in |
| /// its queue. This calls out to the equivalent C++ function, and has the |
| /// same caveats: |
| /// |
| /// WARNING #1: This may run long (flakily timeout) and even never return! |
| /// Do not use this when repeating tasks such as animated web |
| /// pages are present. |
| /// WARNING #2: This may return too early! For example, if used to run until |
| /// an incoming event has occurred but that event depends on a |
| /// task in a different queue -- e.g. another TaskRunner or a |
| /// system event. |
| /// |
| /// Per the warnings above, this tends to lead to flaky tests; prefer |
| /// `get_quit_closure` + `run` when at all possible. |
| pub fn run_until_idle(&self) { |
| ffi::RunUntilIdle(&self.run_loop); |
| } |
| } |
| |
| // SAFETY: we only expose the thread-safe subset of RunLoop's functionality |
| unsafe impl Send for ffi::RunLoop {} |
| unsafe impl Sync for ffi::RunLoop {} |