5
votes

I am writing a C++/CLI wrapper for a native C++ library. In one of the classes that is returned back to the CLI wrapper, uses thread and, specifically #include <mutex> in the header to define the class level mutex.

The problem is once the header is brought into the CLI code (with the /clr option enabled), I get the error that <mutex> is not supported when compiling with /clr or /clr:pure.

Reading this post How to implement a unmanaged thread-safe collection when I get this error: <mutex> is not supported when compiling with /clr, there was a blog article which mentions some possible work arounds. The workarounds, however, assume that you don't need any header files which cause a conflict and all can be done within the class functions, essentially hiding the thread functions from the CLI app.

In the case of a class level mutex, this is not the case. It has to be in the header.

Is there any way to make a CLI app work with a native lib that is threaded?

Yes, I am aware of the GetCurrentThreadID() issue between managed and unmanaged code, but what is the right approach? Or is there no way around it?

I cringe at having to incorporate something like ZMQ because this is in a critical section and now having three copies of data (which could be very large) would be prohibitive. (one on the managed side, one on the native, and one to pass through the message buffer in ZMQ). I am aware of ZMQ's zero copy, but have not tried inproc between managed and unmanaged c++ to see if it is possible to share memory. If it is possible, it would probably require a low level contiguous data structure to be used throughout the application or suffer the consequences of copying the data again.

Any wizards out there with a decent solution?

3
You might try writing a PIMPL wrapper around the class you're consuming in a separate .cpp file and compile that .cpp without a /clr variant, and use that PIMPL wrapper in the rest of your code instead of the original class. This way nothing with a std::mutex is compiled with a /clr variant.ildjarn
Hmmm....interesting idea. Though, is it safe knowing that GetCurrentThreadID() will be used in the native c++? I am not trying to carry threads between managed to unmanaged, so it doesn't seem like it would be a problem. But, of this, I am unsure.user3072517
Calling any given native function is not a problem. The problem is compiling certain code with a /clr variant. A general rule of thumb for mixed-mode C++/CLI is to compile as little code as possible with /clr, only the necessary interop bits.ildjarn
Yeah, that is what I am trying to do...it is just that the header needs the mutex, so now I have to isolate that. Which is not a bad thing....but now I have to add another layer. :-(user3072517
To be fair, writing that layer is pretty much the only thing C++/CLI is meant for to begin with. ;-]ildjarn

3 Answers

1
votes

The solution is to use #ifdef to selectively hide the declaration of the mutex in the .h file in CLI code but still compile it in the cpp native code.

nativeClass.h file:

#ifdef NATIVE_CODE
    #include <mutex>
#endif

class naticeClass {
public:
    void doSomething();
private:
#ifdef NATIVE_CODE
    mutex _mutex;
#endif

};

in nativeClass.cpp:

#define NATIVE_CODE 
#include "nativeClass.h"

void nativeClass:doSomething()
{
    _mutex.lock();
    ...

}
0
votes

I looked into this problem for std::thread before, I found the following to be working. Since CLR does not allow you to include std::thead at compile time, you could try to use it only at linking time. Normally you could resolve this be forward declaring the class in your header and including them only in your cpp files. However you can forward declare your own classes in header files, but you can't for classes in namespace std. According to the C++11 standard, 17.6.4.2.1:

The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified.

A workaround for this problem is to create a threading class that inherits from std::thread that you can forward declare. The header file for this class would look like:

#pragma once
#include <thread>
#include <utility>
namespace Threading
{
    class Thread : std::thread
    {
    public:
        template<class _Fn, class... _Args> Thread(_Fn fn, _Args... args) : std::thread(fn, std::forward<_Args...>(args...))
        {

        }
    private:

    };
}

In the header file that you would like to use the thread you can do forward declare it like:

#pragma once

// Forward declare the thread class 
namespace Threading { class Thread; }
class ExampleClass
{
    public:
        ExampleClass();
        void ThreadMethod();
    private:
        Threading::Thread * _thread;
};

In your source file you can then use the theading class like:

#include "ExampleClass.h"
#include "Thread.h"

ExampleClass::ExampleClass() :
{
    _thread = new Threading::Thread(&ExampleClass::ThreadMethod, this);
}

void ExampleClass::ThreadMethod()
{
}

You should be able to do the same thing for std::mutex. Hope it might help anyone.

Original post: using clr and std::thread

0
votes

Here's how I got around this problem--by creating a roll-your-own Mutex. I'm not overjoyed with the solution because of the spinning wait but it seems to work just fine. I only need to protect multithreaded access to std::vector.

in .h file:
    std::atomic<int> vectorLock = 0;
in .cpp file:
    while (vectorLock.exchange(1) == 1) {}
         ... critical code goes here
    vectorLock = 0;