/** * 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; } }
static GstFlowReturn gst_v4l2_video_dec_finish (GstVideoDecoder * decoder) { GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); GstFlowReturn ret = GST_FLOW_OK; GstBuffer *buffer; if (!g_atomic_int_get (&self->processing)) goto done; GST_DEBUG_OBJECT (self, "Finishing decoding"); GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); if (gst_v4l2_decoder_cmd (self->v4l2output, V4L2_DEC_CMD_STOP, 0)) { GstTask *task = decoder->srcpad->task; /* If the decoder stop command succeeded, just wait until processing is * finished */ GST_OBJECT_LOCK (task); while (GST_TASK_STATE (task) == GST_TASK_STARTED) GST_TASK_WAIT (task); GST_OBJECT_UNLOCK (task); ret = GST_FLOW_FLUSHING; } else { /* otherwise keep queuing empty buffers until the processing thread has * stopped, _pool_process() will return FLUSHING when that happened */ while (ret == GST_FLOW_OK) { buffer = gst_buffer_new (); ret = gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL (self-> v4l2output->pool), &buffer); gst_buffer_unref (buffer); } } /* and ensure the processing thread has stopped in case another error * occured. */ gst_v4l2_object_unlock (self->v4l2capture); gst_pad_stop_task (decoder->srcpad); GST_VIDEO_DECODER_STREAM_LOCK (decoder); if (ret == GST_FLOW_FLUSHING) ret = self->output_flow; GST_DEBUG_OBJECT (decoder, "Done draining buffers"); done: return ret; }
static void gst_task_func (GstTask * task, GstTaskClass * tclass) { GStaticRecMutex *lock; GThread *tself; tself = g_thread_self (); GST_DEBUG ("Entering task %p, thread %p", task, tself); /* we have to grab the lock to get the mutex. We also * mark our state running so that nobody can mess with * the mutex. */ GST_OBJECT_LOCK (task); if (task->state == GST_TASK_STOPPED) goto exit; lock = GST_TASK_GET_LOCK (task); if (G_UNLIKELY (lock == NULL)) goto no_lock; task->abidata.ABI.thread = tself; GST_OBJECT_UNLOCK (task); /* locking order is TASK_LOCK, LOCK */ g_static_rec_mutex_lock (lock); GST_OBJECT_LOCK (task); while (G_LIKELY (task->state != GST_TASK_STOPPED)) { while (G_UNLIKELY (task->state == GST_TASK_PAUSED)) { gint t; t = g_static_rec_mutex_unlock_full (lock); if (t <= 0) { g_warning ("wrong STREAM_LOCK count %d", t); } GST_TASK_SIGNAL (task); GST_TASK_WAIT (task); GST_OBJECT_UNLOCK (task); /* locking order.. */ if (t > 0) g_static_rec_mutex_lock_full (lock, t); GST_OBJECT_LOCK (task); if (G_UNLIKELY (task->state == GST_TASK_STOPPED)) goto done; } GST_OBJECT_UNLOCK (task); task->func (task->data); GST_OBJECT_LOCK (task); } done: GST_OBJECT_UNLOCK (task); g_static_rec_mutex_unlock (lock); GST_OBJECT_LOCK (task); 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); GST_DEBUG ("Exit task %p, thread %p", task, g_thread_self ()); gst_object_unref (task); return; no_lock: { g_warning ("starting task without a lock"); goto exit; } }
static void gst_task_func (GstTask * task) { GStaticRecMutex *lock; GThread *tself; GstTaskPrivate *priv; priv = task->priv; tself = g_thread_self (); GST_DEBUG ("Entering task %p, thread %p", task, tself); /* we have to grab the lock to get the mutex. We also * mark our state running so that nobody can mess with * the mutex. */ GST_OBJECT_LOCK (task); if (task->state == GST_TASK_STOPPED) goto exit; lock = GST_TASK_GET_LOCK (task); if (G_UNLIKELY (lock == NULL)) goto no_lock; task->abidata.ABI.thread = tself; /* only update the priority when it was changed */ if (priv->prio_set) g_thread_set_priority (tself, priv->priority); GST_OBJECT_UNLOCK (task); /* fire the enter_thread callback when we need to */ if (priv->thr_callbacks.enter_thread) priv->thr_callbacks.enter_thread (task, tself, priv->thr_user_data); /* locking order is TASK_LOCK, LOCK */ g_static_rec_mutex_lock (lock); GST_OBJECT_LOCK (task); /* configure the thread name now */ gst_task_configure_name (task); while (G_LIKELY (task->state != GST_TASK_STOPPED)) { while (G_UNLIKELY (task->state == GST_TASK_PAUSED)) { gint t; t = g_static_rec_mutex_unlock_full (lock); if (t <= 0) { g_warning ("wrong STREAM_LOCK count %d", t); } GST_TASK_SIGNAL (task); GST_TASK_WAIT (task); GST_OBJECT_UNLOCK (task); /* locking order.. */ if (t > 0) g_static_rec_mutex_lock_full (lock, t); GST_OBJECT_LOCK (task); if (G_UNLIKELY (task->state == GST_TASK_STOPPED)) goto done; } GST_OBJECT_UNLOCK (task); task->func (task->data); GST_OBJECT_LOCK (task); } done: GST_OBJECT_UNLOCK (task); g_static_rec_mutex_unlock (lock); GST_OBJECT_LOCK (task); task->abidata.ABI.thread = NULL; exit: if (priv->thr_callbacks.leave_thread) { /* fire the leave_thread callback when we need to. We need to do this before * we signal the task and with the task lock released. */ GST_OBJECT_UNLOCK (task); priv->thr_callbacks.leave_thread (task, tself, priv->thr_user_data); GST_OBJECT_LOCK (task); } else { /* restore normal priority when releasing back into the pool, we will not * touch the priority when a custom callback has been installed. */ g_thread_set_priority (tself, G_THREAD_PRIORITY_NORMAL); } /* 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); GST_DEBUG ("Exit task %p, thread %p", task, g_thread_self ()); gst_object_unref (task); return; no_lock: { g_warning ("starting task without a lock"); goto exit; } }
static void gst_hls_demux_loop (GstHLSDemux * demux) { GstBuffer *buf; GstFlowReturn ret; /* Loop for the source pad task. The task is started when we have * received the main playlist from the source element. It tries first to * cache the first fragments and then it waits until it has more data in the * queue. This task is woken up when we push a new fragment to the queue or * when we reached the end of the playlist */ if (G_UNLIKELY (demux->need_cache)) { if (!gst_hls_demux_cache_fragments (demux)) goto cache_error; /* we can start now the updates thread */ gst_hls_demux_start_update (demux); GST_INFO_OBJECT (demux, "First fragments cached successfully"); } if (g_queue_is_empty (demux->queue)) { if (demux->end_of_playlist) goto end_of_playlist; GST_TASK_WAIT (demux->task); /* If the queue is still empty check again if it's the end of the * playlist in case we reached it after beeing woken up */ if (g_queue_is_empty (demux->queue) && demux->end_of_playlist) goto end_of_playlist; } buf = g_queue_pop_head (demux->queue); ret = gst_pad_push (demux->srcpad, buf); if (ret != GST_FLOW_OK) goto error; return; end_of_playlist: { GST_DEBUG_OBJECT (demux, "Reached end of playlist, sending EOS"); gst_pad_push_event (demux->srcpad, gst_event_new_eos ()); gst_hls_demux_stop (demux); return; } cache_error: { GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND, ("Could not cache the first fragments"), NULL); gst_hls_demux_stop (demux); return; } error: { /* FIXME: handle error */ gst_hls_demux_stop (demux); return; } }