From e272ae281fdd854333bff88461d1090989eb21ce Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Fri, 31 Jan 2020 11:32:10 -0500 Subject: [PATCH] task: Introduce gst_task_resume() API This new API allow resuming a task if it was paused, while leaving it to stopped stated if it was stopped or not started yet. This new API can be useful for callback driver workflow, where you basically want to pause and resume the task when buffers are notified while avoiding the race with a gst_task_stop() coming from another thread. --- gst/gsttask.c | 81 ++++++++++++++++++++++++++++----------- gst/gsttask.h | 3 ++ tests/check/gst/gsttask.c | 43 +++++++++++++++++++++ 3 files changed, 104 insertions(+), 23 deletions(-) diff --git a/gst/gsttask.c b/gst/gsttask.c index 30f25fef8f..5e057a26eb 100644 --- a/gst/gsttask.c +++ b/gst/gsttask.c @@ -657,33 +657,14 @@ start_task (GstTask * task) return res; } - -/** - * 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. - */ -gboolean -gst_task_set_state (GstTask * task, GstTaskState state) +static inline gboolean +gst_task_set_state_unlocked (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; @@ -709,7 +690,6 @@ gst_task_set_state (GstTask * task, GstTaskState state) break; } } - GST_OBJECT_UNLOCK (task); return res; @@ -717,12 +697,41 @@ gst_task_set_state (GstTask * task, GstTaskState state) 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_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. + */ +gboolean +gst_task_set_state (GstTask * task, GstTaskState state) +{ + gboolean res = TRUE; + + g_return_val_if_fail (GST_IS_TASK (task), FALSE); + + GST_OBJECT_LOCK (task); + res = gst_task_set_state_unlocked (task, state); + GST_OBJECT_UNLOCK (task); + + return res; +} + /** * gst_task_start: * @task: The #GstTask to start @@ -777,6 +786,32 @@ gst_task_pause (GstTask * task) return gst_task_set_state (task, GST_TASK_PAUSED); } +/** + * gst_task_resume: + * @task: The #GstTask to resume + * + * Resume @task in case it was paused. If the task was stopped, it will + * remain in that state and this function will return %FALSE. + * + * Returns: %TRUE if the task could be resumed. + * + * MT safe. + * Since: 1.18 + */ +gboolean +gst_task_resume (GstTask * task) +{ + gboolean res = FALSE; + g_return_val_if_fail (GST_IS_TASK (task), FALSE); + + GST_OBJECT_LOCK (task); + if (GET_TASK_STATE (task) != GST_TASK_STOPPED) + res = gst_task_set_state_unlocked (task, GST_TASK_STARTED); + GST_OBJECT_UNLOCK (task); + + return res; +} + /** * gst_task_join: * @task: The #GstTask to join diff --git a/gst/gsttask.h b/gst/gsttask.h index 7f8c49e7d7..7ae1d43abb 100644 --- a/gst/gsttask.h +++ b/gst/gsttask.h @@ -207,6 +207,9 @@ gboolean gst_task_stop (GstTask *task); GST_API gboolean gst_task_pause (GstTask *task); +GST_API +gboolean gst_task_resume (GstTask *task); + GST_API gboolean gst_task_join (GstTask *task); diff --git a/tests/check/gst/gsttask.c b/tests/check/gst/gsttask.c index e18a5081f9..843d0e202f 100644 --- a/tests/check/gst/gsttask.c +++ b/tests/check/gst/gsttask.c @@ -31,6 +31,48 @@ static GRecMutex task_mutex; #define TEST_RACE_ITERATIONS 1000 +static void +task_resume_func (void *data) +{ + g_mutex_lock (&task_lock); + g_cond_signal (&task_cond); + g_mutex_unlock (&task_lock); +} + +GST_START_TEST (test_resume) +{ + GstTask *t; + + t = gst_task_new (task_resume_func, &t, NULL); + fail_if (t == NULL); + + g_rec_mutex_init (&task_mutex); + gst_task_set_lock (t, &task_mutex); + + g_cond_init (&task_cond); + g_mutex_init (&task_lock); + + g_mutex_lock (&task_lock); + + /* Pause the task, and resume it. */ + fail_unless (gst_task_pause (t)); + fail_unless (gst_task_resume (t)); + + while (GST_TASK_STATE (t) != GST_TASK_STARTED) + g_cond_wait (&task_cond, &task_lock); + + fail_unless (gst_task_stop (t)); + g_mutex_unlock (&task_lock); + fail_unless (gst_task_join (t)); + + /* Make sure we cannot resume from stopped. */ + fail_if (gst_task_resume (t)); + + gst_object_unref (t); +} + +GST_END_TEST; + static void task_signal_pause_func (void *data) { @@ -265,6 +307,7 @@ gst_task_suite (void) tcase_add_test (tc_chain, test_lock_start); tcase_add_test (tc_chain, test_join); tcase_add_test (tc_chain, test_pause_stop_race); + tcase_add_test (tc_chain, test_resume); return s; }