blob: 0fba5128855d95327071dd8ebec191a22ae0c523 [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.
chromium::import! {
"//base:scoped_refptr";
"//base:sequenced_task_runner";
"//base:sequenced_task_runner_test_bridge";
"//base/test:task_environment";
}
use rust_gtest_interop::prelude::*;
use scoped_refptr::ScopedRefPtr;
use sequenced_task_runner::SequencedTaskRunnerHandle;
use std::sync::{Arc, RwLock};
fn get_test_ref_ptr(
b: *mut bool,
) -> ScopedRefPtr<sequenced_task_runner_test_bridge::ffi::TestRefCounted> {
// SAFETY: CreateTestRefCounted provides a released pointer
unsafe {
ScopedRefPtr::wrap_ref_counted(
sequenced_task_runner_test_bridge::ffi::CreateTestRefCounted(&mut *b),
)
}
.unwrap()
}
#[gtest(RustSequences, TestScopedRefPtr)]
fn test_scoped_refptr() {
// Put the flag in an UnsafeCell so Rust won't move it around while C++
// has it, and the borrow checker won't stop us from examining it.
let destroyed_flag: std::cell::UnsafeCell<bool> = false.into();
let test_ref_counted_ptr = get_test_ref_ptr(destroyed_flag.get());
expect_true!(test_ref_counted_ptr.HasOneRef());
expect_true!(test_ref_counted_ptr.HasAtLeastOneRef());
// SAFETY: We're single-threaded so nothing else is examining this bool
expect_false!(unsafe { *destroyed_flag.get() });
let test_ref_counted_ptr2 = test_ref_counted_ptr.clone();
expect_false!(test_ref_counted_ptr.HasOneRef());
expect_false!(test_ref_counted_ptr2.HasOneRef());
expect_true!(test_ref_counted_ptr.HasAtLeastOneRef());
expect_true!(test_ref_counted_ptr2.HasAtLeastOneRef());
// SAFETY: We're single-threaded so nothing else is examining this bool
expect_false!(unsafe { *destroyed_flag.get() });
drop(test_ref_counted_ptr);
expect_true!(test_ref_counted_ptr2.HasOneRef());
expect_true!(test_ref_counted_ptr2.HasAtLeastOneRef());
// SAFETY: We're single-threaded so nothing else is examining this bool
expect_false!(unsafe { *destroyed_flag.get() });
drop(test_ref_counted_ptr2);
// SAFETY: No other references to this bool exist anymore
expect_true!(unsafe { *destroyed_flag.get() });
}
#[gtest(RustSequences, TestSequencedTaskrunner)]
fn test_sequenced_task_runner() {
// Set up the environment so we can get a task runner and execute the tasks
let _task_env = task_environment::ffi::CreateTaskEnvironment();
let task_runner = SequencedTaskRunnerHandle::get_current_default()
.expect("We just initialized an environment so there should be a default task runner");
let run_tasks = || {
task_runner.run_all_current_tasks_for_testing();
};
// TODO(crbug.com/470438844): Replace these with sequenced primitives once
// we've implemented them.
let count: Arc<RwLock<i32>> = Arc::new(RwLock::new(0));
let count_clone = count.clone();
let add_1 = move || {
// `try_write` will never block. It should always succeed because the
// data is accessed via the sequence.
let mut count_value = count_clone.try_write().unwrap();
*count_value += 1;
};
let count_clone = count.clone();
let times_2 = move || {
let mut count_value = count_clone.try_write().unwrap();
*count_value *= 2;
};
let count_clone = count.clone();
let sub_3 = move || {
let mut count_value = count_clone.try_write().unwrap();
*count_value -= 3;
};
task_runner.post_task(add_1.clone());
task_runner.post_task(times_2.clone());
task_runner.post_task(times_2.clone());
task_runner.post_task(times_2.clone());
task_runner.post_task(sub_3.clone());
task_runner.post_task(times_2.clone());
expect_eq!(*count.try_read().unwrap(), 0);
run_tasks();
expect_eq!(*count.try_read().unwrap(), 10);
let count_clone = count.clone();
task_runner.post_task(move || {
let mut count_value = count_clone.try_write().unwrap();
*count_value *= 100;
});
run_tasks();
expect_eq!(*count.try_read().unwrap(), 1000);
}