Editor freezing with multithreaded DLL native plugin in Release but not in Debug

I have a DLL with a native plugin that runs a very time consuming infinite loop which runs in a separate thread to avoid freezing the main Unity rendering thread. Everything works flawlessly when I compile and run my DLL in Debug mode, but if I compile it in Release, it runs perfectly fine the first time in the editor but if I press play again, everything freezes (Start is never even called the second time, neither is UnityPluginLoad). I’m at the end of my rope with this issue and any help would be greatly appreciated.

And now for the technical details, a dumbed down version of my architecture.

I used SWIG to create an interface between my C++ code and C# code. The interface itself has nothing really fancy going on and there is absolutely no extra SWIG magic needed to get it to work.

On the Unity side I’m doing:

public class myBehavior : MonoBehaviour
{
    private myDLLInterface m_interface;

    private delegate void eventCallbackDelegate( int eventID );
    eventCallbackDelegate m_eventCallback = null;

    void OnDestroy()
    {
        StopCoroutine( "CallPluginAtEndOfFrames" );
        m_interface = null;
    }

    IEnumerator Start()
    {
        m_interface= new myDLLInterface ();
        m_eventCallback = new eventCallbackDelegate( m_interface.updateFrameDataOGL );
        m_interface.run();
        yield return StartCoroutine( "CallPluginAtEndOfFrames" );
    }

    void Update()
    { }

    private IEnumerator CallPluginAtEndOfFrames()
    {
        if( m_eventCallback != null )
        {
            while( m_poseEstimator != null )
            {
                // Wait until all frame rendering is done
                yield return null;

                GL.IssuePluginEvent( Marshal.GetFunctionPointerForDelegate( m_eventCallback ), 1 );
                //Play can stop in the middle of coroutine.
                if( m_interface!= null )
                {
                    doStuff();
                }
            }
        }
    }
}

And this is what’s happening inside my DLL:

myDLLInterface::~myDLLInterface()
{
     if( m_loopRunner)
	{
		m_loopRunner->stop();
		
		while( m_loopRunner->isWorking() )
			;
		delete m_loopRunner;
		m_loopRunner= NULL;
	}
}

void myDLLInterface::run()
{
	std::thread t( &TheLoopRunner::run, m_loopRunner);
	t.detach();
}

void SolARPoseEsimatorDllInterface::updateFrameDataOGL( int eventID )
{
	if( m_loopRunner->tryLock() )
	{
            doStuff();
		m_loopRunner->unlock();
	}
}

And finally, in my TheLoopRunner, this is happening:

TheLoopRunner has member variable: std::mutex m_myMutex;

TheLoopRunner::run()
{
   m_runLoop = true;
   m_isWorking = true;
   while( m_runLoop )
   {
       doExpensiveComputations();
       m_myMutex.lock();
       updateOutput();
       unlock();
   }
   m_isWorking = false;
}

void TheLoopRunner::stop()
{
   m_runLoop = false;
}

bool TheLoopRunner::isWorking()
{
   return m_isWorking();
}

bool TheLoopRunner::tryLock()
{
    return m_myMutex.try_lock();
}

void TheLoopRunner()::unlock()
{
   m_myMutex.unlock();
}

I also tried not detaching the thread, and storing it in a std::thread* member variable and deleting it in the myDllInterface destructor (with or without joining before deleting) , but the behavior remains the same.

What happens in Debug that allows this to work 100% of the time and waht happens in release that stops this from working 100% of the time?

I’m actually encountering the same issue, how did you manage it @LarrxX ?

EDIT:
Ok, I finally solved it.

First, it’s important to understand how Unity manage its threads (it can be usefull for further issues): https://blog.tedd.no/2016/10/09/investigating-unity-hang-on-second-run-multi-threading/

This link was also really usefull to understand that is better to explicitly stop the thread in the C++ by calling a dedicated function due to Unity thread management : https://forum.unity.com/threads/problem-with-callbacks.87513/

But, for my case, the error was due to a global object, the one accessed by the C function for my thread initialization, that was not destroyed across different launch of the game in Unity.
Now my global object is a smart pointer, and I reset the underlying pointer and make a new one at each call of my thread initialization function.

That’s how I finally solved it. I had an explicit “dispose” method that I call when exiting the application, that forcibly stops the thread.