Achieving thread-safe destruction of generated QObject #1240
-
I am aware that the documentation mentions that cxx-qt generated objects are not thread-safe. I am hoping to add some protection for limited thread-safety on certain members. In my use-case, one invokable on the object will be called from another thread. I use a Rust arc+mutex on the struct members which are accessed from this invokable. However, I run into the following issue: I cannot prevent the object being destroyed on the GUI thread unsafely. A use-after-free can occur where the invokable tries to access the rust struct while it is already being destructed. No amount of mutexes on struct members can prevent this, since the rust struct itself may already be freed. Do you have any suggestion on how to handle this? |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 3 replies
-
Are you able to change how you call the invokable on the object at all ? Usually in Qt C++ classes you ensure that you either use a connection between a signal and a slot/invokable so that the connection automatically queues the slot/invokable side if they are in different threads. Or you use something like Maybe a short code example would also help, eg i don't know if you need to manually change the ownership of the QObject (as you can choose to have C++ or QML ownership) or how/where the QObject is being created. |
Beta Was this translation helpful? Give feedback.
-
You have a point that I may be going about this wrong. Instead of calling methods from different threads, maybe I should look into splitting into multiple objects which only operate on one thread each, and connecting them safely with signals. It would be quite a lot of extra code though, and would basically duplicate state in both of the objects. For a code example I would need to minimize it first. But the gist is that properties of the object are atomically, asynchronously updated periodically on a different thread. A use-after-free often occurs when the object is destroyed on the GUI thread, just as the update thread is trying to access the underlying Rust struct. |
Beta Was this translation helpful? Give feedback.
-
@SanderVocke with CXX-Qt we aim to enforce existing Qt best-practices to allow QObjects to be used safely from Rust. One of these best practices is that QObjects are owned by a specific thread. They are usually not thread-safe and should not be accessed from other threads. If you want to access an invokable, you should rather queue the invocation back onto the thread that owns the QObject (e.g. via CxxQtThread or an auto connection). However, if you just want to modify the inner Rust data anyway, you can use another level of indirection inside the Rust backend. E.g. your Rust struct could just look like this: struct MyObjectRust {
data: Arc<Mutex<MyObjectPrivate>>
}
struct MyObjectPrivate {
my_thing: i32
}
impl qobject::MyObject {
fn my_fun(&self) {
let data = Arc::clone(&self.data);
std::thread::spawn(|| {
let data = data.lock().unwrap();
my_thing = 5;
}
}
} You can then have as many references to the I hope that helps :) |
Beta Was this translation helpful? Give feedback.
As for the use-case: it is for a realtime audio app, where the object should keep getting updated and executing part of its functionality even if the GUI thread hangs.
I think the best approach for this would be to totally separate the asynchronous code into a separate object.