UNO oneway call deadlocks using a remote bridge shown at a clipboard API scenario


Normally a deadlock can only occur in conjunction with at least two threads acquiring at least two synchronisation objects concurrently.


But using oneway methods of UNO interfaces through a remote bridge can cause a deadlock even if only one thread is running and only one synchronisation object is used.


One key feature of UNO is that it preserves the order of method calls of any kind (synchronous, oneway) over every bridge. Using an inprocess bridge to an interface this is quite easy because the order is preserved by the callstack of the current thread. Actually a oneway call is synchronous to.


But using an interface through a remote bridge changes the situation. When calling a oneway method the remote bridge accepts the call of the caller and returns immediatly. The call is delivered to the remote end of the bridge and executed. If the caller now calls further oneway methods on that interface in the same thrwead context the calls are delivered to the remote end of the bridge and the caller thread continues execution. But on the remote side of the bridge the the call is scheduled until all previous oneway calls are executed.

If the caller now calls a synchronous method on the interface not only the cally thread on the remote side of the bridge has to wait until all previous oneway calls are finished but also the caller blocks until all previous oneway calls of the thread are finished.

This behaviour ensures the call order when using a remote bridge.


But there is a caveat. The remote bridge implies an additonal synchronisation object and an additional thread on cally side that can cause a deadlock scenario that is not even evident.


Let me explain the problem by an scenario using the clipboard API. The interface com.sun.star.datatransfer.XClipboard has a method setContens to put some content into the clipboard. This method is declared oneway because the client must not wait until the clipboard has finished. The method getContents on the other hand is synchronous because it needs the clipboard content to return.


Assume one thread on caller side calls XClipboard::setContents. The caller thread continues executing while the call is delivered to the remote interface. Now the caller wants to ensure that the data has arrived in the clipboard, locks the formerly know SolarMutex and calls XClipboard::getContents. The call blocks on caller side because the cally still executes the setContents call and as you remember UNO wants to preserve the call order.

Now the cally – the clipboard implementation on remote side – puts the new data into the clipboard and wants to inform the previous clipboard owner that he's not longer the owner of the clipboard. It calls back to the cally side through a XClipboardOwner interface. This call is not executed in the origin thread on cally side because the thread has continued execution after the oneway call. Therefore UNO executes the callback in a new thread. Now the clipboard owner recognizes that content change of the clipboard and wants to update his state and tries to lock the SolarMutex. Unfortunately the SolarMutex is already locked and owned by the origin thread. The result is that the callback waits for the SolarMutex and blocks the termination of the oneway call and the synchronous getContents call block too becuase he waits for the termination of the oneway call.


This situation is a deadlock.


How to prevent from this situation ? Well, actually the implementation of clipboard services do not rely on the call order of the oneway calls of setContens. Each oneway call received is marshalled into one dispatcher thread and the oneway call returns immediatly. This prevents from the situation that subsequent synchronous calls have to wait for the completion of the oneway calls.


There are two issues to care about:


If you are implementing a oneway method of an UNO interface try to return immedialty and deliver execution in an extra thread if you use other interfaces to call back inside the implementation of the oneway method. This is what the clipboard does.


If you are using a oneway method and the call order is not relevant, do call oneway methods in the same thread you are using to call synchronous methods but rather do the oneway calls in an extra thread. This is quite neccessary if you know of the implementation of the interface that it does callbacks through other interfaces during execution of oneway methods.

If you are not aware what the implementation does or even worse you have to rely on call order make sure you don't own any mutex or other synchronisation object that maybe accessed in a callback during a oneway method execution.