mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
gst: add a promise object
An object that can be waited on and asked for asynchronous values. In much the same way as promise/futures in js/java/etc A callback can be installed for when the promise changes state. Original idea by Jan Schmidt <jan@centricular.com> With contributions from Nirbheek Chauhan <nirbheek@centricular.com> Mathieu Duponchelle <mathieu@centricular.com> https://bugzilla.gnome.org/show_bug.cgi?id=789843
This commit is contained in:
parent
40c5396c24
commit
86abf49c23
14 changed files with 1167 additions and 1 deletions
|
@ -96,6 +96,7 @@ Windows. It is released under the GNU Library General Public License
|
|||
<xi:include href="xml/gstpluginfeature.xml" />
|
||||
<xi:include href="xml/gstpoll.xml" />
|
||||
<xi:include href="xml/gstpreset.xml" />
|
||||
<xi:include href="xml/gstpromise.xml" />
|
||||
<xi:include href="xml/gstprotection.xml" />
|
||||
<xi:include href="xml/gstquery.xml" />
|
||||
<xi:include href="xml/gstregistry.xml" />
|
||||
|
|
|
@ -2677,6 +2677,29 @@ GstRegistryPrivate
|
|||
</SECTION>
|
||||
|
||||
|
||||
<SECTION>
|
||||
<FILE>gstpromise</FILE>
|
||||
<TITLE>GstPromise</TITLE>
|
||||
GstPromiseResult
|
||||
GstPromiseChangeFunc
|
||||
GstPromise
|
||||
gst_promise_new
|
||||
gst_promise_new_with_change_func
|
||||
gst_promise_ref
|
||||
gst_promise_unref
|
||||
gst_promise_wait
|
||||
gst_promise_reply
|
||||
gst_promise_interrupt
|
||||
gst_promise_expire
|
||||
<SUBSECTION Standard>
|
||||
GST_PROMISE
|
||||
GST_TYPE_PROMISE
|
||||
gst_promise_get_type
|
||||
GST_TYPE_PROMISE_RESULT
|
||||
gst_promise_result_get_type
|
||||
</SECTION>
|
||||
|
||||
|
||||
<SECTION>
|
||||
<FILE>gstsegment</FILE>
|
||||
<TITLE>GstSegment</TITLE>
|
||||
|
|
|
@ -26,6 +26,7 @@ gst_pad_template_get_type
|
|||
gst_pipeline_get_type
|
||||
gst_plugin_feature_get_type
|
||||
gst_preset_get_type
|
||||
gst_promise_get_type
|
||||
gst_registry_get_type
|
||||
gst_system_clock_get_type
|
||||
gst_tag_setter_get_type
|
||||
|
@ -48,4 +49,3 @@ gst_sample_get_type
|
|||
gst_tag_list_get_type
|
||||
gst_toc_get_type
|
||||
gst_toc_entry_get_type
|
||||
|
||||
|
|
|
@ -96,6 +96,7 @@ libgstreamer_@GST_API_VERSION@_la_SOURCES = \
|
|||
gstquery.c \
|
||||
gstregistry.c \
|
||||
gstregistrychunks.c \
|
||||
gstpromise.c \
|
||||
gstsample.c \
|
||||
gstsegment.c \
|
||||
gststreamcollection.c \
|
||||
|
@ -209,6 +210,7 @@ gst_headers = \
|
|||
gstpreset.h \
|
||||
gstprotection.h \
|
||||
gstquery.h \
|
||||
gstpromise.h \
|
||||
gstsample.h \
|
||||
gstsegment.h \
|
||||
gststreamcollection.h \
|
||||
|
|
|
@ -766,6 +766,7 @@ init_post (GOptionContext * context, GOptionGroup * group, gpointer data,
|
|||
g_type_class_ref (gst_stream_flags_get_type ());
|
||||
g_type_class_ref (gst_stream_type_get_type ());
|
||||
g_type_class_ref (gst_stack_trace_flags_get_type ());
|
||||
g_type_class_ref (gst_promise_result_get_type ());
|
||||
|
||||
_priv_gst_event_initialize ();
|
||||
_priv_gst_buffer_initialize ();
|
||||
|
@ -1221,6 +1222,7 @@ gst_deinit (void)
|
|||
g_type_class_unref (g_type_class_peek (gst_stream_flags_get_type ()));
|
||||
g_type_class_unref (g_type_class_peek (gst_debug_color_mode_get_type ()));
|
||||
g_type_class_unref (g_type_class_peek (gst_stack_trace_flags_get_type ()));
|
||||
g_type_class_unref (g_type_class_peek (gst_promise_result_get_type ()));
|
||||
|
||||
gst_deinitialized = TRUE;
|
||||
GST_INFO ("deinitialized GStreamer");
|
||||
|
|
|
@ -69,6 +69,7 @@
|
|||
#include <gst/gstprotection.h>
|
||||
#include <gst/gstquery.h>
|
||||
#include <gst/gstregistry.h>
|
||||
#include <gst/gstpromise.h>
|
||||
#include <gst/gstsample.h>
|
||||
#include <gst/gstsegment.h>
|
||||
#include <gst/gststreams.h>
|
||||
|
|
369
gst/gstpromise.c
Normal file
369
gst/gstpromise.c
Normal file
|
@ -0,0 +1,369 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gst_private.h"
|
||||
|
||||
#include "gstpromise.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_promise_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
/**
|
||||
* SECTION:gstpromise
|
||||
* @title: GstRespsone
|
||||
* @short_description: a miniobject for future/promise-like functionality
|
||||
* @see_also:
|
||||
*
|
||||
* The #GstPromise object implements the container for values that may
|
||||
* be available later. i.e. a Future or a Promise in
|
||||
* <ulink url="https://en.wikipedia.org/wiki/Futures_and_promises">https://en.wikipedia.org/wiki/Futures_and_promises</ulink>
|
||||
*
|
||||
* A #GstPromise can be created with gst_promise_new(), replied to
|
||||
* with gst_promise_reply(), interrupted with gst_promise_interrupt() and
|
||||
* expired with gst_promise_expire(). A callback can also be installed at
|
||||
* #GstPromise creation for result changes with gst_promise_new_with_change_func().
|
||||
* The change callback can be used to chain #GstPromises's together as in the
|
||||
* following example.
|
||||
* |[<!-- language="C" -->
|
||||
* const GstStructure *reply;
|
||||
* GstPromise *p;
|
||||
* if (gst_promise_wait (promise) != GST_PROMISE_RESULT_REPLIED)
|
||||
* return; // interrupted or expired value
|
||||
* reply = gst_promise_get_reply (promise);
|
||||
* if (error in reply)
|
||||
* return; // propagate error
|
||||
* p = gst_promise_new_with_change_func (another_promise_change_func, user_data, notify);
|
||||
* pass p to promise-using API
|
||||
* ]|
|
||||
*
|
||||
* Each #GstPromise starts out with a #GstPromiseResult of
|
||||
* %GST_PROMISE_RESULT_PENDING and only ever transitions out of that result
|
||||
* into one of the other #GstPromiseResult.
|
||||
*
|
||||
* In order to support multi-threaded code, gst_promise_reply(),
|
||||
* gst_promise_interrupt() and gst_promise_expire() may all be from
|
||||
* different threads with some restrictions, the final result of the promise
|
||||
* is whichever call is made first. There are two restrictions on ordering:
|
||||
*
|
||||
* 1. That gst_promise_reply() and gst_promise_interrupt() cannot be called
|
||||
* after gst_promise_expire()
|
||||
* 2. That gst_promise_reply() and gst_promise_interrupt()
|
||||
* cannot be called twice.
|
||||
*/
|
||||
|
||||
static const int immutable_structure_refcount = 2;
|
||||
|
||||
#define GST_PROMISE_REPLY(p) (((GstPromiseImpl *)(p))->reply)
|
||||
#define GST_PROMISE_RESULT(p) (((GstPromiseImpl *)(p))->result)
|
||||
#define GST_PROMISE_LOCK(p) (&(((GstPromiseImpl *)(p))->lock))
|
||||
#define GST_PROMISE_COND(p) (&(((GstPromiseImpl *)(p))->cond))
|
||||
#define GST_PROMISE_CHANGE_FUNC(p) (((GstPromiseImpl *)(p))->change_func)
|
||||
#define GST_PROMISE_CHANGE_DATA(p) (((GstPromiseImpl *)(p))->user_data)
|
||||
#define GST_PROMISE_CHANGE_NOTIFY(p) (((GstPromiseImpl *)(p))->notify)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstPromise promise;
|
||||
|
||||
GstPromiseResult result;
|
||||
GstStructure *reply;
|
||||
|
||||
GMutex lock;
|
||||
GCond cond;
|
||||
GstPromiseChangeFunc change_func;
|
||||
gpointer user_data;
|
||||
GDestroyNotify notify;
|
||||
} GstPromiseImpl;
|
||||
|
||||
/**
|
||||
* gst_promise_wait:
|
||||
* @promise: a #GstPromise
|
||||
*
|
||||
* Wait for @promise to move out of the %GST_PROMISE_RESULT_PENDING state.
|
||||
* If @promise is not in %GST_PROMISE_RESULT_PENDING then it will return
|
||||
* immediately with the current result.
|
||||
*
|
||||
* Returns: the result of the promise
|
||||
*/
|
||||
GstPromiseResult
|
||||
gst_promise_wait (GstPromise * promise)
|
||||
{
|
||||
GstPromiseResult ret;
|
||||
|
||||
g_return_val_if_fail (promise != NULL, GST_PROMISE_RESULT_EXPIRED);
|
||||
|
||||
g_mutex_lock (GST_PROMISE_LOCK (promise));
|
||||
ret = GST_PROMISE_RESULT (promise);
|
||||
|
||||
while (ret == GST_PROMISE_RESULT_PENDING) {
|
||||
GST_LOG ("%p waiting", promise);
|
||||
g_cond_wait (GST_PROMISE_COND (promise), GST_PROMISE_LOCK (promise));
|
||||
ret = GST_PROMISE_RESULT (promise);
|
||||
}
|
||||
GST_LOG ("%p waited", promise);
|
||||
|
||||
g_mutex_unlock (GST_PROMISE_LOCK (promise));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_promise_reply:
|
||||
* @promise: (allow-none): a #GstPromise
|
||||
* @s: (transfer full): a #GstStructure with the the reply contents
|
||||
*
|
||||
* Set a reply on @promise. This will wake up any waiters with
|
||||
* %GST_PROMISE_RESULT_REPLIED.
|
||||
*/
|
||||
void
|
||||
gst_promise_reply (GstPromise * promise, GstStructure * s)
|
||||
{
|
||||
GstPromiseChangeFunc change_func = NULL;
|
||||
gpointer change_data = NULL;
|
||||
|
||||
/* Caller requested that no reply is necessary */
|
||||
if (promise == NULL)
|
||||
return;
|
||||
|
||||
g_mutex_lock (GST_PROMISE_LOCK (promise));
|
||||
if (GST_PROMISE_RESULT (promise) != GST_PROMISE_RESULT_PENDING &&
|
||||
GST_PROMISE_RESULT (promise) != GST_PROMISE_RESULT_INTERRUPTED) {
|
||||
GstPromiseResult result = GST_PROMISE_RESULT (promise);
|
||||
g_mutex_unlock (GST_PROMISE_LOCK (promise));
|
||||
g_return_if_fail (result == GST_PROMISE_RESULT_PENDING ||
|
||||
result == GST_PROMISE_RESULT_INTERRUPTED);
|
||||
}
|
||||
|
||||
/* XXX: is this necessary and valid? */
|
||||
if (GST_PROMISE_REPLY (promise) && GST_PROMISE_REPLY (promise) != s)
|
||||
gst_structure_free (GST_PROMISE_REPLY (promise));
|
||||
|
||||
/* Only reply iff we are currently in pending */
|
||||
if (GST_PROMISE_RESULT (promise) == GST_PROMISE_RESULT_PENDING) {
|
||||
if (s
|
||||
&& !gst_structure_set_parent_refcount (s,
|
||||
(int *) &immutable_structure_refcount)) {
|
||||
g_critical ("Input structure has a parent already!");
|
||||
g_mutex_unlock (GST_PROMISE_LOCK (promise));
|
||||
return;
|
||||
}
|
||||
|
||||
GST_PROMISE_RESULT (promise) = GST_PROMISE_RESULT_REPLIED;
|
||||
GST_LOG ("%p replied", promise);
|
||||
|
||||
GST_PROMISE_REPLY (promise) = s;
|
||||
|
||||
change_func = GST_PROMISE_CHANGE_FUNC (promise);
|
||||
change_data = GST_PROMISE_CHANGE_DATA (promise);
|
||||
} else {
|
||||
/* eat the value */
|
||||
if (s)
|
||||
gst_structure_free (s);
|
||||
}
|
||||
|
||||
g_cond_broadcast (GST_PROMISE_COND (promise));
|
||||
g_mutex_unlock (GST_PROMISE_LOCK (promise));
|
||||
|
||||
if (change_func)
|
||||
change_func (promise, change_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_promise_get_reply:
|
||||
* @promise: a #GstPromise
|
||||
*
|
||||
* Retrieve the reply set on @promise. @promise must be in
|
||||
* %GST_PROMISE_RESULT_REPLIED and is owned by @promise
|
||||
*
|
||||
* Returns: (transfer none): The reply set on @promise
|
||||
*/
|
||||
const GstStructure *
|
||||
gst_promise_get_reply (GstPromise * promise)
|
||||
{
|
||||
g_return_val_if_fail (promise != NULL, NULL);
|
||||
|
||||
g_mutex_lock (GST_PROMISE_LOCK (promise));
|
||||
if (GST_PROMISE_RESULT (promise) != GST_PROMISE_RESULT_REPLIED) {
|
||||
GstPromiseResult result = GST_PROMISE_RESULT (promise);
|
||||
g_mutex_unlock (GST_PROMISE_LOCK (promise));
|
||||
g_return_val_if_fail (result == GST_PROMISE_RESULT_REPLIED, NULL);
|
||||
}
|
||||
|
||||
g_mutex_unlock (GST_PROMISE_LOCK (promise));
|
||||
|
||||
return GST_PROMISE_REPLY (promise);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_promise_interrupt:
|
||||
* @promise: a #GstPromise
|
||||
*
|
||||
* Interrupt waiting for a @promise. This will wake up any waiters with
|
||||
* %GST_PROMISE_RESULT_INTERRUPTED
|
||||
*/
|
||||
void
|
||||
gst_promise_interrupt (GstPromise * promise)
|
||||
{
|
||||
GstPromiseChangeFunc change_func = NULL;
|
||||
gpointer change_data = NULL;
|
||||
|
||||
g_return_if_fail (promise != NULL);
|
||||
|
||||
g_mutex_lock (GST_PROMISE_LOCK (promise));
|
||||
if (GST_PROMISE_RESULT (promise) != GST_PROMISE_RESULT_PENDING &&
|
||||
GST_PROMISE_RESULT (promise) != GST_PROMISE_RESULT_REPLIED) {
|
||||
GstPromiseResult result = GST_PROMISE_RESULT (promise);
|
||||
g_mutex_unlock (GST_PROMISE_LOCK (promise));
|
||||
g_return_if_fail (result == GST_PROMISE_RESULT_PENDING ||
|
||||
result == GST_PROMISE_RESULT_REPLIED);
|
||||
}
|
||||
/* only interrupt if we are currently in pending */
|
||||
if (GST_PROMISE_RESULT (promise) == GST_PROMISE_RESULT_PENDING) {
|
||||
GST_PROMISE_RESULT (promise) = GST_PROMISE_RESULT_INTERRUPTED;
|
||||
g_cond_broadcast (GST_PROMISE_COND (promise));
|
||||
GST_LOG ("%p interrupted", promise);
|
||||
|
||||
change_func = GST_PROMISE_CHANGE_FUNC (promise);
|
||||
change_data = GST_PROMISE_CHANGE_DATA (promise);
|
||||
}
|
||||
g_mutex_unlock (GST_PROMISE_LOCK (promise));
|
||||
|
||||
if (change_func)
|
||||
change_func (promise, change_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_promise_expire:
|
||||
* @promise: a #GstPromise
|
||||
*
|
||||
* Expire a @promise. This will wake up any waiters with
|
||||
* %GST_PROMISE_RESULT_EXPIRED
|
||||
*/
|
||||
void
|
||||
gst_promise_expire (GstPromise * promise)
|
||||
{
|
||||
GstPromiseChangeFunc change_func = NULL;
|
||||
gpointer change_data = NULL;
|
||||
|
||||
g_return_if_fail (promise != NULL);
|
||||
|
||||
g_mutex_lock (GST_PROMISE_LOCK (promise));
|
||||
if (GST_PROMISE_RESULT (promise) == GST_PROMISE_RESULT_PENDING) {
|
||||
GST_PROMISE_RESULT (promise) = GST_PROMISE_RESULT_EXPIRED;
|
||||
g_cond_broadcast (GST_PROMISE_COND (promise));
|
||||
GST_LOG ("%p expired", promise);
|
||||
|
||||
change_func = GST_PROMISE_CHANGE_FUNC (promise);
|
||||
change_data = GST_PROMISE_CHANGE_DATA (promise);
|
||||
GST_PROMISE_CHANGE_FUNC (promise) = NULL;
|
||||
GST_PROMISE_CHANGE_DATA (promise) = NULL;
|
||||
}
|
||||
g_mutex_unlock (GST_PROMISE_LOCK (promise));
|
||||
|
||||
if (change_func)
|
||||
change_func (promise, change_data);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_promise_free (GstMiniObject * object)
|
||||
{
|
||||
GstPromise *promise = (GstPromise *) object;
|
||||
|
||||
/* the promise *must* be dealt with in some way before destruction */
|
||||
g_warn_if_fail (GST_PROMISE_RESULT (promise) != GST_PROMISE_RESULT_PENDING);
|
||||
|
||||
if (GST_PROMISE_CHANGE_NOTIFY (promise))
|
||||
GST_PROMISE_CHANGE_NOTIFY (promise) (GST_PROMISE_CHANGE_DATA (promise));
|
||||
|
||||
if (GST_PROMISE_REPLY (promise)) {
|
||||
gst_structure_set_parent_refcount (GST_PROMISE_REPLY (promise), NULL);
|
||||
gst_structure_free (GST_PROMISE_REPLY (promise));
|
||||
}
|
||||
g_mutex_clear (GST_PROMISE_LOCK (promise));
|
||||
g_cond_clear (GST_PROMISE_COND (promise));
|
||||
GST_LOG ("%p finalized", promise);
|
||||
|
||||
g_free (promise);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_promise_init (GstPromise * promise)
|
||||
{
|
||||
static volatile gsize _init = 0;
|
||||
|
||||
if (g_once_init_enter (&_init)) {
|
||||
GST_DEBUG_CATEGORY_INIT (gst_promise_debug, "gstpromise", 0, "gstpromise");
|
||||
g_once_init_leave (&_init, 1);
|
||||
}
|
||||
|
||||
gst_mini_object_init (GST_MINI_OBJECT (promise), 0, GST_TYPE_PROMISE, NULL,
|
||||
NULL, gst_promise_free);
|
||||
|
||||
GST_PROMISE_REPLY (promise) = NULL;
|
||||
GST_PROMISE_RESULT (promise) = GST_PROMISE_RESULT_PENDING;
|
||||
g_mutex_init (GST_PROMISE_LOCK (promise));
|
||||
g_cond_init (GST_PROMISE_COND (promise));
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_promise_new:
|
||||
*
|
||||
* Returns: a new #GstPromise
|
||||
*/
|
||||
GstPromise *
|
||||
gst_promise_new (void)
|
||||
{
|
||||
GstPromise *promise = GST_PROMISE (g_new0 (GstPromiseImpl, 1));
|
||||
|
||||
gst_promise_init (promise);
|
||||
GST_LOG ("new promise %p", promise);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_promise_new_with_change_func:
|
||||
* @func: (scope notified): a #GstPromiseChangeFunc to call
|
||||
* @user_data: (closure): argument to call @func with
|
||||
* @notify: notification function that @user_data is no longer needed
|
||||
*
|
||||
* @func will be called exactly once when transitioning out of
|
||||
* %GST_PROMISE_RESULT_PENDING into any of the other #GstPromiseResult
|
||||
* states.
|
||||
*
|
||||
* Returns: a new #GstPromise
|
||||
*/
|
||||
GstPromise *
|
||||
gst_promise_new_with_change_func (GstPromiseChangeFunc func, gpointer user_data,
|
||||
GDestroyNotify notify)
|
||||
{
|
||||
GstPromise *promise = gst_promise_new ();
|
||||
|
||||
GST_PROMISE_CHANGE_FUNC (promise) = func;
|
||||
GST_PROMISE_CHANGE_DATA (promise) = user_data;
|
||||
GST_PROMISE_CHANGE_NOTIFY (promise) = notify;
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
GST_DEFINE_MINI_OBJECT_TYPE (GstPromise, gst_promise);
|
119
gst/gstpromise.h
Normal file
119
gst/gstpromise.h
Normal file
|
@ -0,0 +1,119 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_PROMISE_H__
|
||||
#define __GST_PROMISE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GST_EXPORT
|
||||
GType gst_promise_get_type(void);
|
||||
#define GST_TYPE_PROMISE (gst_promise_get_type())
|
||||
#define GST_PROMISE(obj) ((GstPromise *) obj)
|
||||
|
||||
typedef struct _GstPromise GstPromise;
|
||||
|
||||
/**
|
||||
* GstPromiseResult:
|
||||
* @GST_PROMISE_RESULT_PENDING: Initial state. Waiting for transition to any
|
||||
* other state.
|
||||
* @GST_PROMISE_RESULT_INTERRUPTED: Interrupted by the consumer as it doesn't
|
||||
* want the value anymore.
|
||||
* @GST_PROMISE_RESULT_REPLIED: A producer marked a reply
|
||||
* @GST_PROMISE_RESULT_EXPIRED: The promise expired (the carrying object
|
||||
* lost all refs) and the promise will never be fulfilled.
|
||||
*
|
||||
* The result of a #GstPromise
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GST_PROMISE_RESULT_PENDING,
|
||||
GST_PROMISE_RESULT_INTERRUPTED,
|
||||
GST_PROMISE_RESULT_REPLIED,
|
||||
GST_PROMISE_RESULT_EXPIRED,
|
||||
} GstPromiseResult;
|
||||
|
||||
/**
|
||||
* GstPromiseChangeFunc:
|
||||
* @promise: a #GstPromise
|
||||
* @user_data: (closure): user data
|
||||
*/
|
||||
typedef void (*GstPromiseChangeFunc) (GstPromise * promise, gpointer user_data);
|
||||
|
||||
/**
|
||||
* GstPromise:
|
||||
* @parent: parent #GstMiniObject
|
||||
*/
|
||||
struct _GstPromise
|
||||
{
|
||||
GstMiniObject parent;
|
||||
};
|
||||
|
||||
GST_EXPORT
|
||||
GstPromise * gst_promise_new (void);
|
||||
GST_EXPORT
|
||||
GstPromise * gst_promise_new_with_change_func (GstPromiseChangeFunc func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify);
|
||||
|
||||
GST_EXPORT
|
||||
GstPromiseResult gst_promise_wait (GstPromise * promise);
|
||||
GST_EXPORT
|
||||
void gst_promise_reply (GstPromise * promise,
|
||||
GstStructure * s);
|
||||
GST_EXPORT
|
||||
void gst_promise_interrupt (GstPromise * promise);
|
||||
GST_EXPORT
|
||||
void gst_promise_expire (GstPromise * promise);
|
||||
|
||||
GST_EXPORT
|
||||
const GstStructure * gst_promise_get_reply (GstPromise * promise);
|
||||
|
||||
/**
|
||||
* gst_promise_ref:
|
||||
* @promise: a #GstPromise.
|
||||
*
|
||||
* Increases the refcount of the given @promise by one.
|
||||
*
|
||||
* Returns: (transfer full): @promise
|
||||
*/
|
||||
static inline GstPromise *
|
||||
gst_promise_ref (GstPromise * promise)
|
||||
{
|
||||
return (GstPromise *) gst_mini_object_ref (GST_MINI_OBJECT_CAST (promise));
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_promise_unref:
|
||||
* @promise: (transfer full): a #GstPromise.
|
||||
*
|
||||
* Decreases the refcount of the promise. If the refcount reaches 0, the
|
||||
* promise will be freed.
|
||||
*/
|
||||
static inline void
|
||||
gst_promise_unref (GstPromise * promise)
|
||||
{
|
||||
gst_mini_object_unref (GST_MINI_OBJECT_CAST (promise));
|
||||
}
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_PROMISE_H__ */
|
|
@ -48,6 +48,7 @@ gst_sources = [
|
|||
'gstquery.c',
|
||||
'gstregistry.c',
|
||||
'gstregistrychunks.c',
|
||||
'gstpromise.c',
|
||||
'gstsample.c',
|
||||
'gstsegment.c',
|
||||
'gststreamcollection.c',
|
||||
|
@ -122,6 +123,7 @@ gst_headers = [
|
|||
'gstpreset.h',
|
||||
'gstprotection.h',
|
||||
'gstquery.h',
|
||||
'gstpromise.h',
|
||||
'gstsample.h',
|
||||
'gstsegment.h',
|
||||
'gststreamcollection.h',
|
||||
|
|
|
@ -133,6 +133,7 @@ check_PROGRAMS = \
|
|||
gst/gstpoll \
|
||||
gst/gstprotection \
|
||||
$(PRINTF_CHECKS) \
|
||||
gst/gstpromise \
|
||||
gst/gstsegment \
|
||||
gst/gstsystemclock \
|
||||
gst/gstclock \
|
||||
|
|
1
tests/check/gst/.gitignore
vendored
1
tests/check/gst/.gitignore
vendored
|
@ -35,6 +35,7 @@ gstplugin
|
|||
gstpoll
|
||||
gstpreset
|
||||
gstprintf
|
||||
gstpromise
|
||||
gstprotection
|
||||
gstregistry
|
||||
gstsegment
|
||||
|
|
636
tests/check/gst/gstpromise.c
Normal file
636
tests/check/gst/gstpromise.c
Normal file
|
@ -0,0 +1,636 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* unit test for GstPromise
|
||||
*
|
||||
* Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/check/gstcheck.h>
|
||||
|
||||
struct event_queue
|
||||
{
|
||||
GMutex lock;
|
||||
GCond cond;
|
||||
GThread *thread;
|
||||
GMainContext *main_context;
|
||||
GMainLoop *main_loop;
|
||||
gpointer user_data;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
_unlock_thread (GMutex * lock)
|
||||
{
|
||||
g_mutex_unlock (lock);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
_promise_thread (struct event_queue *q)
|
||||
{
|
||||
g_mutex_lock (&q->lock);
|
||||
q->main_context = g_main_context_new ();
|
||||
q->main_loop = g_main_loop_new (q->main_context, FALSE);
|
||||
|
||||
g_cond_broadcast (&q->cond);
|
||||
g_main_context_invoke (q->main_context, (GSourceFunc) _unlock_thread,
|
||||
&q->lock);
|
||||
|
||||
g_main_loop_run (q->main_loop);
|
||||
|
||||
g_mutex_lock (&q->lock);
|
||||
g_main_context_unref (q->main_context);
|
||||
q->main_context = NULL;
|
||||
g_main_loop_unref (q->main_loop);
|
||||
q->main_loop = NULL;
|
||||
g_cond_broadcast (&q->cond);
|
||||
g_mutex_unlock (&q->lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
event_queue_start (struct event_queue *q)
|
||||
{
|
||||
g_mutex_lock (&q->lock);
|
||||
q->thread = g_thread_new ("promise-thread", (GThreadFunc) _promise_thread, q);
|
||||
|
||||
while (!q->main_loop)
|
||||
g_cond_wait (&q->cond, &q->lock);
|
||||
g_mutex_unlock (&q->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
event_queue_stop (struct event_queue *q)
|
||||
{
|
||||
g_mutex_lock (&q->lock);
|
||||
if (q->main_loop)
|
||||
g_main_loop_quit (q->main_loop);
|
||||
g_mutex_unlock (&q->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
event_queue_stop_wait (struct event_queue *q)
|
||||
{
|
||||
g_mutex_lock (&q->lock);
|
||||
while (q->main_loop) {
|
||||
g_main_loop_quit (q->main_loop);
|
||||
g_cond_wait (&q->cond, &q->lock);
|
||||
}
|
||||
g_mutex_unlock (&q->lock);
|
||||
|
||||
g_thread_unref (q->thread);
|
||||
}
|
||||
|
||||
static struct event_queue *
|
||||
event_queue_new (void)
|
||||
{
|
||||
struct event_queue *q = g_new0 (struct event_queue, 1);
|
||||
|
||||
GST_LOG ("starting event queue %p", q);
|
||||
|
||||
g_mutex_init (&q->lock);
|
||||
g_cond_init (&q->cond);
|
||||
event_queue_start (q);
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
static void
|
||||
event_queue_free (struct event_queue *q)
|
||||
{
|
||||
event_queue_stop_wait (q);
|
||||
|
||||
g_mutex_clear (&q->lock);
|
||||
g_cond_clear (&q->cond);
|
||||
|
||||
GST_LOG ("stopped event queue %p", q);
|
||||
|
||||
g_free (q);
|
||||
}
|
||||
|
||||
static void
|
||||
_enqueue_task (struct event_queue *q, GSourceFunc func, gpointer data,
|
||||
GDestroyNotify notify)
|
||||
{
|
||||
GSource *source;
|
||||
|
||||
source = g_idle_source_new ();
|
||||
g_source_set_priority (source, G_PRIORITY_DEFAULT);
|
||||
g_source_set_callback (source, (GSourceFunc) func, data, notify);
|
||||
g_source_attach (source, q->main_context);
|
||||
g_source_unref (source);
|
||||
}
|
||||
|
||||
GST_START_TEST (test_reply)
|
||||
{
|
||||
GstPromise *r;
|
||||
|
||||
r = gst_promise_new ();
|
||||
|
||||
gst_promise_reply (r, NULL);
|
||||
fail_unless (gst_promise_wait (r) == GST_PROMISE_RESULT_REPLIED);
|
||||
|
||||
gst_promise_unref (r);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_reply_data)
|
||||
{
|
||||
GstPromise *r;
|
||||
GstStructure *s;
|
||||
const GstStructure *ret;
|
||||
|
||||
r = gst_promise_new ();
|
||||
|
||||
s = gst_structure_new ("promise", "test", G_TYPE_INT, 1, NULL);
|
||||
gst_promise_reply (r, s);
|
||||
fail_unless (gst_promise_wait (r) == GST_PROMISE_RESULT_REPLIED);
|
||||
ret = gst_promise_get_reply (r);
|
||||
fail_unless (gst_structure_is_equal (ret, s));
|
||||
|
||||
gst_promise_unref (r);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_reply_immutable)
|
||||
{
|
||||
GstPromise *r;
|
||||
GstStructure *s, *ret;
|
||||
|
||||
r = gst_promise_new ();
|
||||
|
||||
s = gst_structure_new ("promise", "test", G_TYPE_INT, 1, NULL);
|
||||
gst_promise_reply (r, s);
|
||||
ret = (GstStructure *) gst_promise_get_reply (r);
|
||||
|
||||
/* immutable result must not be able to modify the reply */
|
||||
ASSERT_CRITICAL (gst_structure_set (ret, "foo", G_TYPE_STRING, "bar", NULL));
|
||||
fail_unless (gst_structure_get_string (ret, "foo") == NULL);
|
||||
|
||||
gst_promise_unref (r);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_interrupt)
|
||||
{
|
||||
GstPromise *r;
|
||||
|
||||
r = gst_promise_new ();
|
||||
|
||||
gst_promise_interrupt (r);
|
||||
fail_unless (gst_promise_wait (r) == GST_PROMISE_RESULT_INTERRUPTED);
|
||||
|
||||
gst_promise_unref (r);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_expire)
|
||||
{
|
||||
GstPromise *r;
|
||||
|
||||
r = gst_promise_new ();
|
||||
|
||||
gst_promise_expire (r);
|
||||
fail_unless (gst_promise_wait (r) == GST_PROMISE_RESULT_EXPIRED);
|
||||
|
||||
gst_promise_unref (r);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
struct change_data
|
||||
{
|
||||
int change_count;
|
||||
GstPromiseResult result;
|
||||
};
|
||||
|
||||
static void
|
||||
on_change (GstPromise * promise, gpointer user_data)
|
||||
{
|
||||
struct change_data *res = user_data;
|
||||
|
||||
res->result = gst_promise_wait (promise);
|
||||
res->change_count += 1;
|
||||
}
|
||||
|
||||
GST_START_TEST (test_change_func)
|
||||
{
|
||||
GstPromise *r;
|
||||
struct change_data data = { 0, };
|
||||
|
||||
r = gst_promise_new_with_change_func (on_change, &data, NULL);
|
||||
gst_promise_reply (r, NULL);
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_REPLIED);
|
||||
fail_unless (data.change_count == 1);
|
||||
|
||||
gst_promise_unref (r);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_reply_expire)
|
||||
{
|
||||
GstPromise *r;
|
||||
struct change_data data = { 0, };
|
||||
|
||||
r = gst_promise_new_with_change_func (on_change, &data, NULL);
|
||||
gst_promise_reply (r, NULL);
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_REPLIED);
|
||||
fail_unless (data.change_count == 1);
|
||||
gst_promise_expire (r);
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_REPLIED);
|
||||
fail_unless (data.change_count == 1);
|
||||
|
||||
gst_promise_unref (r);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_reply_discard)
|
||||
{
|
||||
GstPromise *r;
|
||||
|
||||
/* NULL promise => discard reply */
|
||||
r = NULL;
|
||||
|
||||
/* no-op, we don't want a reply */
|
||||
gst_promise_reply (r, NULL);
|
||||
|
||||
if (r)
|
||||
gst_promise_unref (r);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_reply_interrupt)
|
||||
{
|
||||
GstPromise *r;
|
||||
struct change_data data = { 0, };
|
||||
|
||||
r = gst_promise_new_with_change_func (on_change, &data, NULL);
|
||||
gst_promise_reply (r, NULL);
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_REPLIED);
|
||||
fail_unless (data.change_count == 1);
|
||||
gst_promise_interrupt (r);
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_REPLIED);
|
||||
fail_unless (data.change_count == 1);
|
||||
|
||||
gst_promise_unref (r);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_reply_reply)
|
||||
{
|
||||
GstPromise *r;
|
||||
GstStructure *s;
|
||||
struct change_data data = { 0, };
|
||||
const GstStructure *ret;
|
||||
|
||||
r = gst_promise_new_with_change_func (on_change, &data, NULL);
|
||||
s = gst_structure_new ("promise", "test", G_TYPE_INT, 1, NULL);
|
||||
gst_promise_reply (r, s);
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_REPLIED);
|
||||
fail_unless (data.change_count == 1);
|
||||
ASSERT_CRITICAL (gst_promise_reply (r, NULL));
|
||||
fail_unless (gst_promise_wait (r) == GST_PROMISE_RESULT_REPLIED);
|
||||
ret = gst_promise_get_reply (r);
|
||||
fail_unless (gst_structure_is_equal (ret, s));
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_REPLIED);
|
||||
fail_unless (data.change_count == 1);
|
||||
|
||||
gst_promise_unref (r);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_interrupt_expire)
|
||||
{
|
||||
GstPromise *r;
|
||||
struct change_data data = { 0, };
|
||||
|
||||
r = gst_promise_new_with_change_func (on_change, &data, NULL);
|
||||
gst_promise_interrupt (r);
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_INTERRUPTED);
|
||||
fail_unless (data.change_count == 1);
|
||||
gst_promise_expire (r);
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_INTERRUPTED);
|
||||
fail_unless (data.change_count == 1);
|
||||
|
||||
gst_promise_unref (r);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_interrupt_reply)
|
||||
{
|
||||
GstPromise *r;
|
||||
struct change_data data = { 0, };
|
||||
|
||||
r = gst_promise_new_with_change_func (on_change, &data, NULL);
|
||||
gst_promise_interrupt (r);
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_INTERRUPTED);
|
||||
fail_unless (data.change_count == 1);
|
||||
gst_promise_reply (r, NULL);
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_INTERRUPTED);
|
||||
fail_unless (data.change_count == 1);
|
||||
|
||||
gst_promise_unref (r);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_interrupt_interrupt)
|
||||
{
|
||||
GstPromise *r;
|
||||
struct change_data data = { 0, };
|
||||
|
||||
r = gst_promise_new_with_change_func (on_change, &data, NULL);
|
||||
gst_promise_interrupt (r);
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_INTERRUPTED);
|
||||
fail_unless (data.change_count == 1);
|
||||
ASSERT_CRITICAL (gst_promise_interrupt (r));
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_INTERRUPTED);
|
||||
fail_unless (data.change_count == 1);
|
||||
|
||||
gst_promise_unref (r);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_expire_expire)
|
||||
{
|
||||
GstPromise *r;
|
||||
struct change_data data = { 0, };
|
||||
|
||||
r = gst_promise_new_with_change_func (on_change, &data, NULL);
|
||||
gst_promise_expire (r);
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_EXPIRED);
|
||||
fail_unless (data.change_count == 1);
|
||||
gst_promise_expire (r);
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_EXPIRED);
|
||||
fail_unless (data.change_count == 1);
|
||||
|
||||
gst_promise_unref (r);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_expire_interrupt)
|
||||
{
|
||||
GstPromise *r;
|
||||
struct change_data data = { 0, };
|
||||
|
||||
r = gst_promise_new_with_change_func (on_change, &data, NULL);
|
||||
gst_promise_expire (r);
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_EXPIRED);
|
||||
fail_unless (data.change_count == 1);
|
||||
ASSERT_CRITICAL (gst_promise_interrupt (r));
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_EXPIRED);
|
||||
fail_unless (data.change_count == 1);
|
||||
|
||||
gst_promise_unref (r);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_expire_reply)
|
||||
{
|
||||
GstPromise *r;
|
||||
struct change_data data = { 0, };
|
||||
|
||||
r = gst_promise_new_with_change_func (on_change, &data, NULL);
|
||||
gst_promise_expire (r);
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_EXPIRED);
|
||||
fail_unless (data.change_count == 1);
|
||||
ASSERT_CRITICAL (gst_promise_reply (r, NULL));
|
||||
fail_unless (data.result == GST_PROMISE_RESULT_EXPIRED);
|
||||
fail_unless (data.change_count == 1);
|
||||
|
||||
gst_promise_unref (r);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
struct stress_item
|
||||
{
|
||||
struct event_queue *q;
|
||||
GstPromise *promise;
|
||||
GstPromiseResult result;
|
||||
};
|
||||
|
||||
static void
|
||||
stress_reply (struct stress_item *item)
|
||||
{
|
||||
switch (item->result) {
|
||||
case GST_PROMISE_RESULT_REPLIED:
|
||||
gst_promise_reply (item->promise, NULL);
|
||||
break;
|
||||
case GST_PROMISE_RESULT_INTERRUPTED:
|
||||
gst_promise_interrupt (item->promise);
|
||||
break;
|
||||
case GST_PROMISE_RESULT_EXPIRED:
|
||||
gst_promise_expire (item->promise);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
struct stress_queues
|
||||
{
|
||||
GAsyncQueue *push_queue;
|
||||
GAsyncQueue *wait_queue;
|
||||
guint64 push_count;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
_push_random_promise (struct event_queue *q)
|
||||
{
|
||||
struct stress_queues *s_q = q->user_data;
|
||||
struct stress_item *item;
|
||||
|
||||
item = g_new0 (struct stress_item, 1);
|
||||
item->promise = gst_promise_new ();
|
||||
while (item->result == GST_PROMISE_RESULT_PENDING)
|
||||
item->result = g_random_int () % 4;
|
||||
|
||||
g_async_queue_push (s_q->wait_queue, item);
|
||||
g_async_queue_push (s_q->push_queue, item);
|
||||
|
||||
s_q->push_count++;
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_push_stop_promise (struct event_queue *q)
|
||||
{
|
||||
struct stress_queues *s_q = q->user_data;
|
||||
gpointer item = GINT_TO_POINTER (1);
|
||||
|
||||
g_async_queue_push (s_q->wait_queue, item);
|
||||
g_async_queue_push (s_q->push_queue, item);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_pop_promise (struct event_queue *q)
|
||||
{
|
||||
struct stress_queues *s_q = q->user_data;
|
||||
struct stress_item *item;
|
||||
|
||||
item = g_async_queue_pop (s_q->push_queue);
|
||||
|
||||
if (item == (void *) 1)
|
||||
return G_SOURCE_REMOVE;
|
||||
|
||||
stress_reply (item);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_wait_promise (struct event_queue *q)
|
||||
{
|
||||
struct stress_queues *s_q = q->user_data;
|
||||
struct stress_item *item;
|
||||
|
||||
item = g_async_queue_pop (s_q->wait_queue);
|
||||
|
||||
if (item == (void *) 1)
|
||||
return G_SOURCE_REMOVE;
|
||||
|
||||
fail_unless (gst_promise_wait (item->promise) == item->result);
|
||||
|
||||
gst_promise_unref (item->promise);
|
||||
g_free (item);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
GST_START_TEST (test_stress)
|
||||
{
|
||||
#define N_QUEUES 3
|
||||
struct event_queue *pushers[N_QUEUES];
|
||||
struct event_queue *poppers[N_QUEUES];
|
||||
struct event_queue *waiters[N_QUEUES];
|
||||
struct stress_queues s_q = { 0, };
|
||||
int i;
|
||||
|
||||
s_q.push_queue = g_async_queue_new ();
|
||||
s_q.wait_queue = g_async_queue_new ();
|
||||
|
||||
for (i = 0; i < N_QUEUES; i++) {
|
||||
pushers[i] = event_queue_new ();
|
||||
pushers[i]->user_data = &s_q;
|
||||
_enqueue_task (pushers[i], (GSourceFunc) _push_random_promise, pushers[i],
|
||||
NULL);
|
||||
waiters[i] = event_queue_new ();
|
||||
waiters[i]->user_data = &s_q;
|
||||
_enqueue_task (waiters[i], (GSourceFunc) _wait_promise, waiters[i], NULL);
|
||||
poppers[i] = event_queue_new ();
|
||||
poppers[i]->user_data = &s_q;
|
||||
_enqueue_task (poppers[i], (GSourceFunc) _pop_promise, poppers[i], NULL);
|
||||
}
|
||||
|
||||
GST_INFO ("all set up, waiting.");
|
||||
g_usleep (100000);
|
||||
GST_INFO ("wait done, cleaning up the test.");
|
||||
|
||||
{
|
||||
struct stress_item *item;
|
||||
int push_size;
|
||||
|
||||
for (i = 0; i < N_QUEUES; i++) {
|
||||
event_queue_stop (pushers[i]);
|
||||
event_queue_stop (poppers[i]);
|
||||
event_queue_stop (waiters[i]);
|
||||
_push_stop_promise (pushers[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < N_QUEUES; i++) {
|
||||
event_queue_free (pushers[i]);
|
||||
event_queue_free (poppers[i]);
|
||||
}
|
||||
|
||||
push_size = g_async_queue_length (s_q.push_queue);
|
||||
|
||||
/* push through all the promises so all the waits will complete */
|
||||
while ((item = g_async_queue_try_pop (s_q.push_queue))) {
|
||||
if (item == (void *) 1)
|
||||
continue;
|
||||
stress_reply (item);
|
||||
}
|
||||
|
||||
for (i = 0; i < N_QUEUES; i++)
|
||||
event_queue_free (waiters[i]);
|
||||
|
||||
GST_INFO ("pushed %" G_GUINT64_FORMAT ", %d leftover in push queue, "
|
||||
"%d leftover in wait queue", s_q.push_count, push_size,
|
||||
g_async_queue_length (s_q.wait_queue));
|
||||
|
||||
while ((item = g_async_queue_try_pop (s_q.wait_queue))) {
|
||||
if (item == (void *) 1)
|
||||
continue;
|
||||
|
||||
fail_unless (gst_promise_wait (item->promise) == item->result);
|
||||
|
||||
gst_promise_unref (item->promise);
|
||||
g_free (item);
|
||||
}
|
||||
}
|
||||
|
||||
g_async_queue_unref (s_q.push_queue);
|
||||
g_async_queue_unref (s_q.wait_queue);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
gst_promise_suite (void)
|
||||
{
|
||||
Suite *s = suite_create ("GstPromise");
|
||||
TCase *tc_chain = tcase_create ("general");
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
tcase_add_test (tc_chain, test_reply);
|
||||
tcase_add_test (tc_chain, test_reply_data);
|
||||
tcase_add_test (tc_chain, test_reply_immutable);
|
||||
tcase_add_test (tc_chain, test_interrupt);
|
||||
tcase_add_test (tc_chain, test_expire);
|
||||
tcase_add_test (tc_chain, test_change_func);
|
||||
tcase_add_test (tc_chain, test_reply_expire);
|
||||
tcase_add_test (tc_chain, test_reply_discard);
|
||||
tcase_add_test (tc_chain, test_reply_interrupt);
|
||||
tcase_add_test (tc_chain, test_reply_reply);
|
||||
tcase_add_test (tc_chain, test_interrupt_reply);
|
||||
tcase_add_test (tc_chain, test_interrupt_expire);
|
||||
tcase_add_test (tc_chain, test_interrupt_interrupt);
|
||||
tcase_add_test (tc_chain, test_expire_expire);
|
||||
tcase_add_test (tc_chain, test_expire_interrupt);
|
||||
tcase_add_test (tc_chain, test_expire_reply);
|
||||
tcase_add_test (tc_chain, test_stress);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
GST_CHECK_MAIN (gst_promise);
|
|
@ -38,6 +38,7 @@ core_tests = [
|
|||
[ 'gst/gstprotection.c' ],
|
||||
[ 'gst/gstquery.c', not have_registry ],
|
||||
[ 'gst/gstregistry.c', not have_registry ],
|
||||
[ 'gst/gstpromise.c'],
|
||||
[ 'gst/gstsegment.c' ],
|
||||
[ 'gst/gststream.c' ],
|
||||
[ 'gst/gststructure.c' ],
|
||||
|
|
|
@ -1091,6 +1091,14 @@ EXPORTS
|
|||
gst_printerrln
|
||||
gst_println
|
||||
gst_progress_type_get_type
|
||||
gst_promise_expire
|
||||
gst_promise_get_type
|
||||
gst_promise_interrupt
|
||||
gst_promise_new
|
||||
gst_promise_reply
|
||||
gst_promise_result_get_type
|
||||
gst_promise_set_change_callback
|
||||
gst_promise_wait
|
||||
gst_protection_filter_systems_by_available_decryptors
|
||||
gst_protection_meta_api_get_type
|
||||
gst_protection_meta_get_info
|
||||
|
|
Loading…
Reference in a new issue