Vuo  0.4.5
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages

When a node allocates data on the heap (for example, with malloc) and sends it through an output port, the data travels to other nodes. The other nodes can hold onto that data for any amount of time. There's no guarantee of which node will be the last to use the data. Who is responsible for deallocating that data?

The answer is that the Vuo runtime is responsible for deallocating the data – but type and node class definitions are responsible for informing the Vuo runtime about the data.

The Vuo runtime uses reference counting to keep track of heap-allocated memory and deallocate it when all nodes are finished using it. The Vuo API for node classes and types provides three functions for reference counting:

To make the reference-counting system easier for you to use, the Vuo compiler automatically inserts most of the necessary calls to vuoRegister, vuoRetain, and vuoRelease. If you're implementing a stateless node class, you never need to call these functions. If you're implementing a stateful node class or a type, read on to learn when to call these functions.

Defining a heap-allocated type

Pointer to heap-allocated data

If you define a type, and the type is a pointer to heap-allocated data, then the *_valueFromString function is responsible for registering that data. For example:

typedef char * VuoString;
VuoString VuoString_valueFromString(const char *initializer)
{
VuoString s = ...;
vuoRegister(s, free);
return s;
}

(The *_stringFromValue and *_summaryFromValue functions should not register their return values.)

Struct containing pointers to heap-allocated data

If your type is a struct, and some of its members are pointers to heap-allocated data, then the type definition is responsible for registering that data. For example:

typedef struct {
float *elements;
size_t elementCount;
} ArrayOfFloats;
ArrayOfFloats ArrayOfFloats_valueFromString(const char *initializer)
{
ArrayOfFloats a;
a.elementCount = ...;
a.elements = (float *)calloc(elementCount, sizeof(float));
vuoRegister(a.elements, free);
return a;
}

For a struct type, you also need to declare the *_retain and *_release functions in order to use the type inside another container. The function implementations are generated automatically by the Vuo compiler, and they recursively traverse the struct and release all pointers to heap-allocated data. An example of declaring these functions:

ArrayOfFloats_retain(ArrayOfFloats value);
ArrayOfFloats_release(ArrayOfFloats value);

Other type containing pointers to heap-allocated data

If your type is some other container, and some of its elements are either pointers to heap-allocated data or structs containing pointers to heap-allocated data, then the type definitions is responsible for registering each element that's allocated, retaining each element that's added, and releasing each element that's removed — as well as registering the container itself. For example:

typedef VuoString * PairOfStrings;
PairOfStrings PairOfStrings_valueFromString(const char *initializer)
{
PairOfStrings pair = (PairOfStrings)calloc(2, sizeof(VuoString));
vuoRegister(pair, PairOfStrings_destroy);
return pair;
}
void PairOfStrings_destroy(void *p)
{
PairOfStrings pair = (PairOfStrings)p;
vuoRelease(pair[0]);
vuoRelease(pair[1]);
free(pair);
}
void PairOfStrings_replaceFirst(PairOfStrings pair, VuoString newFirst)
{
vuoRetain(newFirst);
vuoRelease(pair[0]);
pair[0] = newFirst;
}

If the elements are structs containing pointers to heap-allocated data, use the *_retain and *_release functions to retain and release the struct values. For example:

// ArrayOfFloats type definition
ArrayOfFloats_retain(ArrayOfFloats value);
ArrayOfFloats_release(ArrayOfFloats value);
// PairOfArrayOfFloats type definition
typedef ArrayOfFloats * PairOfArrayOfFloats;
PairOfArrayOfFloats PairOfArrayOfFloats_valueFromString(const char *initializer)
{
PairOfArrayOfFloats pair = (PairOfArrayOfFloats)calloc(2, sizeof(ArrayOfFloats));
vuoRegister(pair, PairOfArrayOfFloats_destroy);
return pair;
}
void PairOfArrayOfFloats_destroy(void *p)
{
PairOfArrayOfFloats pair = (PairOfArrayOfFloats)p;
ArrayOfFloats_release(pair[0]);
ArrayOfFloats_release(pair[1]);
free(pair);
}
void PairOfArrayOfFloats_replaceFirst(PairOfArrayOfFloats pair, ArrayOfFloats newFirst)
{
ArrayOfFloats_retain(newFirst);
ArrayOfFloats_release(pair[0]);
pair[0] = newFirst;
}

Using heap-allocated node instance data

If you use a node instance data type that already has a Vuo type definition (as described in the previous section), then you don't need to do anything. Memory management is already handled for you.

If you define a custom node instance data type within the node class, and the custom type consists of, or contains, pointers to heap-allocated data, then the node class is responsible for the same memory management as if you were defining a full-fledged Vuo type (as described in the previous section). The only difference is the function names (e.g. nodeInstanceInit instead of *_valueFromString).

For example, if the node instance data is a pointer to heap-allocated data, then the nodeInstanceInit function is responsible for registering that data:

typedef char * MyString;
MyString nodeInstanceInit()
{
MyString s = ...;
vuoRegister(s, free);
return s;
}

As another example, if the node instance data is a struct, and some of its members are pointers to heap-allocated data, then the node class is responsible for registering that data:

typedef struct {
int *elements;
size_t elementCount;
} ArrayOfIntegers;
ArrayOfIntegers nodeInstanceInit()
{
ArrayOfIntegers a;
a.elementCount = ...;
a.elements = (int *)calloc(elementCount, sizeof(int));
vuoRegister(a.elements, free);
return a;
}

Automatically-inserted calls to the reference-counting functions

Except for the situations described above, the Vuo compiler automatically inserts calls to vuoRegister, vuoRetain, and vuoRelease where needed. Specifically:

When a port or node instance data has a struct type, the Vuo compiler automatically inserts calls to vuoRetain and vuoRelease any members of the struct (and members of members of the struct, etc.) that are pointers.