2006-08-23 Wim Taymans <wim@fluendo.com>
+ * gst/gstpad.c: (gst_pad_start_task), (gst_pad_pause_task),
+ (gst_pad_stop_task):
+ Improve debugging for task functions.
+
+ * gst/gsttask.c: (gst_task_func), (gst_task_set_lock),
+ (gst_task_start), (gst_task_pause), (gst_task_join):
+ Make sure that the task function started and finished after a
+ join().
+ Don't try to push the task function on the threadpool multiple
+ times.
+ Improve the g_warning message with some useful suggestions
+ about how to fix the problem.
+
+2006-08-23 Wim Taymans <wim@fluendo.com>
+
* gst/gstutils.c: (gst_pad_proxy_getcaps):
Handle RESYNC correctly in _proxy_getcaps.
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
g_return_val_if_fail (func != NULL, FALSE);
+ GST_DEBUG_OBJECT (pad, "start task");
+
GST_OBJECT_LOCK (pad);
task = GST_PAD_TASK (pad);
if (task == NULL) {
task = gst_task_create (func, data);
gst_task_set_lock (task, GST_PAD_GET_STREAM_LOCK (pad));
GST_PAD_TASK (pad) = task;
+ GST_DEBUG_OBJECT (pad, "created task");
}
gst_task_start (task);
GST_OBJECT_UNLOCK (pad);
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+ GST_DEBUG_OBJECT (pad, "pause task");
+
GST_OBJECT_LOCK (pad);
task = GST_PAD_TASK (pad);
if (task == NULL)
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+ GST_DEBUG_OBJECT (pad, "stop task");
+
GST_OBJECT_LOCK (pad);
task = GST_PAD_TASK (pad);
if (task == NULL)
no_task:
{
+ GST_DEBUG_OBJECT (pad, "no task");
GST_OBJECT_UNLOCK (pad);
GST_PAD_STREAM_LOCK (pad);
* the task's thread. We install the task again so that it will be stopped
* again from the right thread next time hopefully. */
GST_OBJECT_LOCK (pad);
+ GST_DEBUG_OBJECT (pad, "join failed");
/* we can only install this task if there was no other task */
if (GST_PAD_TASK (pad) == NULL)
GST_PAD_TASK (pad) = task;
lock = GST_TASK_GET_LOCK (task);
if (G_UNLIKELY (lock == NULL))
goto no_lock;
- task->running = TRUE;
task->abidata.ABI.thread = tself;
GST_OBJECT_UNLOCK (task);
GST_OBJECT_UNLOCK (task);
g_static_rec_mutex_unlock (lock);
- /* now we allow messing with the lock again */
GST_OBJECT_LOCK (task);
- task->running = FALSE;
task->abidata.ABI.thread = NULL;
+
exit:
+ /* now we allow messing with the lock again by setting the running flag to
+ * FALSE. Together with the SIGNAL this is the sign for the _join() to
+ * complete.
+ * Note that we still have not dropped the final ref on the task. We could
+ * check here if there is a pending join() going on and drop the last ref
+ * before releasing the lock as we can be sure that a ref is held by the
+ * caller of the join(). */
+ task->running = FALSE;
GST_TASK_SIGNAL (task);
GST_OBJECT_UNLOCK (task);
* with @data as a parameter. Typically the task will run in
* a new thread.
*
+ * The function cannot be changed after the task has been created. You
+ * must create a new GstTask to change the function.
+ *
* Returns: A new #GstTask.
*
* MT safe.
gst_task_set_lock (GstTask * task, GStaticRecMutex * mutex)
{
GST_OBJECT_LOCK (task);
- if (task->running)
+ if (G_UNLIKELY (task->running))
goto is_running;
GST_TASK_GET_LOCK (task) = mutex;
GST_OBJECT_UNLOCK (task);
{
GstTaskClass *tclass;
- tclass = GST_TASK_GET_CLASS (task);
+ /* If the task already has a thread scheduled we don't have to do
+ * anything. */
+ if (task->running)
+ break;
/* new task, push on threadpool. We ref before so
* that it remains alive while on the threadpool. */
gst_object_ref (task);
+ /* mark task as running so that a join will wait until we schedule
+ * and exit the task function. */
+ task->running = TRUE;
+
+ tclass = GST_TASK_GET_CLASS (task);
+
g_static_mutex_lock (&pool_lock);
g_thread_pool_push (tclass->pool, task, NULL);
g_static_mutex_unlock (&pool_lock);
{
GstTaskClass *tclass;
- tclass = GST_TASK_GET_CLASS (task);
+ if (task->running)
+ break;
gst_object_ref (task);
+ task->running = TRUE;
+
+ tclass = GST_TASK_GET_CLASS (task);
+
g_static_mutex_lock (&pool_lock);
g_thread_pool_push (tclass->pool, task, NULL);
g_static_mutex_unlock (&pool_lock);
* The task will automatically be stopped with this call.
*
* This function cannot be called from within a task function as this
- * will cause a deadlock.
+ * would cause a deadlock. The function will detect this and print a
+ * g_warning.
*
* Returns: TRUE if the task could be joined.
*
/* we don't use a real thread join here because we are using
* threadpools */
GST_OBJECT_LOCK (task);
- if (tself == task->abidata.ABI.thread)
+ if (G_UNLIKELY (tself == task->abidata.ABI.thread))
goto joining_self;
task->state = GST_TASK_STOPPED;
+ /* signal the state change for when it was blocked in PAUSED. */
GST_TASK_SIGNAL (task);
- while (task->running)
+ /* we set the running flag when pushing the task on the threadpool.
+ * This means that the task function might not be called when we try
+ * to join it here. */
+ while (G_LIKELY (task->running))
GST_TASK_WAIT (task);
GST_OBJECT_UNLOCK (task);
{
GST_WARNING_OBJECT (task, "trying to join task from its thread");
GST_OBJECT_UNLOCK (task);
- g_warning ("trying to join task %p from its thread would deadlock", task);
+ g_warning ("\nTrying to join task %p from its thread would deadlock.\n"
+ "You cannot change the state of an element from its streaming\n"
+ "thread. Use g_idle_add() or post a GstMessage on the bus to\n"
+ "schedule the state change from the main thread.\n", task);
return FALSE;
}
}