QtCurve: avoid crashing/hanging on exit
ClosedPublic

Authored by rjvbb on Dec 6 2017, 3:53 PM.

Details

Summary

There have been reports of QtCurve causing applications to hang or crash on exit, and I have certainly seen examples of that myself.

The main reason for such events appears to be delivery of signals to stale QtCurve::Style or plugin instances, or Qt accessing stale objects internally.

Some changes have already been made to reduce the chance such issues occur, this patch should help avoid even more.

The new features are that QtCurve now disconnects from receiving select signals when the shutdown phase starts (currently a signal signal only) and a registry allowing the style plugin to avoid leaving orphaned style instances after it has been deleted itself.

Implementation detail: the patch introduces a d pointer to a private subclass, currently containing only shutdown related private variables moved from the main class. Over time this private class could further unburden the main style class.

Test Plan

has the intended effects on Mac and Linux

Diff Detail

Repository
R626 QtCurve
Lint
Automatic diff as part of commit; lint not applicable.
Unit
Automatic diff as part of commit; unit tests not applicable.
rjvbb requested review of this revision.Dec 6 2017, 3:53 PM
rjvbb created this revision.
ngraham added a subscriber: ngraham.Dec 6 2017, 5:53 PM
rjvbb updated this revision to Diff 23621.Dec 7 2017, 5:05 PM

moved the private class definition to its own header (in preparation of further unrelated changes)

rjvbb set the repository for this revision to R626 QtCurve.Dec 7 2017, 5:05 PM

Implementation detail: the patch introduces a d pointer to a private subclass,

Why is this needed? IIUC, the private subclass pattern is only useful for hiding private members from public API/ABI. However, in this case, the whole class is basically private (nothing else should be accessing the members either directly or indirectly) so I don't think this is necessary.

rjvbb added a comment.Dec 7 2017, 9:24 PM

In this case it's really an implementation detail, meant to unburden the main class which is becoming unwieldy, and make more isolated changes that don't require rebuilding the majority of the plugin each time you make a small change. Ultimately I think it's a princple that allows cleaner coding, not just by hiding things from public API but also by exposing internal things only where they're needed.

So is it needed, no. Is it useful, yes, I think so, though of course the current change is too small to have a real impact. I'm working on an unrelated follow-up that will give this private class a bit more justification.

Maybe we could change the subclass name, and the pointer to it?

In this case it's really an implementation detail, meant to unburden the main class which is becoming unwieldy, and make more isolated changes that don't require rebuilding the majority of the plugin each time you make a small change.

Putting everything in a different class will not help.

Ultimately I think it's a princple that allows cleaner coding, not just by hiding things from public API but also by exposing internal things only where they're needed.

I disagree. Basically everything in here is internal so following that will basically make the original class a thin wrapper and put all code in a real class.

If there's a group of field/functions that are closely coupled and not to anything else, those can certainly be put in a separate file/class and be a field of the main class. Such fields should also be grouped by the function they provide and have a corresponding name and should be created very conservatively, much more conservatively than adding a private pointer for other classes since there isn't a strong reason to do so (like hiding implementation details from public API/ABI).

So is it needed, no. Is it useful, yes, I think so, though of course the current change is too small to have a real impact. I'm working on an unrelated follow-up that will give this private class a bit more justification.

Given what I said above, is the current form harmful, no. Will putting more things in there be harmful, yes. That's because there's no methods (apart from a few override methods that are the public API) and no fields that are not "private" so adding a private pointer to a private object is strictly confusing.

Maybe we could change the subclass name, and the pointer to it?

As I said above, separate according to function is fine, so it would be fine if it is done on a group of fields/functions that are very self-contained.
It will be a trade-off between making it easier to find things closely related (i.e. the code that are separated out) and making it harder to find the caller/callee or find this code from caller/callee.

rjvbb updated this revision to Diff 23626.Dec 7 2017, 10:44 PM

naming change to avoid potential confusion in interpretation of the class's function but also with the KStyle d pointer (which I had missed, TBH).

rjvbb set the repository for this revision to R626 QtCurve.Dec 7 2017, 10:45 PM

This is a qt bug that I can't reproduce and the change looks safe so LGTM....

qt5/style/qtcurve.h
526

Very minor but this should be called m_dbusHelper. It won't be an issue if not for the much more established meaning of DB and I've got confused for a while when seeing it on another line first.

yuyichao accepted this revision.Dec 7 2017, 10:55 PM
This revision is now accepted and ready to land.Dec 7 2017, 10:55 PM
rjvbb added a comment.Dec 8 2017, 8:57 AM
This is a qt bug that I can't reproduce and the change looks safe so LGTM....

Yes, it addresses an issue that occurs only very sporadically. If memory serves me well the situation where orphaned style instances remain after unloading the plugin occurred a bit more often but usually didn't lead to issues. Or they didn't until we introduced the access to the plugin instance from style instances, I don't recall. Either way, I have been testing this long enough to forget the details, clearly.

I've reduced the footprint a bit. There's no point for the DBusHelper class to have its own header file since it'll only ever be used from the main style module.

This revision was automatically updated to reflect the committed changes.