[SOLVED] Segfault on "Esc" from Command

Discussion forum for C++ and script developers who are using the QCAD development platform or who are looking to contribute to QCAD (translations, documentation, etc).

Moderator: andrew

Forum rules

Always indicate your operating system and QCAD version.

Attach drawing files, scripts and screenshots.

Post one question per topic.

Post Reply
Nava2
Junior Member
Posts: 15
Joined: Sun Oct 26, 2014 6:01 pm

[SOLVED] Segfault on "Esc" from Command

Post by Nava2 » Sun Mar 01, 2015 2:54 am

I've been having a consistent segfault when I try to cancel a command.

Flow:
  1. Click shapes
  2. Click rectangle from points
  3. Draw rectangle on canvas
  4. Enter or Right click
  5. Escape to exit command back to selection tool Segfault
I'm fairly confident this is due to my code, but I always get the issue here:
void RDocumentInterface::deleteTerminatedActions() {
    bool removed = false;

    while (currentActions.size()>0 && currentActions.top()->isTerminated()) {
        cursorPosition = RVector::invalid;
        RAction* currentAction = currentActions.top();
        currentAction->finishEvent();
        setClickMode(RAction::PickingDisabled);
        // remember GUI action group:
        QString group;
        if (currentAction->getGuiAction()!=NULL && // currentAction is null
            !currentAction->getGuiAction()->getGroup().isEmpty() &&
            currentAction->isOverride()) {

            group = currentAction->getGuiAction()->getGroup();
        }

        currentActions.pop();
        delete currentAction;

        if (!group.isEmpty()) {
            RGuiAction::triggerGroupDefault(group);
        }
        removed = true;
    }

    // if one or more actions have been terminated, resume previous action
    // or default action:
    if (removed) {
        if (currentActions.size()>0) {
            currentActions.top()->resumeEvent();
        }
        else if (defaultAction!=NULL) {
            defaultAction->resumeEvent();
        }
    }

    // run next queued action:
    if (queuedActions.size()>0) {
        setCurrentAction(queuedActions.dequeue());
    }
}
Or, I get an error around this function:
/**
 * Repaints all views.
 */
