gstreamer/tests/check/gst/gstminiobject.c
Matthew Waters 3d887c7f07 gst: don't use volatile to mean atomic
volatile is not sufficient to provide atomic guarantees and real atomics
should be used instead.  GCC 11 has started warning about using volatile
with atomic operations.

https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1719

Discovered in https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/issues/868

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/775>
2021-03-19 04:15:19 +00:00

485 lines
11 KiB
C

/* GStreamer
*
* unit test for GstMiniObject
*
* Copyright (C) <2005> Thomas Vander Stichele <thomas at apestaart dot org>
* Copyright (C) <2005> Tim-Philipp Müller <tim centricular net>
*
* 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/check/gstcheck.h>
GST_START_TEST (test_copy)
{
GstBuffer *buffer, *copy;
buffer = gst_buffer_new_and_alloc (4);
copy = GST_BUFFER (gst_mini_object_copy (GST_MINI_OBJECT_CAST (buffer)));
fail_if (copy == NULL, "Copy of buffer returned NULL");
fail_unless (gst_buffer_get_size (copy) == 4,
"Copy of buffer has different size");
gst_buffer_unref (buffer);
gst_buffer_unref (copy);
}
GST_END_TEST;
GST_START_TEST (test_is_writable)
{
GstBuffer *buffer;
GstMiniObject *mobj;
buffer = gst_buffer_new_and_alloc (4);
mobj = GST_MINI_OBJECT_CAST (buffer);
fail_unless (gst_mini_object_is_writable (mobj),
"A buffer with one ref should be writable");
fail_if (gst_mini_object_ref (mobj) == NULL, "Could not ref the mobj");
fail_if (gst_mini_object_is_writable (mobj),
"A buffer with two refs should not be writable");
gst_buffer_unref (buffer);
gst_mini_object_unref (mobj);
}
GST_END_TEST;
GST_START_TEST (test_make_writable)
{
GstBuffer *buffer;
GstMiniObject *mobj, *mobj2, *mobj3;
buffer = gst_buffer_new_and_alloc (4);
mobj = GST_MINI_OBJECT_CAST (buffer);
mobj2 = gst_mini_object_make_writable (mobj);
fail_unless (GST_IS_BUFFER (mobj2), "make_writable did not return a buffer");
fail_unless (mobj == mobj2,
"make_writable returned a copy for a buffer with refcount 1");
mobj2 = gst_mini_object_ref (mobj);
mobj3 = gst_mini_object_make_writable (mobj);
fail_unless (GST_IS_BUFFER (mobj3), "make_writable did not return a buffer");
fail_if (mobj == mobj3,
"make_writable returned same object for a buffer with refcount > 1");
fail_unless (GST_MINI_OBJECT_REFCOUNT_VALUE (mobj) == 1,
"refcount of original mobj object should be back to 1");
mobj2 = gst_mini_object_make_writable (mobj);
fail_unless (GST_IS_BUFFER (mobj2), "make_writable did not return a buffer");
fail_unless (mobj == mobj2,
"make_writable returned a copy for a buffer with refcount 1");
gst_buffer_unref (buffer);
gst_mini_object_unref (mobj3);
}
GST_END_TEST;
static gint num_threads = 10;
static gint refs_per_thread = 10000;
/* test thread-safe refcounting of GstMiniObject */
static void
thread_ref (GstMiniObject * mobj)
{
int j;
THREAD_START ();
for (j = 0; j < refs_per_thread; ++j) {
gst_mini_object_ref (mobj);
if (j % num_threads == 0)
THREAD_SWITCH ();
}
GST_DEBUG ("thread stopped");
}
GST_START_TEST (test_ref_threaded)
{
GstBuffer *buffer;
GstMiniObject *mobj;
gint expected;
buffer = gst_buffer_new_and_alloc (4);
mobj = GST_MINI_OBJECT_CAST (buffer);
MAIN_START_THREADS (num_threads, thread_ref, mobj);
MAIN_STOP_THREADS ();
expected = num_threads * refs_per_thread + 1;
ASSERT_MINI_OBJECT_REFCOUNT (mobj, "miniobject", expected);
while (expected-- != 0)
gst_buffer_unref (buffer);
}
GST_END_TEST;
static void
thread_unref (GstMiniObject * mobj)
{
int j;
THREAD_START ();
for (j = 0; j < refs_per_thread; ++j) {
gst_mini_object_unref (mobj);
if (j % num_threads == 0)
THREAD_SWITCH ();
}
}
GST_START_TEST (test_unref_threaded)
{
GstBuffer *buffer;
GstMiniObject *mobj;
int i;
buffer = gst_buffer_new_and_alloc (4);
mobj = GST_MINI_OBJECT (buffer);
for (i = 0; i < num_threads * refs_per_thread; ++i)
gst_mini_object_ref (mobj);
MAIN_START_THREADS (num_threads, thread_unref, mobj);
MAIN_STOP_THREADS ();
ASSERT_MINI_OBJECT_REFCOUNT (mobj, "miniobject", 1);
/* final unref */
gst_mini_object_unref (mobj);
}
GST_END_TEST;
/* ======== weak ref test ======== */
static gboolean weak_ref_notify_succeeded = FALSE;
static void
on_weak_ref_notify (gpointer data, GstMiniObject * where_object_was)
{
weak_ref_notify_succeeded = TRUE;
}
GST_START_TEST (test_weak_ref)
{
GstBuffer *buffer;
buffer = gst_buffer_new_and_alloc (4);
gst_mini_object_weak_ref (GST_MINI_OBJECT (buffer), on_weak_ref_notify,
&buffer);
gst_buffer_unref (buffer);
fail_unless (weak_ref_notify_succeeded,
"No weak reference notification took place.");
}
GST_END_TEST;
#if 0
/* ======== recycle test ======== */
static gint recycle_buffer_count = 10;
typedef struct _MyBufferPool MyBufferPool;
struct _MyBufferPool
{
GSList *buffers;
gboolean is_closed;
};
static void my_recycle_buffer_destroy (MyRecycleBuffer * buf);
static MyBufferPool *
my_buffer_pool_new (void)
{
return g_new0 (MyBufferPool, 1);
}
static void
my_buffer_pool_free (MyBufferPool * self)
{
while (self->buffers != NULL) {
my_recycle_buffer_destroy (self->buffers->data);
self->buffers = g_slist_delete_link (self->buffers, self->buffers);
}
g_free (self);
}
static void
my_buffer_pool_add (MyBufferPool * self, GstBuffer * buf)
{
g_mutex_lock (mutex);
self->buffers = g_slist_prepend (self->buffers, gst_buffer_ref (buf));
g_mutex_unlock (mutex);
}
static GstBuffer *
my_buffer_pool_drain_one (MyBufferPool * self)
{
GstBuffer *buf = NULL;
g_mutex_lock (mutex);
if (self->buffers != NULL) {
buf = self->buffers->data;
self->buffers = g_slist_delete_link (self->buffers, self->buffers);
}
g_mutex_unlock (mutex);
return buf;
}
static void
my_recycle_buffer_finalize (GstMiniObject * mini_object)
{
GstBuffer *self = GST_BUFFER_CAST (mini_object);
if (self->pool != NULL) {
my_buffer_pool_add (self->pool, GST_BUFFER_CAST (self));
g_usleep (G_USEC_PER_SEC / 100);
} else {
GST_MINI_OBJECT_CLASS (my_recycle_buffer_parent_class)->finalize
(mini_object);
}
}
static GstBuffer *
my_recycle_buffer_new (MyBufferPool * pool)
{
GstBuffer *buf;
buf = gst_buffer_new ();
//buf->pool = pool;
return GST_BUFFER_CAST (buf);
}
static void
my_recycle_buffer_destroy (MyRecycleBuffer * buf)
{
buf->pool = NULL;
gst_buffer_unref (GST_BUFFER_CAST (buf));
}
static void
thread_buffer_producer (MyBufferPool * pool)
{
int j;
THREAD_START ();
for (j = 0; j < recycle_buffer_count; ++j) {
GstBuffer *buf = my_recycle_buffer_new (pool);
gst_buffer_unref (buf);
}
g_atomic_int_set (&pool->is_closed, TRUE);
}
static void
thread_buffer_consumer (MyBufferPool * pool)
{
THREAD_START ();
do {
GstBuffer *buf;
buf = my_buffer_pool_drain_one (pool);
if (buf != NULL)
my_recycle_buffer_destroy (MY_RECYCLE_BUFFER_CAST (buf));
THREAD_SWITCH ();
}
while (!g_atomic_int_get (&pool->is_closed));
}
GST_START_TEST (test_recycle_threaded)
{
MyBufferPool *pool;
pool = my_buffer_pool_new ();
MAIN_START_THREADS (1, thread_buffer_producer, pool);
MAIN_START_THREADS (1, thread_buffer_consumer, pool);
MAIN_STOP_THREADS ();
my_buffer_pool_free (pool);
}
GST_END_TEST;
#endif
/* ======== value collection test ======== */
typedef struct _MyFoo
{
GObject object;
} MyFoo;
typedef struct _MyFooClass
{
GObjectClass gobject_class;
} MyFooClass;
enum
{
PROP_BUFFER = 1
};
GType my_foo_get_type (void);
G_DEFINE_TYPE (MyFoo, my_foo, G_TYPE_OBJECT);
static void
my_foo_init (MyFoo * foo)
{
}
static void
my_foo_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstBuffer *new_buf;
g_assert (prop_id == PROP_BUFFER);
new_buf = gst_buffer_new_and_alloc (1024);
g_value_set_boxed (value, GST_MINI_OBJECT (new_buf));
gst_buffer_unref (new_buf);
}
static void
my_foo_set_property (GObject * object, guint prop_id, const GValue * value,
GParamSpec * pspec)
{
GstMiniObject *mini_obj;
g_assert (prop_id == PROP_BUFFER);
mini_obj = g_value_get_boxed (value);
g_assert (GST_IS_BUFFER (mini_obj));
#if 0
/* gst_value_dup_mini_object() does not exist yet */
mini_obj = gst_value_dup_mini_object (value);
g_assert (GST_IS_MINI_OBJECT (mini_obj));
g_assert (GST_IS_BUFFER (mini_obj));
gst_mini_object_unref (mini_obj);
#endif
}
static void
my_foo_class_init (MyFooClass * klass)
{
GObjectClass *gobject_klass = G_OBJECT_CLASS (klass);
gobject_klass->get_property = my_foo_get_property;
gobject_klass->set_property = my_foo_set_property;
g_object_class_install_property (gobject_klass, PROP_BUFFER,
g_param_spec_boxed ("buffer", "Buffer",
"a newly created GstBuffer", GST_TYPE_BUFFER, G_PARAM_READWRITE));
}
GST_START_TEST (test_value_collection)
{
GstBuffer *buf = NULL;
MyFoo *foo;
foo = (MyFoo *) g_object_new (my_foo_get_type (), NULL);
/* test g_object_get() refcounting */
g_object_get (foo, "buffer", &buf, NULL);
g_assert (GST_IS_BUFFER (buf));
g_assert (GST_MINI_OBJECT_REFCOUNT_VALUE (GST_MINI_OBJECT_CAST (buf)) == 1);
gst_buffer_unref (buf);
/* test g_object_set() refcounting */
buf = gst_buffer_new_and_alloc (1024);
g_object_set (foo, "buffer", buf, NULL);
g_assert (GST_MINI_OBJECT_REFCOUNT_VALUE (GST_MINI_OBJECT_CAST (buf)) == 1);
gst_buffer_unref (buf);
g_object_unref (foo);
}
GST_END_TEST;
GST_START_TEST (test_dup_null_mini_object)
{
GValue value = { 0, };
GstMiniObject *mo;
g_value_init (&value, GST_TYPE_BUFFER);
g_value_set_boxed (&value, NULL);
mo = GST_MINI_OBJECT_CAST (g_value_dup_boxed (&value));
g_assert (mo == NULL);
g_value_unset (&value);
}
GST_END_TEST;
static Suite *
gst_mini_object_suite (void)
{
Suite *s = suite_create ("GstMiniObject");
TCase *tc_chain = tcase_create ("general");
/* turn off timeout */
tcase_set_timeout (tc_chain, 60);
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_copy);
tcase_add_test (tc_chain, test_is_writable);
tcase_add_test (tc_chain, test_make_writable);
tcase_add_test (tc_chain, test_ref_threaded);
tcase_add_test (tc_chain, test_unref_threaded);
tcase_add_test (tc_chain, test_weak_ref);
//tcase_add_test (tc_chain, test_recycle_threaded);
tcase_add_test (tc_chain, test_value_collection);
tcase_add_test (tc_chain, test_dup_null_mini_object);
return s;
}
GST_CHECK_MAIN (gst_mini_object);