Hi

Having issues with a stateful node developing quite the memory leak. I can't seem to use VuoRelease() (or VuoRetain()) on floats or integers. I see from Vuo.Math.Count that you use a pointer to the stored value, and creates it as an input. How would I go about solving this when the stored value is not coming from an input, and the instance is a struct instead of a type?

When building the project with retailn/release on these values, I get a warning: incompatible integer to pointer conversion passing 'VuoInteger' (aka long long') to parameter of type 'const void ' VuoRelease((instance)->count);

In the struct, there is also a VuoPoint2d, which retains and releases without any error messages. So I'm clearly doing something wrong when it comes to floats & ints. I suspect I'm passing the value of the VuoReal/-Integer instead of the heap info I'm supposed to pass, but can't figure out how I'd do it correctly.

Would love some help on this! Cheers!

Comments

Floats and integers don't

jstrecker's picture
Submitted by

Floats and integers don't need to be (and shouldn't be) retained and released. Only heap-allocated pointers need it.

A few examples of node classes that use struct instance data are vuo.data.record, vuo.osc.send, and vuo.scene.twirl. If those don't answer your question, maybe post some of your code here and I can try to give a more specific answer.

Thank you Jaymie (@jstrecker)

MartinusMagneson's picture
Submitted by

Thank you Jaymie, I think I might have stumbled onto something else though, causing this kind of behaviour.

I just made a new average list items node to smooth audio ranges a bit, and it just explodes wildly really fast. This node however is a stateless node, and it only gets triggered on start, so although I could be the culprit, I don't see how.

Here is an image of the Activity Monitor after running the composition for about a minute: The render scene to image node is there only to get the comp in the dock, as it otherwise wouldn't even show up. By removing the average list items node, it has a more expected memory usage.

The code for the node:

#include "node.h"
#include "math.h"
 
VuoModuleMetadata({
                      "title" : "Average List Items",
                      "keywords" : [ "magnitude", "bigger", "smaller", "some", "small", "most", ">", "<", "upper", "range" ],
                      "version" : "2.1.0",
                      "node": {
                          "exampleCompositions" : [ ]
                      }
                  });
 
void nodeEvent
(
        VuoInputData(VuoList_VuoReal) list,
        VuoInputData(VuoInteger) averageOfItems,
        VuoOutputData(VuoList_VuoReal) averageList
)
{
    unsigned long listCount = VuoListGetCount_VuoReal(list);
    *averageList = VuoListCreate_VuoReal();
 
    VuoInteger averageCount = averageOfItems;
 
    VuoList_VuoReal tempList = VuoListCreateWithCount_VuoReal(averageCount, 0.0);
 
    for(int i = 1; i <= listCount; i = (i + averageCount)){
        for(int j = 1; j <= VuoListGetCount_VuoReal(tempList); ++j){
            VuoListSetValue_VuoReal(tempList, VuoListGetValue_VuoReal(list, i+ (j - 1)), j, false);
        }
        VuoListAppendValue_VuoReal(*averageList, VuoReal_average(tempList));
    }
 
}

The original-problem node takes a relatively long time to reach any significant memory usage, but it still slowly grows out of its expected range, and into problem territory. That would be kind of worse if I hadn't noticed after running the node for a long time. Still work in progress it lacks defaults and proper naming, but here is the code if there is something glaringly obvious:

#include "node.h"
#include <math.h>
 
VuoModuleMetadata({
                     "title" : "Grow Real",
                     "description" : "Grows a list of VuoReals from a starting point to an end point.",
                     "keywords" : [ ],
                     "version" : "1.0.0",
                     "dependencies" : [ ],
                     "node": {
                         "isInterface" : false
                     }
                 });
 
struct nodeInstanceData
{
    VuoList_VuoPoint2d instanceTime;
    VuoReal previousTime;
    VuoInteger count;
};
 
struct nodeInstanceData * nodeInstanceInit
(
        VuoInputData(VuoInteger) points
)
{
    struct nodeInstanceData * instance = (struct nodeInstanceData *)malloc(sizeof(struct nodeInstanceData));
    VuoRegister(instance, free);
 
    instance->instanceTime = VuoListCreateWithCount_VuoPoint2d(points, VuoPoint2d_make(0.0, 0.0));
    instance->count = 0;
    VuoRetain(instance->instanceTime);
    instance->previousTime = 0.0;
 
    return instance;
}
 
void nodeInstanceEvent
(
        VuoInstanceData(struct nodeInstanceData *) instance,
        VuoInputEvent({"eventBlocking":"wall"}) trigger,
        VuoInputData(VuoReal) time,
        VuoInputData(VuoInteger, {"default":100, "suggestedMin":1, "suggestedMax":10000}) points,
        VuoInputData(VuoReal) startPosition,
        VuoInputData(VuoReal) endPosition,
        VuoInputData(VuoReal) duration,
        VuoInputData(VuoCurve) curve,
        VuoInputData(VuoCurveEasing) easing,
        VuoInputData(VuoLoopType) loop,
        VuoOutputData(VuoList_VuoReal) timeList
)
{
    //Make the output list
 
    *timeList = VuoListCreateWithCount_VuoReal(points, 0.0);
 
    //Make a copy of the info list
 
    VuoList_VuoPoint2d workList = VuoListCopy_VuoPoint2d((*instance)->instanceTime);
    VuoRelease((*instance)->instanceTime);
 
    //Get current count of active items
 
    VuoInteger activeCount = (*instance)->count;
    VuoRelease((*instance)->count);
 
    //Calculate time interval
    VuoReal pTime = (*instance)->previousTime;
    VuoReal frameTime = time - pTime;
 
    //Trigger durations
 
    if(trigger){
        activeCount += 1;
        if(activeCount > points){
            activeCount = 1;
        }
        VuoListSetValue_VuoPoint2d(workList, VuoPoint2d_make(time,duration), activeCount, false);
    }
 
    //VuoPoint3d stores curve data as: x = start time, y = duration. Duration decides if it is active or not (switch between durationTime / 0 for off)
    //Calculating the individual point/scale times
 
    for(unsigned int i = 0; i < points; ++i){
        VuoInteger index = i + 1;
 
        VuoPoint2d instanceInfo = VuoListGetValue_VuoPoint2d(workList, index);
 
        VuoReal currentTime = (time - instanceInfo.x) + frameTime;
 
        VuoReal pointTime = VuoReal_curve(
                        currentTime,
                        startPosition,
                        endPosition,
                        instanceInfo.y,
                        curve,
                        easing,
                        loop);
 
        if(pointTime == endPosition){
            instanceInfo = VuoPoint2d_make(0,0);
            pointTime = 0.0;
        }
 
 
        VuoListSetValue_VuoReal(
                    *timeList,
                    pointTime,
                    index,
                    false
                    );
 
        VuoListSetValue_VuoReal(*timeList, pointTime, index, false);
        VuoListSetValue_VuoPoint2d(workList, instanceInfo, index, false);
    }
 
    (*instance)->previousTime = time;
    (*instance)->count = activeCount;
    (*instance)->instanceTime = workList;
}
 
void nodeInstanceFini
(
        VuoInstanceData(struct nodeInstanceData *) instance
)
{
}

Both nodes are attached.

Wow, that's some leak :)

jstrecker's picture
Submitted by

Wow, that's some leak :)

When Average Of Items is 0, the memory skyrockets and the node never outputs an event. If you change Average Of Items to 1, the memory is fine and the node outputs the expected event. I'll bet the node is stuck in an infinite loop in the case where Average Of Items is 0.

How about here: for(int i = 1; i <= listCount; i = (i + averageCount)) — if averageCount is 0, i never increments.