Skip to content

Commit

Permalink
cxx-qt-gen: add cxx_qt::Locking as a trait which can be negated
Browse files Browse the repository at this point in the history
Closes KDAB#561
  • Loading branch information
ahayzen-kdab committed Jun 5, 2023
1 parent 7a7d542 commit 58d2d58
Show file tree
Hide file tree
Showing 28 changed files with 343 additions and 65 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Allow associated constants, types and macro invocations within `impl qobject::T` blocks
- Ensure that generated Rust code works when `#![deny(missing_docs)]` is enabled
- Ability to connect and disconnect from signals in Rust triggering a function pointer
- `unsafe impl !cxx_qt::Locking for qobject::T` to disable internal locking

### Changed

Expand Down
8 changes: 8 additions & 0 deletions book/src/concepts/threading.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ This means that Rust code, such as invokables and properties, which are directly

Note that a recursive mutex is used internally, this allows for signals to be emitted and then call slots on the same object without deadlocks.

### Locking

In certain scenarios it might be useful to disable locking that occurs when the context switches from C++ to Rust.

To disable the generation of locking use an unsafe negation `unsafe impl !cxx_qt::Locking for qobject::T {}`.

However if locking is disabled the `cxx_qt::Threading` trait can not be enabled on the object.

## Multi threading

