PLUGINS: Crash when using methods returning QObject*'s (Qt 5.2.1 regression?)

• Jul 4, 2014 - 09:21
Type
Functional
Severity
S4 - Minor
Status
closed
Project

Context: Today commit self-compiled under Linux Mint 17, Qt 5.2.1.

This issue may be replicable or not, as it very probably depends on individual system configuration and characteristics. I myself I am unable to replicate it on a Linux Mint 14 (Qt 5.1.0) running on the same machine, without some degree of 'tricks'.

Steps:

  1. Load the attached test_plugin_accid.mscz test score; it is a score with just one accidental, one note dot and one note slur.
  2. Run the attached test_accid.qml test plugin. This plugin is a modification of the colornotes.qml plugin coming with MuseScore itself and running wthout problems. It just replaces the contents of the colornotes() function with a few lines which test the presence of a note accidental and do nothing on it or with it.

Result: once the plugin finishes, MuseScore crashes while attempting a global layout.

Note: The test score is the shortest score for which the issue shows up on my system: shorter scores (even by removing a single measure) do not trigger the bug. For the reasons explained below, longer scores might be needed on different systems.

Analysis: analysis has been difficult. The crash happens while attempting to layout (or to insert in the page Btree) an Element which is referenced as accidental by a note, but it is not (or no longer) really an accidental.

By setting a data change breakpoint on the address of the (unique) score accidental, it can be seen that the accidental itself is deleted 'somewhen' during or after plugin execution, leaving a wild pointer in the parent note object. The stack trace shows that the deletion does not happen in some MuseScore function, but in a long stack of system or Qt calls.

The inference is that the accidental, once referenced by the Qml statement, has its ownership transferred to QML and is garbage collected at some point. This is consistent with this sentence in QML QQmlEngine::ObjectOwnership discussion in the Qt docs :

"Objects not-created by QML have CppOwnership by default. The exception to this is objects returned from C++ method calls; in these cases, the ownership of the returned objects will be set to JavaScriptOwnerShip. Note this applies only to explicit invocations of Q_INVOKABLE methods or slots, and not to property getter invocations.".

This explains why the bug shows in different context on different systems: g.c. is presumably triggered differently. Also, if the bug does not show up 'spontaneously', it may be forced by adding a line:
      gc();       // call JavaScript own garbage collector
right after the call to func(note) in function applyToNotesInSelection(func): execution will be much slower, but sooner or later the program will crash (this is how I could replicate the bug in my other system).

Also, the same happens if -- instead of note.accidental() -- note.dot(0) or note.tieFor/Back() are used: all these methods returns pointers to QObject's via Q_INVOKABLE functions.

Fix

The easiest fix is to convert Q_INVOKABLE methods which return QObject*'s into properties. I tested it and it works. I have a patch ready with this fix and I'll post it to Github.

An alternative may be altering the object ownership parameters of the QQmlEngine created by MuseScore, but it is not obvious how.

Any further comment is welcome.

Thanks,

Maurizio

Attachment Size
test_plugin_accid.mscz 2.64 KB
test_accid.qml 3.1 KB

Comments

I see only one Q_INVOKABLE that returns a QObject*:
Q_INVOKABLE QObject* currentScore();
Or are you talking about Q_INVOKABLEs returning pointers to something?
There are quite a few of those...

Status (old) active patch (code needs review)

(I thought I already posted this comment, but possibly I forgot to push some button...)

I was talking about Q_INVOKABLE returning pointers to [objects derived from] QObject: they all look like QObject*'s, as far as QML is concerned.

M.

And I think this is the same problem that crashes the "walk" plugin. The log files from AddressSanitizer are similar for this example and the walk plugin.