Thread safety of Matlab engine API
When I first started using the engine, I didn't run across any documentation on thread safety, so I assumed that it was not thread-safe.
I use a C++ class to synchronize access to an engine instance. For more parallel processing designs, I instantiate multiple instances of the engine class.
(edit) I'm using MATLAB R14 on Solaris. I open the engine using the 'engOpen' call, and close it using 'engClose'. My platform does not crash when the Close is called by a different thread than the one that called Open.
Call MATLAB directly (multiple threading) in Visual Studio
Here is a simple example I tried:
test_engine.cpp
#include <cstdio>
#include "engine.h"
#define BUFSIZE 1000
int main()
{
// open connection
Engine *matlab = engOpen(NULL);
if (matlab == NULL) {
fprintf(stderr, "Error to open MATLAB engine\n");
return EXIT_FAILURE;
}
// output buffer
char buf[BUFSIZE+1];
buf[BUFSIZE] = '\0';
engOutputBuffer(matlab, buf, BUFSIZE);
// call MATLAB
engEvalString(matlab, "x = magic(5)");
printf("Output:\n%s\n", buf);
// close connection
engClose(matlab);
return EXIT_SUCCESS;
}
Instead of manually creating a Visual Studio project to compile it, we can do this right from MATLAB:
>> mbuild test_engine.cpp -llibeng -llibmx
in R2014a and up, we can also use:
>> mex -client engine test_engine.cpp
(assuming you've run mex -setup
and mbuild -setup
to select a proper C++ compiler, or mex -setup C++
and mex -setup C++ -client MBUILD
in R2014a).
Here is the output of the program (I'm running R2014a x64 with VS2013):
C:\> test_engine.exe
Output:
x =
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
EDIT:
Here is how to compile the above example in Visual Studio.
Start by creating a new empty "Win32 Console Application" project
Since we're working with MATLAB x64, we need to adjust project configuration to generate 64-bit binaries. From "Build" menu, select "Configuration Manager". From the drop-down menu choose
<New>
to create a new platform configs, selectx64
and accept.Add the source code from the previous example
test_engine.cpp
Next step is tell the VC++ compiler/linker where to find MATLAB headers and libraries. We could do this by setting the project properties.
A better approach is to create a separate property sheet that can be reused from multiple projects without having to repeat the same settings over and over again. So create the
MATLAB_Engine.props
property sheet shown below, and add it the project (open the "Property Manager" panel and click the "Add Existing Property Sheet" button).Finally build the solution and run it (Ctrl+F5)
MATLAB_Engine.props
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<LocalDebuggerEnvironment>PATH=C:\Program Files\MATLAB\R2014a\bin\win64;%PATH%$(LocalDebuggerEnvironment)</LocalDebuggerEnvironment>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>C:\Program Files\MATLAB\R2014a\extern\include;C:\Program Files\MATLAB\R2014a\extern\include\win64</AdditionalIncludeDirectories>
<PreprocessorDefinitions>IBMPC</PreprocessorDefinitions>
</ClCompile>
<Link>
<AdditionalLibraryDirectories>C:\Program Files\MATLAB\R2014a\extern\lib\win64\microsoft</AdditionalLibraryDirectories>
<AdditionalDependencies>libmx.lib;libmex.lib;libmat.lib;libeng.lib;mclmcrrt.lib</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup />
</Project>
(this is intended for MATLAB R2014a on a 64-bit Windows. Adjust the path if you installed MATLAB to a different location)
The output as expected:
Tip: if you are testing an engine program repeatedly, then each time it runs a new MATLAB process opens up and closes in the background. To make things easier during development, you could start a normal MATLAB session, and execute the command below to tell it to act like as an Automation server. That way all Engine programs will run in this same session which remains open.
>> enableservice('AutomationServer',true);
EDIT2:
The MATLAB documentation explicitly states that the Engine library is not thread-safe (same goes for the MEX-API and the MAT-API). In Windows systems, standalone Engine programs communicate with the external MATLAB process through a COM interface, while it uses pipes on Linux/Mac systems as IPC mechanism.
So if you create multi-threaded applications, make sure only one thread accesses the engine application.
Note: For Windows only, there is another function engOpenSingleUse
. It differs from engOpen
in that it creates a new non-shared MATLAB engine session. That way you can have multiple threads each connected to a different session (obviously workspace is not shared, because each session has a separate address space).
Multithreaded C++ application using Matlab Engine
CoInitialize has to be called from every thread where you use a COM object, not just the main thread.
It has been a decade since I last automated Matlab, so excuse rustiness in what follows. That you have received the CoInitialize error suggests that the engOpen call wraps underlying COM calls. Unfortunately, this exposes you unawares to the can of worms that is COM. I guess you are right and that engOpen includes a call to CoInitialize, which initialises the COM library on the current thread. To access COM objects from a thread CoInitialize must always have been called on that thread before any calls to COM (other than one permitted COM function, I forget which.)
My advice is to isolate all of your Matlab calls onto one thread now. If you do that you won't have to make the explicit CoInitialize call, and you avoid any later multithreaded COM issues. You might get your program working today by calling CoInitialize on the second thread but one day you'll be bitten by another COM problem.
[I spent about a decade elbows-deep on COM and it is full of bear traps. You could spend a few weeks reading up on a technology which Microsoft tried to hide / kill with .Net, but it is better just to take the easy (single-thread) path now and forget about it.]
Update
I'm afraid your edit has tripped you into the mire of COM threading models. COINIT_MULTITHREADED effectively tells COM that you're going to take care of all of the little nuances of threading, which is almost certainly not what you want to do. COM operates with several (last time I paid attention it was three) threading models and the parameter you pass to CoInitializeEx declares which of those models you wish to use.
Apologies to all if what follows is slightly off.
If you specify COINIT_MULTITHREADED you need to either know that the COM object you are calling is thread-safe or do the appropriate locking (and marshalling of interfaces and data between threads) yourself.
COINIT_APARTMENTTHREADED, which is probably what engOpen uses as in my experience it's the most common, lets the COM library deal with multithreading for you. The library may, for instance, create proxy and stub objects to mediate calls across threads (or process boundaries, which is what will happen when you call to Matlab.)
engOpen created a Matlab proxy object on your main thread. This proxy object can be called from the thread where it was created and, if I recall correctly, from any other thread in the 'apartment' (where CoInitializeEx has been called with COINIT_APARTMENTTHREADED.) You have tried to call through the proxy from a thread in a different threading model, the COM library has noticed, and has issued the error you mention.
In many ways COM is amazing, but the intricacies are a pain in the arse. Be thankful you are never likely to have to use Distributed COM, which is truly nasty!
Update 2
My ancient memory of COM threading models is wrong. This MSDN page states that there is one thread per apartment with COINIT_APARTMENTTHREADED. COM objects can be accessed using the same interface pointer from all threads in the apartment where they were created. For COINIT_APARTMENTTHREADED that means just the thread where the object was created. In COINIT_MULTITHREADED that would be all the threads in the multithreaded apartment but (1) you don't get to choose which thread the Matlab engine is created on if you use engOpen and (2) trying to call a COM object that you didn't write from a multithreaded apartment is risky. The original OLE threading model only allowed COM calls from the main GUI thread, BTW.
Asynchronous call to MATLAB's engEvalString
So, I figured out the problem, but would love it if someone could explain why!
The following works:
#include <boost/thread.hpp>
extern "C" {
#include <engine.h>
}
void asyncEvalString()
{
Engine* eng = engOpen("");
engEvalString(eng,"y=5");
}
int main()
{
Engine* eng = engOpen("");
engEvalString(eng,"x=10");
boost::thread asyncEvalString(&asyncEvalString);
boost::this_thread::sleep(boost::posix_time::seconds(1));
engEvalString(eng,"z=15");
return 0;
}
As you can see, you need to get a new pointer to the engine in the new thread. The pointer returned in asyncEvalString
is different to the original pointer returned by engOpen
in the main function, however both pointers continue to operate without problem:
» x
x =
10
» y
y =
5
» z
z =
15
Finally, to tackle the problem of thread safety, a mutex could be set up around the engEvalString calls to ensure only one thread uses the engine at any one time. The asyncEvalString function could also be modified to trigger a callback function once the engEvalString function has been completed.
I would however appreciate someone explaining why the above solution works. Threads share heap allocated memory of the process, and can access memory on other threads' stacks (?), so I fail to understand why the first Engine*
was suddenly invalid when used in a separate thread.
Connect to already running MATLAB with MATLAB Engine
"On the Mac and Linux® platforms, you cannot make an engine program connect to an existing MATLAB session."
Error when calling compiled m-file(.jar) in a multi threaded Java app
My guess is that MATLAB requires you to access it from exactly one thread. You say it works in a single threaded application, perhaps you need to start a dedicated thread for interacting with MATLAB to get this to work correctly.
See also
Thread safety of Matlab engine API
Related Topics
Piping Input into a C++ Program to Debug in Visual Studio
Cannot Evaluate Function -- May Be Inlined
How to Do Password Authentication for a User Using Ldap
How to Compile a C++ Code in Gcc (G++) to See The Name Mangling on Overloaded Functions
How to Check That I Didn't Break Anything When Refactoring
How to Reproduce Tcp Protocol 3-Way Handshake with Raw Sockets Correctly
Documenting Preprocessor Defines in Doxygen
What's the Real Use of Using N[C-'0']
Linux Ipc - Multiple Writers, Single Reader
Conversion from 'Myitem*' to Non-Scalar Type 'Myitem' Requested
How to Efficiently Wait for Cts or Dsr of Rs232 in Linux
What Is The Linux Equivalent Of: Multibytetowidechar & Widechartomultibyte
How to Find The Full Path of The C++ Linux Program from Within
Move the String Out of a Std::Ostringstream