Memory Leak with TagData and GeDialog



  • On 11/01/2018 at 07:49, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   R19 
    Platform:      Mac OSX  ; 
    Language(s) :     C++  ;

    ---------
    Hi everyone,

    I'm using a GeDialog attached to a tag, catching MSG_EDIT to open it. So far so good.
    Everything run as i wish, but when I modify parameters on the tag itself i have memory leak.

    I've tried to isolate the problem creating an exemple as simple as possible to reproduce the problem.

    In the exemple, i've used GetDDescription() to create the UI but the problem is the same with .res/.h/.str

    This is where it get strange :
     - If i play with several parameters except the CheckBox   --> no memory leak.
     - If i play with several parameters and the CheckBox -- > memory leak.
     - If i only play with the checkbox i got no leak. (even if i got several checkbox)

    - If i remove the GeDialog variable i got no leak and using a pointer to the dialog doesn't change anything.

    I've check the way i initialise the Checkbox but even without using GeData(true) and only true it doesn't change anything.

    The main.cpp and h, there nothing except the call to registerplugin and resource.Init()

    In the exemple, the MYDIALOG class got nothing, but i didn't found anything there that could help.
    I've include everything in one file (except the main.cpp)

    I removed all other plugins to be sure.

    If anybody got a hint.

    //  memleak.cpp
    #include "c4d.h"
    // test ID
    #define PLUGIN_ID 1000002
    enum {
        
        MY_DYN_POINT_NUMBER = 1000,
      
        MY_DYN_BOOL,
        MY_DYN_BOOL_ONE,
        MY_DYN_BOOL_TWO,
        MY_DYN_SPLINE_OUTPUT,
        
        MY_DYN_REAL,
        MY_DYN_REAL_SLIDER,
        MY_DYN_ANOTHER_REAL,
        
        MY_BUTTON_ID,
      
        
        
    };
      
    class MYDIALOG : public GeDialog
    {
        
        
    };
      
      
    class MyTag : public TagData
    {
        
        INSTANCEOF(MyTag, TagData);
      
    private:
        MYDIALOG dlg;
        
    public:
        virtual     Bool				Init(GeListNode* node);
        virtual     void                Free(GeListNode* node);
        virtual     Bool				GetDDescription(GeListNode* node, Description* description, DESCFLAGS_DESC& flags);
        static      NodeData*           Alloc (void) { return NewObjClear(MyTag);}
        
    };
      
    Bool MyTag::Init(GeListNode *node)
    {
        
        node->SetParameter(MY_DYN_POINT_NUMBER, GeData(120), DESCFLAGS_SET_0);
      
        node->SetParameter(MY_DYN_BOOL, GeData(true), DESCFLAGS_SET_0);
      
        node->SetParameter(MY_DYN_SPLINE_OUTPUT, GeData(CUSTOMDATATYPE_SPLINE, DEFAULTVALUE), DESCFLAGS_SET_0);
        
        node->SetParameter(MY_DYN_REAL, GeData(1.0), DESCFLAGS_SET_0);
        node->SetParameter(MY_DYN_ANOTHER_REAL, GeData(5.0), DESCFLAGS_SET_0);
        node->SetParameter(MY_DYN_REAL_SLIDER, GeData(500.0), DESCFLAGS_SET_0);
        
    //    dlg = NewObjClear(MYDIALOG);
        
        return true;
    }
      
    void MyTag::Free(GeListNode *node)
    {
    //    DeleteObj(dlg);
      
    }
      
      
    Bool MyTag::GetDDescription(GeListNode *node, Description *description, DESCFLAGS_DESC &flags)
    {
        
        if (!description->LoadDescription(Tbase))
            return false;
        
        
        const DescID* singleid = description->GetSingleDescID();
        DescID cid;
       
        
        
      
        
        // point number ------------------------------------------------------
        
        cid = DescLevel(MY_DYN_POINT_NUMBER, DTYPE_LONG,0);
        if (!singleid || cid.IsPartOf(*singleid, nullptr))
        {
            BaseContainer bc = GetCustomDataTypeDefault(DTYPE_LONG);
            bc.SetString(DESC_NAME, "Dyn point number");
            bc.SetInt32(DESC_SCALEH, 1);
            bc.SetInt32(DESC_MIN, 0);
            bc.SetInt32(DESC_MAX, 1000);
            bc.SetInt32(DESC_STEP, 1);
            description->SetParameter(cid, bc, DescLevel(ID_TAGPROPERTIES));
        }
        // bool  ------------------------------------------------------
        cid = DescLevel(MY_DYN_BOOL, DTYPE_BOOL, 0);
        if (!singleid || cid.IsPartOf(*singleid, nullptr))
        {
            BaseContainer bc = GetCustomDataTypeDefault(DTYPE_BOOL);
            bc.SetString(DESC_NAME, "dyn bool");
            description->SetParameter(cid, bc, DescLevel(ID_TAGPROPERTIES));
        }
          // spline  ------------------------------------------------------
        cid = DescLevel(MY_DYN_SPLINE_OUTPUT, CUSTOMDATATYPE_SPLINE, 0);
        
        if (!singleid || cid.IsPartOf(*singleid, nullptr))
        {
            BaseContainer bc = GetCustomDataTypeDefault(CUSTOMDATATYPE_SPLINE);
            
            bc.SetInt32(DESC_CUSTOMGUI, CUSTOMGUI_SPLINE);
            bc.SetString(DESC_NAME, "dyn weight");
            bc.SetBool(SPLINECONTROL_GRID_H, true);
            bc.SetBool(SPLINECONTROL_GRID_V, true);
            bc.SetBool(SPLINECONTROL_VALUE_EDIT_H, true);
            bc.SetBool(SPLINECONTROL_VALUE_EDIT_V, true);
            bc.SetFloat(SPLINECONTROL_X_MIN, 0);
            bc.SetFloat(SPLINECONTROL_X_MAX, 1);
            bc.SetFloat(SPLINECONTROL_Y_MIN, 0);
            bc.SetFloat(SPLINECONTROL_Y_MAX, 1);
            
            
            description->SetParameter(cid, bc, DescLevel(ID_TAGPROPERTIES));
        }
        
        // real ------------------------------------------------------
        
        cid = DescLevel(MY_DYN_REAL, DTYPE_REAL, 0);
        
        if (!singleid || cid.IsPartOf(*singleid, nullptr))
        {
            BaseContainer bc = GetCustomDataTypeDefault(DTYPE_REAL);
            
            bc.SetString(DESC_NAME, "dyn Real");
            bc.SetInt32(DESC_SCALEH, 1);
            bc.SetInt32(DESC_MIN, 0);
            bc.SetInt32(DESC_MAX, 1000);
            bc.SetInt32(DESC_STEP, 1);
            bc.SetInt32(DESC_UNIT, DESC_UNIT_METER);
            
            description->SetParameter(cid, bc, DescLevel(ID_TAGPROPERTIES));
        }
        
        
        
        // another real ------------------------------------------------------
        cid = DescLevel(MY_DYN_ANOTHER_REAL, DTYPE_REAL, 0);
        
        if (!singleid || cid.IsPartOf(*singleid, nullptr))
        {
            BaseContainer bc = GetCustomDataTypeDefault(DTYPE_REAL);
            
            bc.SetString(DESC_NAME, "dyn another Real");
            bc.SetInt32(DESC_SCALEH, 1);
            bc.SetInt32(DESC_MIN, 0);
            bc.SetInt32(DESC_MAX, 10);
            bc.SetFloat(DESC_STEP, 0.1);
            
            description->SetParameter(cid, bc, DescLevel(ID_TAGPROPERTIES));
        }
        
        
        
        // real slider  ------------------------------------------------------
        
        cid = DescLevel(MY_DYN_REAL_SLIDER, DTYPE_REAL, 0);
        
        if (!singleid || cid.IsPartOf(*singleid, nullptr))
        {
            BaseContainer bc = GetCustomDataTypeDefault(DTYPE_REAL);
            
            bc.SetInt32(DESC_CUSTOMGUI, CUSTOMGUI_REALSLIDER);
            bc.SetString(DESC_NAME, "dyn Real Slider");
            bc.SetInt32(DESC_SCALEH, 1);
            bc.SetInt32(DESC_MIN, 0);
            bc.SetInt32(DESC_MAX, 1000);
            bc.SetInt32(DESC_STEP, 1);
            bc.SetInt32(DESC_UNIT, DESC_UNIT_METER);
            
            // slider settings
            bc.SetFloat(DESC_MINSLIDER, 250);
            bc.SetFloat(DESC_MAXSLIDER, 750);
            
            description->SetParameter(cid, bc, DescLevel(ID_TAGPROPERTIES));
        }
      
        // Button ------------------------------------------------------
        
         cid = DescLevel(MY_BUTTON_ID, DTYPE_BUTTON,0);
        
        if (!singleid || cid.IsPartOf(*singleid, nullptr))
        {
            BaseContainer bc = GetCustomDataTypeDefault(DTYPE_BUTTON);
            bc.SetInt32(DESC_CUSTOMGUI, CUSTOMGUI_BUTTON); // done with default data type ?
            bc.SetString(DESC_NAME, "Push me");
            bc.SetInt32(DESC_ANIMATE, DESC_ANIMATE_OFF);
            bc.SetBool(DESC_SCALEH, true);
            description->SetParameter(cid, bc, DescLevel(ID_TAGPROPERTIES));
        }
        
      
        
        flags |= DESCFLAGS_DESC_LOADED;
        
        
        return SUPER::GetDDescription(node, description, flags);
    }
      
      
      
      
      
    Bool RegisterMemLeakTest(void);
      
    Bool RegisterMemLeakTest()
    {
        
        return RegisterTagPlugin(PLUGIN_ID, "testing Leak", TAG_EXPRESSION | TAG_VISIBLE   , MyTag::Alloc, "", nullptr, 0);
    }
      
    

    and the memory leaks (the first one is always there)

    Memory Leaks Detected:
    ../../../frameworks/core.framework/source/maxon/basearray.h (223) : Memory leak of 16 bytes () at 0x114abb500
    /perforce_buildsystem_osx/compile-osx-3-release/depot/release/19.0/modules/c4dplugin/source/src/richard/genesis/ge_smart_link.cpp (465) : 2 Memory leaks of 40 bytes (, first leak at 0x13376c800)
    /perforce_buildsystem_osx/compile-osx-3-release/depot/release/19.0/modules/c4dplugin/source/src/richard/genesis/ge_smart_link.cpp (584) : 2 Memory leaks of 72 bytes (, first leak at 0x108d61580)
    /perforce_buildsystem_osx/compile-osx-3-release/depot/release/19.0/modules/c4dplugin/source/src/tilo/cgui/cgui_coffeedialog.cpp (1421) : 2 Memory leaks of 624 bytes (, first leak at 0x136e2ec00)
    /perforce_buildsystem_osx/compile-osx-3-release/depot/release/19.0/modules/c4dplugin/source/src/tilo/cgui/cgui_coffeedialog.cpp (1432) : 2 Memory leaks of 400 bytes (, first leak at 0x134569300)
    /perforce_buildsystem_osx/compile-osx-3-release/depot/release/19.0/modules/c4dplugin/source/src/tilo/cgui/cgui_coffeedialog.cpp (2968) : 2 Memory leaks of 1368 bytes (, first leak at 0x12e82f700)
    /perforce_buildsystem_osx/compile-osx-3-release/depot/release/19.0/modules/c4dplugin/source/src/tilo/cgui/cgui_main.cpp (114) : 2 Memory leaks of 600 bytes (, first leak at 0x136e2ef00)
    /perforce_buildsystem_osx/compile-osx-3-release/depot/release/19.0/modules/c4dplugin/source/src/tilo/cgui/cgui_main.cpp (157) : 2 Memory leaks of 488 bytes (, first leak at 0x134712280)
    8 blocks not freed
    


  • On 12/01/2018 at 03:50, xxxxxxxx wrote:

    Hi,

    A NodeData plugin can't hold a GeDialog like a CommandData.
    An object plugin is created multiple times (on the undo stack for instance) and is also managed in different threads than the main. Remember GUI operations have to be done in the main threads.

    The solution is to have a CommandData holding the dialog. Then from the tag's MSG_EDIT use CallCommand() to invoke it.
    MSG_EDIT is run on the main thread so GUI operations can be done. Same for MSG_DESCRIPTION_COMMAND with buttons in descriptions.

    Catch EVMSG_CHANGE from the dialog's CoreMessage() to refresh it according to the tag's state.



  • On 12/01/2018 at 08:36, xxxxxxxx wrote:

    Ok changing all this.

    i'm storing data in the tag (not in a basecontainer)
    Using SpecialEventAdd() to send a pointer to my data after the callCommand seems to work. Is it ok to do that ?

    edit : 
    even with those change i still got memory leak when i'm playing with checkbox



  • On 12/01/2018 at 09:06, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Using SpecialEventAdd() to send a pointer to my data after the callCommand seems to work. Is it ok to do that ?

    Yes that's fine.



  • On 16/01/2018 at 00:46, xxxxxxxx wrote:

    after changing the data stored in the tag, i've killed the memory leak. Thanks for the support :)



  • On 16/01/2018 at 01:35, xxxxxxxx wrote:

    You're welcome. Glad the memory leak is solved.


Log in to reply