Skip to content

add DeferCall to replace QMetaObject::invokeMethod#48101

Merged
jkarneges merged 4 commits intomainfrom
jkarneges/defercall
Jan 23, 2025
Merged

add DeferCall to replace QMetaObject::invokeMethod#48101
jkarneges merged 4 commits intomainfrom
jkarneges/defercall

Conversation

@jkarneges
Copy link
Member

@jkarneges jkarneges commented Jan 22, 2025

This helps reduce direct dependence on QObject by wrapping QMetaObject::invokeMethod with our own internal API for doing the same. It introduces the DeferCall type and updates a few places to use it. The goal is to use DeferCall for all delayed invocations in the entire project, such that the only direct usage of invokeMethod occurs within DeferCall itself. Then when we replace the Qt event loop we can substitute out that final usage.

Unlike invokeMethod, which takes a method name by string and a variable number of dynamically typed args, DeferCall::defer simply takes a function/closure. This allows static type-checking, and any data that would have normally been supplied by args can be captured instead.

A single DeferCall instance can be used to queue multiple calls. Destroying it will cancel the calls. Objects that need to make deferred calls to themselves are expected to do it through a DeferCall kept as a member variable. That way, when the object is destroyed so are its queued calls.

DeferCall also provides a replacement for QObject::deleteLater, via the static method DeferCall::deleteLater. Deferred deletions are often applied to child objects during parent object destruction, so it is important for the queued calls to remain in effect after the parent object goes away. To help with that, these calls are made against a singleton (thread local) instead of a member instance. DeferCall::cleanup is used to destroy the singleton at thread/program exit.

Tested with valgrind and came up clean.

Comment on lines +61 to +62
Call c = deferredCalls_.front();
deferredCalls_.pop_front();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will execute a list of deferred calls in First-In-First-Out order, which seems to make sense to me. But defer in go executes in reverse order. I can't recall the specifics of any other languages right now, but I wonder if there's an expectation for one or other that might cause problems in the future, especially when using the deleteLater feature with complex data structures.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point regarding deleteLater. Will double-check Qt's behavior before landing this, since the goal is for it to work similarly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed the deletions are in-order.

There is one exception: if there are nested event loops, deferred deletions only occur within the event loop that they were originally queued. So processing of deletions within an event loop is FIFO, but processing of the loops themselves is LIFO. In any case, we don't use nested event loops.

@jkarneges jkarneges merged commit 24c4ef3 into main Jan 23, 2025
19 checks passed
@jkarneges jkarneges deleted the jkarneges/defercall branch January 23, 2025 18:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants