Skip to content

Instantly share code, notes, and snippets.

@amroamroamro
Created November 22, 2016 11:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amroamroamro/5841304ff008049820153f63ea479a11 to your computer and use it in GitHub Desktop.
Save amroamroamro/5841304ff008049820153f63ea479a11 to your computer and use it in GitHub Desktop.
[MATLAB] mex locking test

See #299.

Files:

MyClass.m
private/
    MyClass_.cpp

Compile the MEX-file as usual mex -largeArrayDims MyClass_.cpp (preferrably with C++11).

Running on R2016b/Windows/VS2015, I get this:

>> obj1 = MyClass(10);
[M] ctor begin
-- DLL_PROCESS_ATTACH: hModule=0x3c3a0000
[C] MEX begin id=0
[C] MyClass ctor id=0
-- lock
[M] ctor end

>> obj2 = MyClass(20);
[M] ctor begin
[C] MEX begin id=0
[C] MyClass ctor id=0
-- lock
[M] ctor end

>> clear mex

>> clear all
[M] dtor begin
[C] MEX begin id=1
-- unlock
[C] MEX end 1
[C] MyClass dtor id=0
[M] dtor end
[M] dtor begin
[C] MEX begin id=2
-- unlock
[C] MEX end 2
[C] MyClass dtor id=0
[M] dtor end
[C] MEX atexit. size(map)=0
-- DLL_PROCESS_DETACH: hModule=0x3c3a0000

This is what I get in R2014a/Windows/VS2010:

>> obj1 = MyClass(10);
[M] ctor begin
-- DLL_PROCESS_ATTACH: hModule=0x3c3a0000
[C] MEX begin id=0
[C] MyClass ctor id=0
-- lock
[M] ctor end

>> obj2 = MyClass(20);
[M] ctor begin
[C] MEX begin id=0
[C] MyClass ctor id=0
-- lock
[M] ctor end

>> clear mex

>> clear all
[M] dtor begin
[C] MEX begin id=1
-- unlock
[C] MEX end 1
[C] MyClass dtor id=0
[M] dtor end
[M] dtor begin
[C] MEX begin id=2
-- unlock
[C] MEX end 2
[C] MyClass dtor id=0
[M] dtor end

>> clear all
[C] MEX atexit. size(map)=0
-- DLL_PROCESS_DETACH: hModule=0x3c3a0000

Notice how in R2014a clear all attempts to unload mex-files before clearing the variables, which is why a second clear all was needed.

classdef MyClass < handle
properties (SetAccess = private)
id
end
properties (Dependent)
x
end
methods
function this = MyClass(varargin)
disp('[M] ctor begin')
this.id = MyClass_(0, 'new', varargin{:});
disp('[M] ctor end')
end
function delete(this)
disp('[M] dtor begin')
if isempty(this.id), return; end
MyClass_(this.id, 'delete');
disp('[M] dtor end')
end
function load(this)
disp('[M] load begin')
MyClass_(this.id, 'load');
disp('[M] load end')
end
function call(this)
disp('[M] call begin')
MyClass_(this.id, 'call');
disp('[M] call end')
end
function value = get.x(this)
disp('[M] get begin')
value = MyClass_(this.id, 'get');
disp('[M] get end')
end
function set.x(this, value)
disp('[M] set begin')
MyClass_(this.id, 'set', value);
disp('[M] set end')
end
end
end
#include "mex.h"
#include <string>
#include <map>
#include <memory>
using namespace std;
#define LOCKING
#define ATEXIT
#ifdef _WIN32
#include <windows.h>
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
switch (dwReason) {
case DLL_PROCESS_ATTACH:
mexPrintf("-- DLL_PROCESS_ATTACH: hModule=0x%x\n", hModule);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
mexPrintf("-- DLL_PROCESS_DETACH: hModule=0x%x\n", hModule);
break;
}
return TRUE;
}
#endif
// fake cv::Ptr with C++11 smart pointers
#define Ptr std::shared_ptr
#define makePtr std::make_shared
class MyClass
{
public:
MyClass(int x = 0) : id(x) { mexPrintf("[C] MyClass ctor id=%d\n", id); }
~MyClass() { mexPrintf("[C] MyClass dtor id=%d\n", id); }
void call() { mexPrintf("[C] MyClass call id=%d\n", id); }
int get() { return id; }
void set(int x) { id = x; }
private:
int id;
};
namespace {
int last_id = 0;
map<int,Ptr<MyClass>> obj_;
}
static void onExit()
{
mexPrintf("[C] MEX atexit. size(map)=%d\n", obj_.size());
#ifdef ATEXIT
// because we use lock/unlock, this is not really needed
obj_.clear();
#endif
}
static void nargchk(bool cond)
{
if (!cond)
mexErrMsgIdAndTxt("mex:error", "wrong number of args");
}
static string toString(const mxArray* p)
{
if (!mxIsChar(p))
mexErrMsgIdAndTxt("mex:error", "not a string");
char *pc = mxArrayToString(p);
string str(pc);
mxFree(pc);
return str;
}
static int toInt(const mxArray *p)
{
if (!mxIsDouble(p) || mxGetNumberOfElements(p)!=1)
mexErrMsgIdAndTxt("mex:error", "not an int");
return static_cast<int>(*(mxGetPr(p)));
}
static mxArray* fromInt(int x)
{
return mxCreateDoubleScalar(x);
}
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
mexAtExit(onExit);
nargchk(nrhs>=2 && nlhs<=1);
int id = toInt(prhs[0]);
string method = toString(prhs[1]);
mexPrintf("[C] MEX begin id=%d\n", id);
if (method == "new") {
nargchk((nrhs==2||nrhs==3) && nlhs<=1);
obj_[++last_id] = makePtr<MyClass>((nrhs==3) ? toInt(prhs[0]) : last_id);
#ifdef LOCKING
mexLock(); mexPrintf("-- lock\n");
#endif
plhs[0] = fromInt(last_id);
return;
}
#if 1
Ptr<MyClass> obj = obj_[id];
if (!obj) mexWarnMsgIdAndTxt("mex:error", "invalid id=%d", id);
#else
auto it = obj_.find(id);
if (it == obj_.end()) {
// we should not throw errors inside MATLAB class destructors
if (method == "delete")
mexWarnMsgIdAndTxt("mex:warn", "invalid id=%d", id);
else
mexErrMsgIdAndTxt("mex:error", "invalid id=%d", id);
return;
}
Ptr<MyClass> obj = it->second;
#endif
if (method == "delete") {
obj_.erase(id);
#ifdef LOCKING
mexUnlock(); mexPrintf("-- unlock\n");
#endif
}
else if (method == "load") {
obj_[id] = makePtr<MyClass>(99);
}
else if (method == "call") {
obj->call();
}
else if (method == "get") {
plhs[0] = fromInt(obj->get());
}
else if (method == "set") {
obj->set(toInt(prhs[2]));
}
mexPrintf("[C] MEX end %d\n", id);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment