I don't quite have a working example but the logic for allocation and deallocation is pretty much as explained by JKSH already. But that is not where it would be very useful really. What he does is simply calculating the time difference between when the VI hierarchy containing the CLFN was started until it was terminated. Not that useful really. 😀
The usefulness is in the third function AbortCallback() and the actual CLFN function itself.
// Our data structure to manage the asynchronous management
typedef struct
{
time_t time;
int state;
LStrHandle buff;
} MyManagedStruct;
// These are the CLFN Callback functions. You could either have multiple sets of Callback functions each operating on their own data
// structure as InstanceDataPtr for one or more functions or one set for an entire library. Using the same data structure for all. In
// the latter case these functions will need to be a bit smarter to determine differences for different functions or function sets
// based on extra info in the data structure but it is a lot easier to manage, since you don't have different Callback functions for
// different CLFNs.
MgErr LibXYZReserve(InstanceDataPtr *data)
{
// LabVIEW wants us to initialize our instance data pointer. If everything fits into a pointer
// we could just use it, otherwise we allocate a memory buffer and assign its pointer to
// the instanceDataPtr
MyManagedStruct *myData;
if (!*data)
{
// We got a NULL pointer, allocate our struct. This should be the standard unless the VI was run before and we forgot to
// assign the Unreserve function or didn't deallocate or clear the InstanceDataPtr in there.
*data = (InstanceDataPtr)malloc(sizeof(MyManagedStruct));
if (!*data)
return mFullErr;
memset(*data, 0, sizeof(MyManagedStruct));
}
myData = (MyManagedStruct*)*data;
myData->time = time(NULL);
myData->state = Idle;
return noErr;
}
MgErr LibXYZUnreserve(InstanceDataPtr *data)
{
// LabVIEW wants us to deallocate a instance data pointer
if (*data)
{
MyManagedStruct *myData = (MyManagedStruct*)*data;
// We could check if there is still something active and signal to abort and wait for it
// to have aborted but it's better to do that in the Abort callback
.......
// Deallocate all resources
if (myData->buff)
DSDisposeHandle(myData->buff);
// Deallocate our memory buffer and assign NULL to the InstanceDataPointer
free(*data)
*data = NULL;
}
return noErr;
}
MgErr LibXYZAbort(InstanceDataPtr *data)
{
// LabVIEW wants us to abort a pending operation
if (*data)
{
MyManagedStruct *myData = (MyManagedStruct*)*data;
// In a real application we do probably want to first check that there is actually something to abort and
// if so signal an abort and then wait for the function to actually have aborted.
// This here is very simple and not fully thread safe. Better would be to use an Event or Notifier
// or whatever or at least use atomic memory access functions with semaphores or similar.
myData->state = Abort;
}
return noErr;
}
// This is the actual function that is called by the Call Library Node
MgErr LibXYZBlockingFunc1(........, InstanceDataPtr *data)
{
if (*data)
{
MyManagedStruct *myData = (MyManagedStruct*)*data;
myData->state = Running;
// keep looping until abort is set to true
while (myData->state != Abort)
{
if (LongOperationFinished(myData))
break;
}
myData->state = Idle;
}
else
{
// Shouldn't happen but maybe we can operate synchronous and risk locking the
// machine when the user tries to abort us.
}
return noErr;
}
When you now configure a CLFN, you can assign an extra parameter as InstanceDataPtr. This terminal will be greyed out as you can not connect anything to it on the diagram. But LabVIEW will pass it the InstanceDataPtr that you have created in the ReserveCallback() function configuration for that CLFN. And each CLFN on a diagram has its own InstanceDataPtr that is only valid for that specific CLFN. And if your VI is set reentrant LabVIEW will maintain an InstanceDataPtr per CLFN per reentrant instance!