/** * gst_task_set_thread_callbacks: * @task: The #GstTask to use * @callbacks: a #GstTaskThreadCallbacks pointer * @user_data: user data passed to the callbacks * @notify: called when @user_data is no longer referenced * * Set callbacks which will be executed when a new thread is needed, the thread * function is entered and left and when the thread is joined. * * By default a thread for @task will be created from a default thread pool. * * Objects can use custom GThreads or can perform additional configuration of * the threads (such as changing the thread priority) by installing callbacks. * * MT safe. * * Since: 0.10.24 */ void gst_task_set_thread_callbacks (GstTask * task, GstTaskThreadCallbacks * callbacks, gpointer user_data, GDestroyNotify notify) { GDestroyNotify old_notify; g_return_if_fail (task != NULL); g_return_if_fail (GST_IS_TASK (task)); g_return_if_fail (callbacks != NULL); GST_OBJECT_LOCK (task); old_notify = task->priv->thr_notify; if (old_notify) { gpointer old_data; old_data = task->priv->thr_user_data; task->priv->thr_user_data = NULL; task->priv->thr_notify = NULL; GST_OBJECT_UNLOCK (task); old_notify (old_data); GST_OBJECT_LOCK (task); } task->priv->thr_callbacks = *callbacks; task->priv->thr_user_data = user_data; task->priv->thr_notify = notify; GST_OBJECT_UNLOCK (task); }
EXPORT_C #endif gboolean gst_task_stop (GstTask * task) { GstTaskClass *tclass; GstTaskState old; g_return_val_if_fail (GST_IS_TASK (task), FALSE); tclass = GST_TASK_GET_CLASS (task); GST_DEBUG_OBJECT (task, "Stopping task %p", task); GST_OBJECT_LOCK (task); old = task->state; task->state = GST_TASK_STOPPED; switch (old) { case GST_TASK_STOPPED: break; case GST_TASK_PAUSED: GST_TASK_SIGNAL (task); break; case GST_TASK_STARTED: break; } GST_OBJECT_UNLOCK (task); return TRUE; }
/** * gst_task_join: * @task: The #GstTask to join * * Joins @task. After this call, it is safe to unref the task * and clean up the lock set with gst_task_set_lock(). * * The task will automatically be stopped with this call. * * This function cannot be called from within a task function as this * would cause a deadlock. The function will detect this and print a * g_warning. * * Returns: %TRUE if the task could be joined. * * MT safe. */ gboolean gst_task_join (GstTask * task) { GThread *tself; GstTaskPrivate *priv; gpointer id; GstTaskPool *pool = NULL; priv = task->priv; g_return_val_if_fail (GST_IS_TASK (task), FALSE); tself = g_thread_self (); GST_DEBUG_OBJECT (task, "Joining task %p, thread %p", task, tself); /* we don't use a real thread join here because we are using * thread pools */ GST_OBJECT_LOCK (task); 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); /* we set the running flag when pushing the task on the thread pool. * 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); /* clean the thread */ task->abidata.ABI.thread = NULL; /* get the id and pool to join */ pool = priv->pool_id; id = priv->id; priv->pool_id = NULL; priv->id = NULL; GST_OBJECT_UNLOCK (task); if (pool) { if (id) gst_task_pool_join (pool, id); gst_object_unref (pool); } GST_DEBUG_OBJECT (task, "Joined task %p", task); return TRUE; /* ERRORS */ joining_self: { GST_WARNING_OBJECT (task, "trying to join task from its thread"); GST_OBJECT_UNLOCK (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; } }
EXPORT_C #endif gboolean gst_task_pause (GstTask * task) { GstTaskState old; g_return_val_if_fail (GST_IS_TASK (task), FALSE); GST_DEBUG_OBJECT (task, "Pausing task %p", task); GST_OBJECT_LOCK (task); if (G_UNLIKELY (GST_TASK_GET_LOCK (task) == NULL)) goto no_lock; old = task->state; task->state = GST_TASK_PAUSED; switch (old) { case GST_TASK_STOPPED: { GstTaskClass *tclass; 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); break; } case GST_TASK_PAUSED: break; case GST_TASK_STARTED: break; } GST_OBJECT_UNLOCK (task); return TRUE; /* ERRORS */ no_lock: { GST_WARNING_OBJECT (task, "pausing task without a lock"); GST_OBJECT_UNLOCK (task); g_warning ("pausing task without a lock"); return FALSE; } }
/** * gst_task_get_state: * @task: The #GstTask to query * * Get the current state of the task. * * Returns: The #GstTaskState of the task * * MT safe. */ GstTaskState gst_task_get_state (GstTask * task) { GstTaskState result; g_return_val_if_fail (GST_IS_TASK (task), GST_TASK_STOPPED); GST_OBJECT_LOCK (task); result = task->state; GST_OBJECT_UNLOCK (task); return result; }
/** * gst_task_set_state: * @task: a #GstTask * @state: the new task state * * Sets the state of @task to @state. * * The @task must have a lock associated with it using * gst_task_set_lock() when going to GST_TASK_STARTED or GST_TASK_PAUSED or * this function will return %FALSE. * * MT safe. * * Returns: %TRUE if the state could be changed. * * Since: 0.10.24 */ gboolean gst_task_set_state (GstTask * task, GstTaskState state) { GstTaskState old; gboolean res = TRUE; g_return_val_if_fail (GST_IS_TASK (task), FALSE); GST_DEBUG_OBJECT (task, "Changing task %p to state %d", task, state); GST_OBJECT_LOCK (task); if (state != GST_TASK_STOPPED) if (G_UNLIKELY (GST_TASK_GET_LOCK (task) == NULL)) goto no_lock; /* if the state changed, do our thing */ old = task->state; if (old != state) { task->state = state; switch (old) { case GST_TASK_STOPPED: /* If the task already has a thread scheduled we don't have to do * anything. */ if (G_UNLIKELY (!task->running)) res = start_task (task); break; case GST_TASK_PAUSED: /* when we are paused, signal to go to the new state */ GST_TASK_SIGNAL (task); break; case GST_TASK_STARTED: /* if we were started, we'll go to the new state after the next * iteration. */ break; } } GST_OBJECT_UNLOCK (task); return res; /* ERRORS */ no_lock: { GST_WARNING_OBJECT (task, "state %d set on task without a lock", state); GST_OBJECT_UNLOCK (task); g_warning ("task without a lock can't be set to state %d", state); return FALSE; } }
/** * gst_task_get_pool: * @task: a #GstTask * * Get the #GstTaskPool that this task will use for its streaming * threads. * * MT safe. * * Returns: the #GstTaskPool used by @task. gst_object_unref() * after usage. * * Since: 0.10.24 */ GstTaskPool * gst_task_get_pool (GstTask * task) { GstTaskPool *result; GstTaskPrivate *priv; g_return_val_if_fail (GST_IS_TASK (task), NULL); priv = task->priv; GST_OBJECT_LOCK (task); result = gst_object_ref (priv->pool); GST_OBJECT_UNLOCK (task); return result; }
/** * gst_task_set_priority: * @task: a #GstTask * @priority: a new priority for @task * * Changes the priority of @task to @priority. * * Note: try not to depend on task priorities. * * MT safe. * * Since: 0.10.24 */ void gst_task_set_priority (GstTask * task, GThreadPriority priority) { GstTaskPrivate *priv; GThread *thread; g_return_if_fail (GST_IS_TASK (task)); priv = task->priv; GST_OBJECT_LOCK (task); priv->prio_set = TRUE; priv->priority = priority; thread = task->abidata.ABI.thread; if (thread != NULL) { /* if this task already has a thread, we can configure the priority right * away, else we do that when we assign a thread to the task. */ g_thread_set_priority (thread, priority); } GST_OBJECT_UNLOCK (task); }
/** * gst_task_set_pool: * @task: a #GstTask * @pool: a #GstTaskPool * * Set @pool as the new GstTaskPool for @task. Any new streaming threads that * will be created by @task will now use @pool. * * MT safe. * * Since: 0.10.24 */ void gst_task_set_pool (GstTask * task, GstTaskPool * pool) { GstTaskPool *old; GstTaskPrivate *priv; g_return_if_fail (GST_IS_TASK (task)); g_return_if_fail (GST_IS_TASK_POOL (pool)); priv = task->priv; GST_OBJECT_LOCK (task); if (priv->pool != pool) { old = priv->pool; priv->pool = gst_object_ref (pool); } else old = NULL; GST_OBJECT_UNLOCK (task); if (old) gst_object_unref (old); }
EXPORT_C #endif gboolean gst_task_start (GstTask * task) { GstTaskState old; g_return_val_if_fail (GST_IS_TASK (task), FALSE); GST_DEBUG_OBJECT (task, "Starting task %p", task); GST_OBJECT_LOCK (task); if (G_UNLIKELY (GST_TASK_GET_LOCK (task) == NULL)) goto no_lock; old = task->state; task->state = GST_TASK_STARTED; switch (old) { case GST_TASK_STOPPED: { GstTaskClass *tclass; /* 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); break; } case GST_TASK_PAUSED: /* PAUSE to PLAY, signal */ GST_TASK_SIGNAL (task); break; case GST_TASK_STARTED: /* was OK */ break; } GST_OBJECT_UNLOCK (task); return TRUE; /* ERRORS */ no_lock: { GST_WARNING_OBJECT (task, "starting task without a lock"); GST_OBJECT_UNLOCK (task); g_warning ("starting task without a lock"); return FALSE; } }