gstreamer/omx/gstomxrecmutex.c
George Kiagiadakis 158775f497 Implement a new custom recursive mutex type and fix locking in callbacks so that in-context calls are allowed.
According to the OMX specification, implementations are allowed to call
callbacks in the context of their function calls. However, our callbacks
take locks and this causes deadlocks if the unerlying OMX implementation
uses this kind of in-context calls.

A solution to the problem would be a recursive mutex. However, a normal
recursive mutex does not fix the problem because it is not guaranteed
that the callbacks are called from the same thread. What we see in Broadcom's
implementation for example is:

- OMX_Foo is called
- OMX_Foo waits on a condition
- A callback is executed in a different thread
- When the callback returns, its calling function
  signals the condition that OMX_Foo waits on
- OMX_Foo wakes up and returns

The solution I came up with here is to take a second lock inside the callback,
but only if recursion is expected to happen. Therefore, all calls to OMX
functions are guarded by calls to gst_omx_rec_mutex_begin_recursion() / _end_recursion(),
which effectively tells the mutex that at this point we want to allow calls
to _recursive_lock() to succeed, although we are still holding the master lock.
2012-05-07 17:01:16 +03:00

97 lines
2.5 KiB
C

/*
* Copyright (C) 2012, Collabora Ltd.
* Author: George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstomxrecmutex.h"
void
gst_omx_rec_mutex_init (GstOMXRecMutex * mutex)
{
mutex->lock = g_mutex_new ();
mutex->recursion_lock = g_mutex_new ();
mutex->recursion_allowed = FALSE;
}
void
gst_omx_rec_mutex_clear (GstOMXRecMutex * mutex)
{
g_mutex_free (mutex->lock);
g_mutex_free (mutex->recursion_lock);
}
void
gst_omx_rec_mutex_lock (GstOMXRecMutex * mutex)
{
g_mutex_lock (mutex->lock);
}
void
gst_omx_rec_mutex_unlock (GstOMXRecMutex * mutex)
{
g_mutex_unlock (mutex->lock);
}
/* must be called with mutex->lock taken */
void
gst_omx_rec_mutex_begin_recursion (GstOMXRecMutex * mutex)
{
g_mutex_lock (mutex->recursion_lock);
g_assert (mutex->recursion_allowed == FALSE);
mutex->recursion_allowed = TRUE;
g_mutex_unlock (mutex->recursion_lock);
}
/* must be called with mutex->lock taken */
void
gst_omx_rec_mutex_end_recursion (GstOMXRecMutex * mutex)
{
g_mutex_lock (mutex->recursion_lock);
g_assert (mutex->recursion_allowed == TRUE);
mutex->recursion_allowed = FALSE;
g_mutex_unlock (mutex->recursion_lock);
}
void
gst_omx_rec_mutex_recursive_lock (GstOMXRecMutex * mutex)
{
g_mutex_lock (mutex->recursion_lock);
if (!mutex->recursion_allowed) {
/* no recursion allowed, lock the proper mutex */
g_mutex_unlock (mutex->recursion_lock);
g_mutex_lock (mutex->lock);
}
}
void
gst_omx_rec_mutex_recursive_unlock (GstOMXRecMutex * mutex)
{
/* It is safe to check recursion_allowed here because
* we hold at least one of the two locks and
* either lock protects it from being changed.
*/
if (mutex->recursion_allowed) {
g_mutex_unlock (mutex->recursion_lock);
} else {
g_mutex_unlock (mutex->lock);
}
}