Issue with Mex Function

6 views (last 30 days)
Scott Lichtor
Scott Lichtor on 30 Jul 2015
Commented: James Tursa on 5 Aug 2015
I tried running a very simple example with a mex file and a structure with another structure inside of it and ran into an issue. I created a standalone C++ file with this code:
#include <stdio.h>
#include "mex.h"
void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray
*prhs[])
{
mxSize dims[2] = {1, 1};
const char* fields1[] = {"field1", "field2"};
const char* fields2[] = {"field3"};
mxArray *mexMsg = mxCreateStructMatrix(1, 1, 2, fields1);
mxArray *subMexMsg = mxCreateStructMatrix(1, 2, 1, fields2);
int intValue = 32;
mxArray* inputMxArray = mxCreateDoubleScalar((double) intValue);
mxSetField(subMexMsg, 0, "field3", inputMxArray);
mxSetField(mexMsg, 0, "field1", subMexMsg);
mxSetField(mexMsg, 0, "field2", inputMxArray);
plhs[0] = mexMsg;
}
My goal was to create a structure of structures. The result (let’s call it res) should have looked like this res field1 subMessage field3 32 field2 32
I compiled it with Matlab using mex and ran it. Everything looked fine and as expected. Then I executed ‘clear all’ and it failed with a segfault. I hope that this should be easily repeatable on another machine. I expect that the error came about when trying to clear the mxArrays that were created in the mex function. Anyone know why this error is occurring? Thank you for any help.

Accepted Answer

James Tursa
James Tursa on 30 Jul 2015
Edited: James Tursa on 30 Jul 2015
You can't re-use mxArray variables like you are doing when you are building a cell or struct array in a mex routine. There is an official way to fix this, and an unofficial way to fix this. The official way, which has the downside of requiring a deep data copy, is:
mxSetField(subMexMsg, 0, "field3", inputMxArray);
mxSetField(mexMsg, 0, "field1", subMexMsg);
mxSetField(mexMsg, 0, "field2", mxDuplicateArray(inputMxArray));
That is, every time you re-use a variable as a cell or struct element, you must actually use a deep data copy of it. Not very memory efficient if you have large variables, but that is the way it is. The reason your code is seg faulting is because when the 1st reference to inputMxArray gets destroyed, the memory for it is actually free'd. Then when the 2nd reference to inputMxArray gets destroyed, the memory for it has already been free'd so invalid memory is accessed. MATLAB didn't know there was another reference to the variable in memory when the 1st reference was destroyed.
MATLAB can, and does at the m-file level, re-use variables the way you are doing, but it increments a "reference count" for the variable to keep track of how many copies are around in cell and struct variables. It does this automatically in the background. That way, when variables get cleared, memory doesn't actually get free'd until the last reference copy gets cleared. I.e., when the 1st reference to inputMxArray gets destroyed, the only thing that happens is the reference count gets decremented by 1. No memory is free'd. When the 2nd reference to inputMxArray gets destroyed, the memory actually gets free'd since it is the last copy in memory.
There is no official way to duplicate this behavior in a mex routine using only official API routines. There is an unofficial mex routine called mxCreateReference, that will do this for you (i.e., increment the reference count by 1 instead of creating a deep data copy). To use it, simply include the prototype and call it (but you will be departing from official library code). The mxCreateReference function is intended specifically for re-using variables in cell or struct variables. E.g.,
mxArray *mxCreateReference( mxArray * );
:
mxSetField(subMexMsg, 0, "field3", inputMxArray);
mxSetField(mexMsg, 0, "field1", subMexMsg);
mxSetField(mexMsg, 0, "field2", mxCreateReference(inputMxArray));
The return value (mxArray address) is the same as the input value.
Unfortunately, TMW has removed mxCreateReference in later versions of MATLAB. The only recourse left to the programmer at this point would be to hack into the mxArray to bump up the reference count manually. Let me know if you need this.
For comparison, mxSetField, mxSetFieldByNumber, and mxSetCell do the following to the input variable:
1) Changes the variable type to 3 (SUB_ELEMENT)
2) Removes the variable address from the garbage collection
  2 Comments
Scott Lichtor
Scott Lichtor on 30 Jul 2015
Thank you so much! For the help, and especially for explaining it so clearly.
James Tursa
James Tursa on 5 Aug 2015
To complete this thread, here is the hack:
/* Replacement hack for mxCreateReference in later versions of MATLAB */
/* Programmer: James Tursa */
mxArray *myCreateReference( mxArray *mx )
{
struct mxArray_Tag_Partial {
void *nameORxlink;
mxClassID ClassID;
int VariableType;
mxArray *CrossLink;
size_t ndim;
unsigned int RefCount; /* Number of sub-elements identical to this one */
};
struct mxArray_Tag_Partial *mp;
mp = (struct mxArray_Tag_Partial *) mx;
mp->RefCount++; /* Manually bump up the reference count by 1 */
return mx;
}

Sign in to comment.

More Answers (0)

Categories

Find more on Write C Functions Callable from MATLAB (MEX Files) in Help Center and File Exchange

Tags

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!