user2747939
user2747939

Reputation: 43

MATLAB array assignment fails after MEX call

I was working with MEX and getting bizarre behavior, which I isolated to the following very simple program:

#include "mex.h"
#include <stdio.h>

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
  double *A;
  int i;

  if (    nrhs != 1
       || nlhs > 1
       || !mxIsDouble(prhs[0])
       || mxIsComplex(prhs[0])
       || mxGetM(prhs[0])!=1
    ) mexErrMsgTxt("internal error: dtimes2: input error");

  A = mxGetPr(prhs[0]);
  for (i=0; i<3; i++) A[i] *= 2;
  return; }

So the problem is this: in a MATLAB session,

B=[3.2,5.6,9.4]; dtimes2(B); B

and MATLAB says: B = 6.4000 11.2000 18.8000

So far so good. But now:

B=[3.2,5.6,9.4]

and MATLAB says: B = 6.4000 11.2000 18.8000

but when I say

B=[-34.5,-57.6,-28.9]

then MATLAB says: B = -34.5000 -57.6000 -28.9000

You see in the middle I cannot reassign B if the numbers are the same as they were before. So, reality check:

A=[1,2,3]; A=2*A; A=[1,2,3]

does work, of course: MATLAB says A = [1,2,3] in the end.

MEX warns that my compiler is '6.2.1-2' but the supported one is '4.7.x', but for this simple program I hardly expected a problem. Whats going wrong here?

Upvotes: 1

Views: 60

Answers (2)

user2747939
user2747939

Reputation: 43

I just was thinking about what happened and how it could have happened, and thought to make a note to underscore the danger of changing immutable MATLAB objects out from under MATLAB, in case that helps anyone e Given A=[1,2,3], MATLAB must have calculated a hash for [1,2,3], say "boo", and stored [1,2,3] in bin "boo". In the MEX file, it happily gave up a pointer to [1,2,3], and the big strong c code made the vector at that address into [2,4,6]. Then, on the statement A=[1,2,3], MATLAB calculated the hash again and it was the same, namely "boo". Since the session was simple, there was only one object in that bin. Well, if there was only one object, it had to be [1,2,3] already, so why re-write that to memory? So it didn't. But the memory was not [1,2,3] it was [2,4,6], which never should have been in bin "boo". After the violation, the result was the following conversation:

ME: MATLAB, pls set A=[1,2,3].

MATLAB: Sir, yes sir! Did that! A=[2,4,6]!

ME: No, really, set A=[1,2,3].

MATLAB: Sir, yes sir! A=[2,4,6]!

ME: Ok. Umm, set A=[3,4,5].

MATLAB: Sir, yes sir! A=[3,4,5].

ME: So, umm, ok, now set A=[1,2,3]

MATLAB: Sir, yes sir! A=[2,4,6]!

Every time the bad MEX file executed, the resulting vector was in the wrong hash bin. Randomly, some the bins had exactly one wrong object. This caused extreme confusion.

Upvotes: 0

rayryeng
rayryeng

Reputation: 104555

You are not supposed to modify any inputs that are coming into your MEX wrapper. This is undefined behaviour and that is what is happening in your MEX function. MEX recommends that you return outputs instead of mutating the inputs. You can read more about this in Yair Altman's Undocumented MATLAB blog if you really desire mutating the inputs or what he calls in-place editing. When you're starting out with MEX though, I don't encourage this behaviour. Though there are situations where it's merited, unless you know what you're doing, avoid this if possible: http://undocumentedmatlab.com/blog/matlab-mex-in-place-editing.

As for not modifying inputs, this is clearly defined in the function declaration of your mexFunction gateway:

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
                                                      ^^^^^^^^^^^^^^^^^^^^^

The const modifier ensures that none of the input pointers are changed, which is fine... but that doesn't stop you from actually modifying the contents that the pointers to memory are referring to or doing this in-place. Generally speaking, the memory that you created for any MATLAB variable in the MATLAB workspace might be shared with other MATLAB variables due to the "lazy copy" behaviour. Therefore, the values that you see in a matrix, vector or single value may be linked to other matrices, vectors or single values of other variables. When you change the memory in-place, you would have changed those other variables too and this may be the reason why you are seeing inconsistent behaviour.

Always return the actual desired output rather than mutating the inputs as you will be guaranteed to get the right results. I've modified your code below so that it will output a new matrix rather than perform the computations in-place.

#include "mex.h"
#include <stdio.h>

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
  double *A;
  double *B; // Change - For the output
  int i;

  if (    nrhs != 1
       || nlhs > 1
       || !mxIsDouble(prhs[0])
       || mxIsComplex(prhs[0])
       || mxGetM(prhs[0])!=1
    ) mexErrMsgTxt("internal error: dtimes2: input error");

  A = mxGetPr(prhs[0]);

  // Change - Create output memory
  mwSize rows = mxGetM(prhs[0]);
  mwSize cols = mxGetN(prhs[0]); 
  plhs[0] = mxCreateDoubleMatrix(rows, cols, mxREAL);

  // Change - get a pointer to the output memory
  B = mxGetPr(plhs[0]);

  for (i=0; i<3; i++) B[i] = 2*A[i]; // Change - Write to output memory
  return;
}

You would then do this instead:

B = dtimes2(B);

Upvotes: 5

Related Questions