To achieve safe multi-threading on the Rust side we use an [`CxxQtThread<T>`](../qobject/cxxqtthread.md).
Expand Down
2 changes: 2 additions & 0 deletions book/src/qobject/cxxqtthread.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ First threading needs to be enabled for the [`qobject::T`](./generated-qobject.m
{{#include ../../../examples/qml_features/rust/src/threading.rs:book_qt_thread}}
```

Note that locking must not be disabled for the object (eg `unsafe impl cxx_qt::Locking for qobject::T`) for `cxx_qt::Threading` to be allowed.

Then to access the `CxxQtThread<T>` use the `qt_thread(&self)` method on a [`qobject::T`](./generated-qobject.md).

```rust,ignore,noplayground
Expand Down
33 changes: 21 additions & 12 deletions crates/cxx-qt-gen/src/generator/cpp/invokable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use crate::{
fragment::{CppFragment, CppNamedType},
qobject::GeneratedCppQObjectBlocks,
types::CppType,
RUST_OBJ_MUTEX_LOCK_GUARD,
},
naming::{invokable::QInvokableName, qobject::QObjectName},
},
Expand All @@ -25,6 +24,7 @@ pub fn generate_cpp_invokables(
invokables: &Vec<ParsedQInvokable>,
qobject_idents: &QObjectName,
cxx_mappings: &ParsedCxxMappings,
lock_guard: Option<&str>,
) -> Result<GeneratedCppQObjectBlocks> {
let mut generated = GeneratedCppQObjectBlocks::default();
let qobject_ident = qobject_idents.cpp_class.cpp.to_string();
Expand Down Expand Up @@ -132,7 +132,7 @@ pub fn generate_cpp_invokables(
is_const = is_const,
parameter_types = parameter_types,
qobject_ident = qobject_ident,
rust_obj_guard = RUST_OBJ_MUTEX_LOCK_GUARD,
rust_obj_guard = lock_guard.unwrap_or_default(),
body = if return_cxx_ty.is_some() {
format!("return {body}", body = body)
} else {
Expand Down Expand Up @@ -202,9 +202,13 @@ mod tests {
];
let qobject_idents = create_qobjectname();

let generated =
generate_cpp_invokables(&invokables, &qobject_idents, &ParsedCxxMappings::default())
.unwrap();
let generated = generate_cpp_invokables(
&invokables,
&qobject_idents,
&ParsedCxxMappings::default(),
Some("// ::std::lock_guard"),
)
.unwrap();

// methods
assert_eq!(generated.methods.len(), 4);
Expand All @@ -221,7 +225,7 @@ mod tests {
void
MyObject::voidInvokable() const
{
const ::std::lock_guard<::std::recursive_mutex> guard(*m_rustObjMutex);
// ::std::lock_guard
m_rustObj->voidInvokableWrapper(*this);
}
"#}
Expand All @@ -242,7 +246,7 @@ mod tests {
::std::int32_t
MyObject::trivialInvokable(::std::int32_t param) const
{
const ::std::lock_guard<::std::recursive_mutex> guard(*m_rustObjMutex);
// ::std::lock_guard
return m_rustObj->trivialInvokableWrapper(*this, param);
}
"#}
Expand All @@ -263,7 +267,7 @@ mod tests {
::std::unique_ptr<QColor>
MyObject::opaqueInvokable(QColor const& param)
{
const ::std::lock_guard<::std::recursive_mutex> guard(*m_rustObjMutex);
// ::std::lock_guard
return m_rustObj->opaqueInvokableWrapper(*this, param);
}
"#}
Expand All @@ -284,7 +288,7 @@ mod tests {
::std::int32_t
MyObject::specifiersInvokable(::std::int32_t param) const
{
const ::std::lock_guard<::std::recursive_mutex> guard(*m_rustObjMutex);
// ::std::lock_guard
return m_rustObj->specifiersInvokableWrapper(*this, param);
}
"#}
Expand Down Expand Up @@ -312,8 +316,13 @@ mod tests {
.cxx_names
.insert("B".to_owned(), "B2".to_owned());

let generated =
generate_cpp_invokables(&invokables, &qobject_idents, &cxx_mappings).unwrap();
let generated = generate_cpp_invokables(
&invokables,
&qobject_idents,
&cxx_mappings,
Some("// ::std::lock_guard"),
)
.unwrap();

// methods
assert_eq!(generated.methods.len(), 1);
Expand All @@ -330,7 +339,7 @@ mod tests {
B2
MyObject::trivialInvokable(A1 param) const
{
const ::std::lock_guard<::std::recursive_mutex> guard(*m_rustObjMutex);
// ::std::lock_guard
return m_rustObj->trivialInvokableWrapper(*this, param);
}
"#}
Expand Down
3 changes: 0 additions & 3 deletions crates/cxx-qt-gen/src/generator/cpp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ use crate::parser::Parser;
use qobject::GeneratedCppQObject;
use syn::Result;

pub const RUST_OBJ_MUTEX_LOCK_GUARD: &str =
"const ::std::lock_guard<::std::recursive_mutex> guard(*m_rustObjMutex);";

/// Representation of the generated C++ code for a group of QObjects
pub struct GeneratedCppBlocks {
/// Stem of the CXX header to include
Expand Down
11 changes: 8 additions & 3 deletions crates/cxx-qt-gen/src/generator/cpp/property/getter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::generator::{
cpp::{fragment::CppFragment, types::CppType, RUST_OBJ_MUTEX_LOCK_GUARD},
cpp::{fragment::CppFragment, types::CppType},
naming::property::QPropertyName,
};
use indoc::formatdoc;

pub fn generate(idents: &QPropertyName, qobject_ident: &str, cxx_ty: &CppType) -> CppFragment {
pub fn generate(
idents: &QPropertyName,
qobject_ident: &str,
cxx_ty: &CppType,
lock_guard: Option<&str>,
) -> CppFragment {
CppFragment::Pair {
header: format!(
"{return_cxx_ty} const& {ident_getter}() const;",
Expand All @@ -28,7 +33,7 @@ pub fn generate(idents: &QPropertyName, qobject_ident: &str, cxx_ty: &CppType) -
return_cxx_ty = cxx_ty.as_cxx_ty(),
ident_getter = idents.getter.cpp.to_string(),
qobject_ident = qobject_ident,
rust_obj_guard = RUST_OBJ_MUTEX_LOCK_GUARD,
rust_obj_guard = lock_guard.unwrap_or_default(),
),
}
}
50 changes: 33 additions & 17 deletions crates/cxx-qt-gen/src/generator/cpp/property/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub fn generate_cpp_properties(
properties: &Vec<ParsedQProperty>,
qobject_idents: &QObjectName,
cxx_mappings: &ParsedCxxMappings,
lock_guard: Option<&str>,
) -> Result<GeneratedCppQObjectBlocks> {
let mut generated = GeneratedCppQObjectBlocks::default();
let qobject_ident = qobject_idents.cpp_class.cpp.to_string();
Expand All @@ -28,12 +29,18 @@ pub fn generate_cpp_properties(
let cxx_ty = CppType::from(&property.ty, cxx_mappings)?;

generated.metaobjects.push(meta::generate(&idents, &cxx_ty));
generated
.methods
.push(getter::generate(&idents, &qobject_ident, &cxx_ty));
generated
.methods
.push(setter::generate(&idents, &qobject_ident, &cxx_ty));
generated.methods.push(getter::generate(
&idents,
&qobject_ident,
&cxx_ty,
lock_guard,
));
generated.methods.push(setter::generate(
&idents,
&qobject_ident,
&cxx_ty,
lock_guard,
));
generated.methods.push(signal::generate(&idents));
}

Expand Down Expand Up @@ -67,9 +74,13 @@ mod tests {
];
let qobject_idents = create_qobjectname();

let generated =
generate_cpp_properties(&properties, &qobject_idents, &ParsedCxxMappings::default())
.unwrap();
let generated = generate_cpp_properties(
&properties,
&qobject_idents,
&ParsedCxxMappings::default(),
Some("// ::std::lock_guard"),
)
.unwrap();

// metaobjects
assert_eq!(generated.metaobjects.len(), 2);
Expand All @@ -90,7 +101,7 @@ mod tests {
::std::int32_t const&
MyObject::getTrivialProperty() const
{
const ::std::lock_guard<::std::recursive_mutex> guard(*m_rustObjMutex);
// ::std::lock_guard
return m_rustObj->getTrivialProperty(*this);
}
"#}
Expand All @@ -111,7 +122,7 @@ mod tests {
void
MyObject::setTrivialProperty(::std::int32_t const& value)
{
const ::std::lock_guard<::std::recursive_mutex> guard(*m_rustObjMutex);
// ::std::lock_guard
m_rustObj->setTrivialProperty(*this, value);
}
"#}
Expand All @@ -137,7 +148,7 @@ mod tests {
::std::unique_ptr<QColor> const&
MyObject::getOpaqueProperty() const
{
const ::std::lock_guard<::std::recursive_mutex> guard(*m_rustObjMutex);
// ::std::lock_guard
return m_rustObj->getOpaqueProperty(*this);
}
"#}
Expand All @@ -158,7 +169,7 @@ mod tests {
void
MyObject::setOpaqueProperty(::std::unique_ptr<QColor> const& value)
{
const ::std::lock_guard<::std::recursive_mutex> guard(*m_rustObjMutex);
// ::std::lock_guard
m_rustObj->setOpaqueProperty(*this, value);
}
"#}
Expand Down Expand Up @@ -186,8 +197,13 @@ mod tests {
.cxx_names
.insert("A".to_owned(), "A1".to_owned());

let generated =
generate_cpp_properties(&properties, &qobject_idents, &cxx_mapping).unwrap();
let generated = generate_cpp_properties(
&properties,
&qobject_idents,
&cxx_mapping,
Some("// ::std::lock_guard"),
)
.unwrap();

// metaobjects
assert_eq!(generated.metaobjects.len(), 1);
Expand All @@ -207,7 +223,7 @@ mod tests {
A1 const&
MyObject::getMappedProperty() const
{
const ::std::lock_guard<::std::recursive_mutex> guard(*m_rustObjMutex);
// ::std::lock_guard
return m_rustObj->getMappedProperty(*this);
}
"#}
Expand All @@ -225,7 +241,7 @@ mod tests {
void
MyObject::setMappedProperty(A1 const& value)
{
const ::std::lock_guard<::std::recursive_mutex> guard(*m_rustObjMutex);
// ::std::lock_guard
m_rustObj->setMappedProperty(*this, value);
}
"#}
Expand Down
11 changes: 8 additions & 3 deletions crates/cxx-qt-gen/src/generator/cpp/property/setter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::generator::{
cpp::{fragment::CppFragment, types::CppType, RUST_OBJ_MUTEX_LOCK_GUARD},
cpp::{fragment::CppFragment, types::CppType},
naming::property::QPropertyName,
};
use indoc::formatdoc;

pub fn generate(idents: &QPropertyName, qobject_ident: &str, cxx_ty: &CppType) -> CppFragment {
pub fn generate(
idents: &QPropertyName,
qobject_ident: &str,
cxx_ty: &CppType,
lock_guard: Option<&str>,
) -> CppFragment {
CppFragment::Pair {
header: format!(
"Q_SLOT void {ident_setter}({cxx_ty} const& value);",
Expand All @@ -28,7 +33,7 @@ pub fn generate(idents: &QPropertyName, qobject_ident: &str, cxx_ty: &CppType) -
cxx_ty = cxx_ty.as_cxx_ty(),
ident_setter = idents.setter.cpp,
qobject_ident = qobject_ident,
rust_obj_guard = RUST_OBJ_MUTEX_LOCK_GUARD,
rust_obj_guard = lock_guard.unwrap_or_default(),
},
}
}
11 changes: 11 additions & 0 deletions crates/cxx-qt-gen/src/generator/cpp/qobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ pub struct GeneratedCppQObject {
pub base_class: String,
/// The blocks of the QObject
pub blocks: GeneratedCppQObjectBlocks,
/// Whether locking is enabled
pub locking: bool,
}

impl GeneratedCppQObject {
Expand All @@ -93,24 +95,33 @@ impl GeneratedCppQObject {
.clone()
.unwrap_or_else(|| "QObject".to_string()),
blocks: GeneratedCppQObjectBlocks::from(qobject),
locking: qobject.locking,
};
let lock_guard = if qobject.locking {
Some("const ::std::lock_guard<::std::recursive_mutex> guard(*m_rustObjMutex);")
} else {
None
};

// Generate methods for the properties, invokables, signals
generated.blocks.append(&mut generate_cpp_properties(
&qobject.properties,
&qobject_idents,
cxx_mappings,
lock_guard,
)?);
generated.blocks.append(&mut generate_cpp_invokables(
&qobject.invokables,
&qobject_idents,
cxx_mappings,
lock_guard,
)?);
if let Some(signals_enum) = &qobject.signals {
generated.blocks.append(&mut generate_cpp_signals(
&signals_enum.signals,
&qobject_idents,
cxx_mappings,
lock_guard,
)?);
}
generated.blocks.append(&mut inherit::generate(
Expand Down
Loading

0 comments on commit 58d2d58

Please sign in to comment.