SOVED by @CrisLuengo and @JamesTursa.Solution at the end of the post
Original Post
I'm creating a MEX to find a path between two nodes using the A* algorithm. The code works as expected, retrieves the right result and everything seems to be fine, but when I return from the method that calls the MEX, Matlab simply shuts itself down.
As I know sometimes Matlab creates a pointer to the same memory address when the user tries to create a copy of a variable (I.E. A = 3; B = A, then A and B are pointing to the same memory address, even though Matlab shows them as 2 independent variables) I used an old trick that consists on performing an operation directly to the copy of the variable, even if its a silly one, Matlab will think that the two variables are no longer the same and will create a copy of it (I.E. A = 3; B = A; B = B+0, now A and B are stored as different and independent variables).
So the only way that I have found to fix the issue is to do what is shown below in function getWP:
function testAStarC() % load the map of the area load('epsp0_2_nav.mat'); % Set the initial node initNode = '16x21'; % Set the target node finalNode = '-15x54'; % Select Heuristic heuristic = 'Manhattan'; % Create a target location (targetX, targetY, targetAngle) targetCoords = [-15*110 54*110 0.15]; % Function that hosts the call to the MEX wp = getWP(map, initNode, finalNode, targetCoords, heuristic); disp('If you remove the line cellNodes{keyID}.x = cellNodes{keyID}.x; from getWP ... I wont reach this line'); disp(['Route with ',num2str(length(wp)),' wp found']); disp('done');function waypointsList = getWP(map, initNode, finalNode, targetCoords, heuristic) % HashMap containing the nodes (this is a Java hashmap) nodesHash = map.navMap.nodes.nodes; keys = nodesHash.keys(); numNodes = length(keys); cellNodes = cell(1,numNodes); % Parse the nodes from the HashMap to Cells as the MEX seems to be % unable to read directly from the Java HashMap for keyID=1:numNodes cellNodes{keyID} = nodesHash(keys{keyID}); %--------------------------------------------------------- % WITHOUTH THIS MATLAB CRASHES WHEN RETURNING FROM GETWP %--------------------------------------------------------- % We need this to force Matlab to create a new copy of the content, % otherwise will send a pointer aiming to the HashMap and crash when % returning from getWP. cellNodes{keyID}.x = cellNodes{keyID}.x; end waypointsList = AStar(cellNodes, initNode, finalNode, targetCoords, heuristic, 1); disp('I am not crashing here if you remove cellNodes{keyID}.x = cellNodes{keyID}.x');
My first thought was that I was doing something wrong to "cellNodes" inside the MEX and this was causing Matlab to crash, but I am not performing any operations using directly the input parameter. This is the constructor for the Node class:
Node.cpp
Node::Node(mxArray *cellElement){ double *xIn; double *yIn; char strIn[15]; double *posXIn; double *posYIn; double *weightIn; double *tempVal; size_t numCellElms; mxArray *cellElement2; numCellElms = mxGetNumberOfFields(cellElement); size_t size; for (int cellItem = 0; cellItem < numCellElms; cellItem++) { cellElement2 = mxGetCell(cellElement,cellItem); if (cellItem == 0) { xIn = mxGetPr(cellElement2); memcpy(tempVal,xIn,sizeof(double)); gridX = int(*tempVal); } if (cellItem == 1) { yIn = mxGetPr(cellElement2); memcpy(tempVal,yIn,sizeof(double)); gridY = int(*tempVal); } if (cellItem >= 2 && cellItem < 10) { mwSize buflen = mxGetN(cellElement2)*sizeof(mxChar)+1; if (buflen <= 1) { connections[cellItem-2][0] = '\0'; } else { mxGetString(cellElement2, strIn, buflen); memcpy(&connections[cellItem-2][0], strIn, buflen); } } if (cellItem == 10) { posXIn = mxGetPr(cellElement2); memcpy(&posX,posXIn,sizeof(double)); } if (cellItem == 11) { posYIn = mxGetPr(cellElement2); memcpy(&posY,posYIn,sizeof(double)); } if (cellItem == 12) { posXIn = mxGetPr(cellElement2); memcpy(&wpX,posXIn,sizeof(double)); } if (cellItem == 13) { posYIn = mxGetPr(cellElement2); memcpy(&wpY,posYIn,sizeof(double)); } if (cellItem == 14) { weightIn = mxGetPr(cellElement2); memcpy(&weight,weightIn,sizeof(double)); } } sprintf(xStr,"%i",gridX); sprintf(yStr,"%i",gridY); sprintf(key,"%ix%i",gridX,gridY);}
And this is how I initialize the Nodes list inside the AStar.cpp
// Create nodes in the nodes hashmxArray *cellElement;std::map<std::string, Node> nodesHash;for (int nodeID=0; nodeID < numberOfNodes; nodeID++) { cellElement = mxGetCell(prhs[0], nodeID); Node n(cellElement); nodesHash[n.key] = n;}
Form now on nothing uses prhs[0] anymore, as nothing has altered the content of the prhs[0] (variable containing the pointer to the Matlab variable cellNodes), this variable should be exactly the same after leaving the MEX.
From here I have two questions:
- If nothing is altering the content of the first parameter, why should it crash when returning from getWP?
- Is there a more elegant way of forcing Matlab to create a real copy of a variable?
Thanks!
*Edit1: Using Matlab 2015b in Windows10 64 bit.
Solution
[Node.cpp] 'double* tempVal' was not allocated, although the code worked, this was possibly messing with the integrity of prhs[0] thus causing Matlab to crash when returning from the function that invoked the MEX.
The solution was to declare tempVal as 'double tempVal[1]'.After that, the testAStarC.m the line 'cellNodes{keyID}.x = cellNodes{keyID}.x;' can be removed without causing the error.
Although unrelated to the crash, the use of memcpy to get scalar doubles has been replaced with mxGetScalar().
Node constructor:
Node::Node(mxArray *cellElement){ char strIn[15]; double tempVal[1]; size_t numCellElms = mxGetNumberOfFields(cellElement); mwSize buflen; for (int cellItem = 0; cellItem < numCellElms; cellItem++) { mxArray *cellElement2 = mxGetCell(cellElement,cellItem); switch (cellItem) { case 0: gridX = (int)mxGetScalar(cellElement2); break; case 1: gridY = (int)mxGetScalar(cellElement2); break; case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: buflen = mxGetN(cellElement2)*sizeof(mxChar)+1; if (buflen <= 1) connections[cellItem-2][0] = '\0'; else { mxGetString(cellElement2, strIn, buflen); memcpy(&connections[cellItem-2][0], strIn, buflen); } break; case 10: posX = mxGetScalar(cellElement2); break; case 11: posY = mxGetScalar(cellElement2); break; case 12: wpX = mxGetScalar(cellElement2); break; case 13: wpY = mxGetScalar(cellElement2); break; case 14: weight = mxGetScalar(cellElement2); break; } } sprintf(xStr,"%i",gridX); sprintf(yStr,"%i",gridY); sprintf(key,"%ix%i",gridX,gridY);}
testAStarC.m
function waypointsList = getWP(map, initNode, finalNode, targetCoords, heuristic) % HashMap containing the nodes (this is a Java hashmap) nodesHash = map.navMap.nodes.nodes; keys = nodesHash.keys(); numNodes = length(keys); cellNodes = cell(1,numNodes); % Parse the nodes from the HashMap to Cells as the MEX seems to be % unable to read directly from the Java HashMap for keyID=1:numNodes cellNodes{keyID} = nodesHash(keys{keyID}); end waypointsList = AStar(cellNodes, initNode, finalNode, targetCoords, heuristic, 1);