From d4873c5d4fdd11f484df183e01c0825fe347fd8b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 15 Nov 2024 12:08:43 +0100 Subject: [PATCH] bql: add a "mock" BQL for Rust unit tests Right now, the stub BQL in stubs/iothread-lock.c always reports itself as unlocked. However, Rust would like to run its tests in an environment where the BQL *is* locked. Provide an extremely dirty function that flips the return value of bql_is_locked() to true. Signed-off-by: Paolo Bonzini --- include/qemu/main-loop.h | 8 ++++++++ rust/qemu-api/meson.build | 2 +- rust/qemu-api/src/cell.rs | 26 +++++++++++++++++++++++--- stubs/iothread-lock.c | 8 +++++++- system/cpus.c | 6 ++++++ 5 files changed, 45 insertions(+), 5 deletions(-) diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 646306c272f..3935a573391 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -247,6 +247,14 @@ void event_notifier_set_handler(EventNotifier *e, GSource *iohandler_get_g_source(void); AioContext *iohandler_get_aio_context(void); +/** + * rust_bql_mock_lock: + * + * Called from Rust doctests to make bql_lock() return true. + * Do not touch. + */ +void rust_bql_mock_lock(void); + /** * bql_locked: Return lock status of the Big QEMU Lock (BQL) * diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 7ff408ad68e..50ec00e128d 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -60,7 +60,7 @@ test('rust-qemu-api-integration', dependencies: [qemu_api, qemu_api_macros], link_whole: [rust_qemu_api_objs, libqemuutil]), args: [ - '--test', + '--test', '--test-threads', '1', '--format', 'pretty', ], protocol: 'rust', diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs index 28349de291a..eae4e2ce786 100644 --- a/rust/qemu-api/src/cell.rs +++ b/rust/qemu-api/src/cell.rs @@ -124,9 +124,18 @@ use std::{ use crate::bindings; -// TODO: When building doctests do not include the actual BQL, because cargo -// does not know how to link them to libqemuutil. This can be fixed by -// running rustdoc from "meson test" instead of relying on cargo. +/// An internal function that is used by doctests. +pub fn bql_start_test() { + if cfg!(MESON) { + // SAFETY: integration tests are run with --test-threads=1, while + // unit tests and doctests are not multithreaded and do not have + // any BQL-protected data. Just set bql_locked to true. + unsafe { + bindings::rust_bql_mock_lock(); + } + } +} + pub fn bql_locked() -> bool { // SAFETY: the function does nothing but return a thread-local bool !cfg!(MESON) || unsafe { bindings::bql_locked() } @@ -220,6 +229,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlCell::new(5); /// ``` @@ -236,6 +246,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlCell::new(5); /// @@ -253,6 +264,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let cell = BqlCell::new(5); /// assert_eq!(cell.get(), 5); @@ -274,6 +286,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlCell::new(5); /// let five = c.into_inner(); @@ -293,6 +306,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlCell::new(5); /// @@ -315,6 +329,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlCell::new(5); /// @@ -333,6 +348,7 @@ impl BqlCell { /// /// ``` /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlCell::new(5); /// let five = c.take(); @@ -461,6 +477,7 @@ impl BqlRefCell { /// /// ``` /// use qemu_api::cell::BqlRefCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlRefCell::new(5); /// @@ -472,6 +489,7 @@ impl BqlRefCell { /// /// ```should_panic /// use qemu_api::cell::BqlRefCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlRefCell::new(5); /// @@ -513,6 +531,7 @@ impl BqlRefCell { /// /// ``` /// use qemu_api::cell::BqlRefCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlRefCell::new("hello".to_owned()); /// @@ -525,6 +544,7 @@ impl BqlRefCell { /// /// ```should_panic /// use qemu_api::cell::BqlRefCell; + /// # qemu_api::cell::bql_start_test(); /// /// let c = BqlRefCell::new(5); /// let m = c.borrow(); diff --git a/stubs/iothread-lock.c b/stubs/iothread-lock.c index 54676598950..6050c081f53 100644 --- a/stubs/iothread-lock.c +++ b/stubs/iothread-lock.c @@ -1,11 +1,17 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" +static bool bql_is_locked = false; static uint32_t bql_unlock_blocked; bool bql_locked(void) { - return false; + return bql_is_locked; +} + +void rust_bql_mock_lock(void) +{ + bql_is_locked = true; } void bql_lock_impl(const char *file, int line) diff --git a/system/cpus.c b/system/cpus.c index ba633c7688b..4b43ceb5435 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -538,6 +538,12 @@ bool qemu_in_main_thread(void) return bql_locked(); } +void rust_bql_mock_lock(void) +{ + error_report("This function should be used only from tests"); + abort(); +} + /* * The BQL is taken from so many places that it is worth profiling the * callers directly, instead of funneling them all through a single function. -- 2.39.5