void RDocumentInterface::repaintViews() {
    if (deleting) {
        return;
    }
    QList<RGraphicsScene*>::iterator it;
    for (it=scenes.begin(); it!=scenes.end(); it++) {
        (*it)->repaintViews();
    }
}
This has issues because there is a NULL RGraphicsScene pointer or just an invalid pointer (i.e. it's already been freed). I do not call repaintViews() explicitly anywhere, so this is coming from somewhere else (the debug trace is useless since its via EMCAScript).

The log file from a debug build is here:

Code: Select all

Debugging starts
&"warning: GDB: Failed to set controlling terminal: Inappropriate ioctl for device\n"
Debug:    RDxfPlugin::init 
Debug:    RExamplePlugin::init 
Debug:    TransactionListenerPlugin::init 
Debug:    loading patterns from file:  "/home/kevin/workspace/qcad/linetypes/metric/qcadiso.lin" 
Debug:    loading patterns from file:  "/home/kevin/workspace/qcad/linetypes/imperial/qcad.lin" 
Warning:  RScriptHandlerEcma::RScriptHandlerEcma: script debugger enabled! Not recommended. 
Debug:    TIMER:  86 ms -  "loading add-ons" 
Warning:  "QFormBuilder was unable to create a custom widget of the class 'QWebView'; defaulting to base class 'QWidget'." 
Debug:    TIMER:  377 ms -  "initializing add-ons" 
Debug:    TransactionListenerPlugin::postInit 
Warning:  RScriptHandlerEcma::RScriptHandlerEcma: script debugger enabled! Not recommended. 
Debug:    got transaction 
Debug:    got transaction 
Debug:    IDs of objects that were changed:  () 
Debug:    List of changed objects: 
Debug:    End of list 
Debug:    got transaction 
Debug:    IDs of objects that were changed:  (4, 44, 45, 46, 47) 
Debug:    List of changed objects: 
Debug:    Changed object:  RBlock(RObject(id: 4, handle: "0x4", document: "0x5849eb0", address: "0x5e02440", undone: 0, protected: 0), name: "*Model_Space", origin: RVector(0, 0, 0, true), frozen: false, anonymous: false)
Debug:    Changed object:  RLineEntity(REntity(RObject(id: 44, handle: "0x2c", document: "0x5849eb0", address: "0x8ea5df0", undone: 0, protected: 0), type: 7, layerId: 3, blockId: 4, parentId: -1, childIds: QSet() , lineweight:  -1 , linetypeId:  0 , linetypeScale:  1 , color:  RColor(ByLayer) , drawOrder:  0 , selectionStatus:  false , boundingBoxes:  (RBox(RVector(0, 0, 0, true) - RVector(90, 0, 0, true)) )  ) , startPoint: RVector(0, 0, 0, true), endPoint: RVector(90, 0, 0, true))
Debug:    Changed object:  RLineEntity(REntity(RObject(id: 45, handle: "0x2d", document: "0x5849eb0", address: "0x8ea5df0", undone: 0, protected: 0), type: 7, layerId: 3, blockId: 4, parentId: -1, childIds: QSet() , lineweight:  -1 , linetypeId:  0 , linetypeScale:  1 , color:  RColor(ByLayer) , drawOrder:  1 , selectionStatus:  false , boundingBoxes:  (RBox(RVector(90, 0, 0, true) - RVector(90, 30, 0, true)) )  ) , startPoint: RVector(90, 0, 0, true), endPoint: RVector(90, 30, 0, true))
Debug:    Changed object:  RLineEntity(REntity(RObject(id: 46, handle: "0x2e", document: "0x5849eb0", address: "0x8ea5df0", undone: 0, protected: 0), type: 7, layerId: 3, blockId: 4, parentId: -1, childIds: QSet() , lineweight:  -1 , linetypeId:  0 , linetypeScale:  1 , color:  RColor(ByLayer) , drawOrder:  2 , selectionStatus:  false , boundingBoxes:  (RBox(RVector(0, 30, 0, true) - RVector(90, 30, 0, true)) )  ) , startPoint: RVector(90, 30, 0, true), endPoint: RVector(0, 30, 0, true))
Debug:    Changed object:  RLineEntity(REntity(RObject(id: 47, handle: "0x2f", document: "0x5849eb0", address: "0x8ea5df0", undone: 0, protected: 0), type: 7, layerId: 3, blockId: 4, parentId: -1, childIds: QSet() , lineweight:  -1 , linetypeId:  0 , linetypeScale:  1 , color:  RColor(ByLayer) , drawOrder:  3 , selectionStatus:  false , boundingBoxes:  (RBox(RVector(0, 0, 0, true) - RVector(0, 30, 0, true)) )  ) , startPoint: RVector(0, 30, 0, true), endPoint: RVector(0, 0, 0, true))
Debug:    End of list 
My script does not create any actions aside from one that shows a Widget then finishes the action immediately.

I'm looking for any insight in to what might be causing a null action to be put onto the stack.

Edit:
I've been digging around, and I've am fairly confident that the issue is that something is being deleted by the EMCA script interpretter within the action stack that is not cleaning itself up within the C++ side. Some action is trying to get it's own GUI action, but the action was already deleted by the script engine.

I have no idea what would cause this.

Side note, I'm unsure if this was intentional, but I did find this:
RGraphicsScene::~RGraphicsScene() {
    deleting = true;
    while (!views.isEmpty()) {
        delete views.takeFirst();
    }

    documentInterface.unregisterScene(*this); // this was missing, I added it because the ctor registers
}
Edit2: I think it might be a race condition, it only seems to be consistent with pressing "Esc" multiple times quickly.
Last edited by Nava2 on Fri Mar 06, 2015 6:57 am, edited 1 time in total.

Nava2
Junior Member
Posts: 15
Joined: Sun Oct 26, 2014 6:01 pm

Re: Segfault on "Esc" from Command

Post by Nava2 » Sun Mar 01, 2015 4:14 am

I've found one issue, and I have a "hacky" fix to it. The issue surrounding bad actions is because there is a race condition between threads deleting actions on the action stack.

I was unable to catch multiple threads in the act, but I found one of the segfaults was inside the loop in the following code segment while the stack used in the condition was empty.
QMutex _mutex(QMutex::Recursive); // This should not be defined here

/**
 * Deletes all actions that have been terminated.
 */
void RDocumentInterface::deleteTerminatedActions() {
    bool removed = false;

    QMutexLocker locker(&_mutex);
    while (currentActions.size()>0 && currentActions.top()->isTerminated()) { // this condition was passing
        cursorPosition = RVector::invalid;
        RAction* currentAction = currentActions.top(); // this pointer was valid initially, but another thread was deleting the pointer before it was used below

        currentAction->finishEvent(); 
        setClickMode(RAction::PickingDisabled);
        // remember GUI action group:
        QString group;
        if (currentAction->getGuiAction() != NULL && // HERE
            !currentAction->getGuiAction()->getGroup().isEmpty() &&
            currentAction->isOverride()) {
   
            group = currentAction->getGuiAction()->getGroup();
        }

        currentActions.pop();
        delete currentAction;

        if (!group.isEmpty()) {
            RGuiAction::triggerGroupDefault(group);
        }
        removed = true;
    }

    // if one or more actions have been terminated, resume previous action
    // or default action:
    if (removed) {
        if (currentActions.size()>0) {
            currentActions.top()->resumeEvent();
        }
        else if (defaultAction!=NULL) {
            defaultAction->resumeEvent();
        }
    }

    // run next queued action:
    if (queuedActions.size()>0) {
        setCurrentAction(queuedActions.dequeue());
    }
}
I'm unsure if locking it is the solution, but the problem is most definitely a race condition.

This does not solve the issue of invalid iterators on the `scene` field.

Nava2
Junior Member
Posts: 15
Joined: Sun Oct 26, 2014 6:01 pm

Re: Segfault on "Esc" from Command

Post by Nava2 » Sun Mar 01, 2015 5:15 am

After a full rebuild, these changes seem to fix the issues I had.

https://gist.github.com/Nava2/478996185c1ce846bff3

I should probably open a bug on the tracker.

User avatar
andrew
Site Admin
Posts: 9036
Joined: Fri Mar 30, 2007 6:07 am

Re: Segfault on "Esc" from Command

Post by andrew » Mon Mar 02, 2015 2:29 pm

You appear to be running QCAD with the script debugger enabled:
Warning:  RScriptHandlerEcma::RScriptHandlerEcma: script debugger enabled! Not recommended
This is not recommended unless you need the script debugger. Any crashes in this mode will not be fixed. The reason for these crashes is that the Qt debugger essentially processes events while executing scripts. This means that you can for example run a script which adds 1000 lines to a drawing and while the script is running the user can close the current drawing, open another one, etc. This can easily lead to a situation in which a drawing document is being destroyed while a script is still using it, leading to such crashes.

If you experience any problems without -enable-script-debugger, please post back.

Nava2
Junior Member
Posts: 15
Joined: Sun Oct 26, 2014 6:01 pm

Re: Segfault on "Esc" from Command

Post by Nava2 » Fri Mar 06, 2015 6:56 am

Sorry, Andrew, I meant to reply and say that yes, you're correct. It is not an issue outside the debugging mode.

Post Reply

Return to “QCAD Programming, Script Programming and Contributing”