Deadlock due to lacking documentation?


#1

Just spend a nice amount of time debugging a deadlock in some seemingly valid code.

Here's what happening:

1. Main thread/message thread (#thread 1) "waits" on renderOpenGL(), since it gains a MessageManagerLock (juce_OpenGLContext.cpp:170) - ie. lock #1
2. Thread #2 acquires lock #2, and tries to acquire lock #1 (MessageManagerLock), but never succeeds, since
3. openglthread (#thread 3) tries to acquire lock #2, while implicitly having lock #1

I would have never written the code as is, because I assumed no synchronization happens between the openGL thread and the main thread, since the documentation of OpenGLRenderer::renderOpenGL states the following:

        Note that this callback will be made on a background thread, not the message
        thread, so make sure that your implementation is thread-safe.

Ie. it is not clear that there may be a possible deadlock situation using MessageManagerLock's. This should probably be stated. Another thing, is it guaranteed that MessageManagerLocks are recursive (I don't find anything in the description about this), that is, can I acquire the MessageManagerLock from inside the renderOpenGL() call without trouble, while it already is acquired on the current thread?

This was on JUCE 3, but I can see the documentation hasn't changed since.


#2

Good point, I'll tweak the docs - in fact it only locks the MM when you've also attached the context to a component, but it has to do that so that it can safely call paint() on the render thread.

And yes, MM locks are re-entrant - that's already in the docs for its constructor, but I'll add it to the class description too.


#3

Okay, yeah I see it now. So the OpenGL rendering won't synchronize if i set setComponentPainting(false)?


#4

That's true at the moment, but it's an implementation detail and no guarantees about how that'll work on all platforms in the future.


#5

Cool, as long as it says so in le docs!