Commit c47dc4d8 authored by Wim Taymans's avatar Wim Taymans

First THREADED backport attempt, focusing on adding locks and making sure the...

First THREADED backport attempt, focusing on adding locks and making sure the API is threadsafe. Needs more work. Mor...

Original commit message from CVS:
First THREADED backport attempt, focusing on adding locks and
making sure the API is threadsafe. Needs more work. More docs
follow this week.
parent d5e9b91e
This diff is collapsed.
......@@ -8,13 +8,23 @@ SUBDIRS_DOCS =
endif
if BUILD_TESTS
SUBDIRS_TESTS = tests testsuite
## SUBDIRS_TESTS = tests testsuite
## FIXME: write tests from scratch
SUBDIRS_TESTS =
if HAVE_CHECK
SUBDIRS_CHECK = check
else
SUBDIRS_CHECK =
endif
else
SUBDIRS_TESTS =
SUBDIRS_CHECK =
endif
if BUILD_EXAMPLES
SUBDIRS_EXAMPLES = examples
## FIXME: write examples from scratch
# SUBDIRS_EXAMPLES = examples
SUBDIRS_EXAMPLES =
else
SUBDIRS_EXAMPLES =
endif
......@@ -31,7 +41,9 @@ aclocal_DATA = gst-element-check-@GST_MAJORMINOR@.m4
SUBDIRS = \
include gst libs tools \
$(SUBDIRS_TESTS) $(SUBDIRS_EXAMPLES) \
$(SUBDIRS_CHECK) \
$(SUBDIRS_TESTS) \
$(SUBDIRS_EXAMPLES) \
pkgconfig po \
common \
$(SUBDIRS_DOCS)
......@@ -40,6 +52,7 @@ SUBDIRS = \
DIST_SUBDIRS = \
include libs gst \
tools \
check \
tests testsuite \
examples \
pkgconfig \
......
......@@ -24,7 +24,7 @@ dnl - library source changed -> increment REVISION
dnl - interfaces added/removed/changed -> increment CURRENT, REVISION = 0
dnl - interfaces added -> increment AGE
dnl - interfaces removed -> AGE = 0
AS_LIBTOOL(GST, 5, 0, 4)
AS_LIBTOOL(GST, 6, 0, 0)
AM_PROG_LIBTOOL
AC_CONFIG_SRCDIR([gst/gst.c])
......@@ -314,6 +314,10 @@ AC_SUBST(POPT_LIBS)
dnl Check for ucontext.h
AC_CHECK_HEADER(ucontext.h, AC_DEFINE(HAVE_UCONTEXT_H, 1, [defined if we have ucontext.h]))
dnl check for "check", unit testing library/header
AM_PATH_CHECK(0.9.2, HAVE_CHECK=yes, HAVE_CHECK=no)
AM_CONDITIONAL(HAVE_CHECK, test "x$HAVE_CHECK" = "xyes")
dnl ######################################################################
dnl # Check command line parameters, and set shell variables accordingly #
dnl ######################################################################
......@@ -664,6 +668,7 @@ libs/gst/control/Makefile
libs/gst/dataprotocol/Makefile
libs/gst/getbits/Makefile
po/Makefile.in
check/Makefile
tests/Makefile
tests/bufspeed/Makefile
tests/instantiate/Makefile
......
This diff is collapsed.
......@@ -16,7 +16,7 @@ Function names
--------------
Within the context of a given object, functions defined in that object's header and/or source file will have their
object-specific prefix stripped. For instance, gst_element_add_pad() would be referred to as simply _get_pad(). Note
object-specific prefix stripped. For instance, gst_element_add_pad() would be referred to as simply _add_pad(). Note
that the trailing parentheses should always be present, but sometimes may not be. A prefixing underscore (_) will
always tell you it's a function, however, regardless of the presence or absence of the trailing parentheses.
......@@ -25,7 +25,59 @@ always tell you it's a function, however, regardless of the presence or absence
Values and macros defined as enums and preprocessor macros will be referred to in all capitals, as per their
definition. This includes object flags and element states, as well as general enums. Examples are the states NULL,
READY, PLAYING, and PAUSED; the element flags DECOUPLED and USE_COTHREAD, and state return values SUCCESS, FAILURE, and
READY, PLAYING, and PAUSED; the element flags LOCKED_STATE , and state return values SUCCESS, FAILURE, and
ASYNC. Where there is a prefix, as in the element flags, this is usually dropped, and implied. Not however that
element flags should be cross-checked with the header, as there are currently two conventions in use: with and without
_FLAGS_ in the middle.
FIXME: check flags for consistency.
Drawing conventions
===================
When drawing pictures the folowing conventions apply:
objects
-------
Objects are drawn with a box like
+------+
| |
+------+
pointers
--------
a pointer to an object.
+-----+
*--->| |
+-----+
an invalid pointer, this is a pointer that should not be used.
*-//->
elements
--------
+----------+
| name |
sink src
+----------+
pad links
---------
-----+ +---
| |
src--sink
-----+ +---
GstObject
=========
The base class for the entire GStreamer hierarchy is the GstObject. It is currently a bit of a hack caused by the
fact that GStreamer is built on top of GtkObject. GObject should help, if it's designed properly. Currently the
capabilities provided by GstObject are quite underutilized, this will be fixed with a refactoring of the
object system and a code cleanup at some point in the future. If nothing else, it serves as an easy check to see if a
given object belongs to GStreamer.
The base class for the entire GStreamer hierarchy is the GstObject.
Parentage
---------
A pointer is available to store the current parent of the object. This one of the two fundamental requires for a
hierarchical system such as GStreamer (for the other, read up on GstBin). Three functions are provided: _set_parent(),
_get_parent(), and _unparent(). The third is required because there is an explicit check in _set_parent(): an object
must not already have a parent if you wish to set one. You must unparent the object first. This allows for new
additions later.
A pointer is available to store the current parent of the object. This is one
of the two fundamental requires for a hierarchical system such as GStreamer
(for the other, read up on GstBin). Three functions are provided:
_set_parent(), _get_parent(), and _unparent(). The third is required because
there is an explicit check in _set_parent(): an object must not already have a
parent if you wish to set one. You must unparent the object first. This
allows for new additions later.
- GstObject's that can be parented:
GstElement (inside a bin)
GstPad (inside an element)
Refcounting
-----------
- GObject refcount is not threadsafe.
GStreamer sets it to a constant value on each _ref/_unref()
and uses an atomic int "refcount" instead for threadsafe refcounting
This implies you should always use gst_object_ref() and gst_object_unref() !
Naming
------
- names of objects cannot be changed when they are parented
- names of objects should be unique across parent
- set_name() can fail because of this
- as can gst_element_add_pad()/gst_bin_add_element()
- gst_object_set_name() only changes the object's name
A reference count is kept for the object. When this is fully utilized, it will enable generic destruction of objects
by simply reducing the reference count to zero. GObject should provide these capabilities, however.
- objects also have a name_prefix that is used to prefix the object name
during debugging and identification
- there are object-specific set_name's() which also set the name_prefix
on the object. This is useful for debugging purposes to give the object
a more identifiable name. Typically a parent will call _set_name_prefix
on children, taking a lock on them to do so.
Locking
-------
The GstObject contains the necessary primitives to lock the object in a thread-safe manner. This will be used to
provide general thread-safety as needed. However, this lock is generic, i.e. it covers the whole object. Note that
this does *not* mean that no other thread can modify the object at the same time that the lock is held. It only means
that any two sections of code that obey the lock are guaranteed to not be running simultaneously.
The GstObject contains the necessary primitives to lock the object in a
thread-safe manner. This will be used to provide general thread-safety as
needed. However, this lock is generic, i.e. it covers the whole object.
All members of the GstObject structure marked as
/*< public >*/ /* with LOCK */
are protected by this lock. These members can only be accessed for reading
or writing while the lock is held.
Note that this does *not* mean that no other thread can modify the object at
the same time that the lock is held. It only means that any two sections of
code that obey the lock are guaranteed to not be running simultaneously. "The
lock is voluntary and cooperative".
This lock will ideally be used for parentage and refcounting, which is reasonable, since they are the only possible
things to protect in the GstObject.
This lock will ideally be used for parentage and refcounting, which is
reasonable, since they are the only possible things to protect in the
GstObject.
Path Generation
---------------
FIXME: rethink this ?
Due to the base nature of the GstObject, it becomes the only reasonable place to put this particular function
(_get_path_string). It will generate a string describing the parent hierarchy of a given GstObject. Currently it is
forced to use several child-class-specific functions, because we do not properly use the base capabilities (parentage,
etc.) of GstObject properly.
Due to the base nature of the GstObject, it becomes the only reasonable place
to put this particular function (_get_path_string). It will generate a string
describing the parent hierarchy of a given GstObject. Currently it is forced
to use several child-class-specific functions, because we do not properly use
the base capabilities (parentage, etc.) of GstObject properly.
This diff is collapsed.
......@@ -187,7 +187,7 @@ main (int argc, char *argv[])
gst_element_set_state (main_bin, GST_STATE_PLAYING);
/* write out the schedule */
gst_scheduler_show (GST_ELEMENT_SCHED (main_bin));
gst_scheduler_show (GST_ELEMENT_SCHEDULER (main_bin));
playing = TRUE;
j = 0;
......
#include <stdlib.h>
#include <gst/gst.h>
static GMainLoop *loop;
/* eos will be called when the src element has an end of stream */
void
eos (GstElement * element, gpointer data)
......@@ -12,7 +14,8 @@ eos (GstElement * element, gpointer data)
/* stop the bin */
gst_element_set_state (GST_ELEMENT (thread), GST_STATE_NULL);
gst_main_quit ();
g_main_loop_quit (loop);
g_main_loop_unref (loop);
}
int
......@@ -68,7 +71,7 @@ main (int argc, char *argv[])
/* start playing */
gst_element_set_state (GST_ELEMENT (thread), GST_STATE_PLAYING);
gst_main ();
loop = g_main_loop_new (NULL, FALSE);
gst_object_unref (GST_OBJECT (thread));
......
......@@ -66,7 +66,7 @@ else
GST_URI_SRC = gsturi.c
endif
SUBDIRS = $(GST_PARSE_DIRS) $(GST_REGISTRY_DIRS) . autoplug elements schedulers $(GST_INDEX_DIRS)
SUBDIRS = $(GST_PARSE_DIRS) $(GST_REGISTRY_DIRS) . elements schedulers $(GST_INDEX_DIRS)
DIST_SUBDIRS = autoplug elements parse registries schedulers indexers
# make variables for all generated source and header files to make the
......@@ -98,6 +98,7 @@ libgstreamer_@GST_MAJORMINOR@_la_SOURCES = \
$(GST_INDEX_SRC) \
gstinfo.c \
gstinterface.c \
gstiterator.c \
gstmemchunk.c \
gstpad.c \
gstpipeline.c \
......@@ -171,6 +172,7 @@ gst_headers = \
gstindex.h \
gstinfo.h \
gstinterface.h \
gstiterator.h \
gstmacros.h \
gstmemchunk.h \
gstpad.h \
......
......@@ -65,12 +65,12 @@ gst_autoplug_caps_intersect (const GstCaps * src, const GstCaps * sink)
/* if the caps can't link, there is no intersection */
if (gst_caps_is_empty (caps)) {
gst_caps_free (caps);
gst_caps_unref (caps);
return FALSE;
}
/* hurrah, we can link, now remove the intersection */
gst_caps_free (caps);
gst_caps_unref (caps);
return TRUE;
}
......
......@@ -464,11 +464,11 @@ gst_spider_identity_plug (GstSpiderIdentity * ident)
GST_ELEMENT_ERROR (spider, STREAM, CODEC_NOT_FOUND,
(_("There is no element present to handle the stream's mime type %s."), mime), (NULL));
gst_caps_free (src_caps);
gst_caps_unref (src_caps);
return;
}
}
gst_caps_free (src_caps);
gst_caps_unref (src_caps);
}
/* get the direction of our ident */
......@@ -491,7 +491,7 @@ gst_spider_identity_plug (GstSpiderIdentity * ident)
}
/* now iterate all possible pads and link when needed */
padlist = gst_element_get_pad_list (GST_ELEMENT (spider));
padlist = GST_ELEMENT (spider)->pads;
for (; padlist; padlist = padlist->next) {
GstPad *otherpad;
GstSpiderIdentity *peer;
......@@ -697,8 +697,8 @@ gst_spider_plug_from_srcpad (GstSpiderConnection * conn, GstPad * srcpad)
caps1 = gst_pad_get_caps (srcpad);
caps2 = gst_pad_get_caps (conn->src->sink);
plugpath = gst_autoplug_sp (caps1, caps2, spider->factories);
gst_caps_free (caps1);
gst_caps_free (caps2);
gst_caps_unref (caps1);
gst_caps_unref (caps2);
/* prints out the path that was found for plugging */
/* g_print ("found path from %s to %s:\n", GST_ELEMENT_NAME (conn->current), GST_ELEMENT_NAME (conn->src));
......
......@@ -289,7 +289,7 @@ gst_spider_identity_getcaps (GstPad * pad)
if (ident->caps) {
GstCaps *ret2 = gst_caps_intersect (ident->caps, ret);
gst_caps_free (ret);
gst_caps_unref (ret);
ret = ret2;
}
return ret;
......@@ -388,12 +388,12 @@ gst_spider_identity_change_state (GstElement * element)
gst_pad_get_caps ((GstPad *) GST_PAD_PEER (ident->sink));
if (gst_caps_is_any (caps) || gst_caps_is_empty (caps)) {
gst_spider_identity_start_type_finding (ident);
gst_caps_free (caps);
gst_caps_unref (caps);
break;
} else {
gst_spider_identity_plug (ident);
}
gst_caps_free (caps);
gst_caps_unref (caps);
}
/* autoplug on src */
if ((GST_RPAD_PEER (ident->src) != NULL)
......@@ -528,7 +528,7 @@ gst_spider_identity_sink_loop_type_finding (GstSpiderIdentity * ident)
if (!gst_caps_is_empty (find.caps) && !gst_caps_is_any (find.caps)) {
goto plug;
} else {
gst_caps_free (find.caps);
gst_caps_unref (find.caps);
find.caps = NULL;
}
......
......@@ -887,7 +887,7 @@ gst_fakesrc_loop (GstElement * element)
src = GST_FAKESRC (element);
pads = gst_element_get_pad_list (element);
pads = element->pads;
while (pads) {
GstPad *pad = GST_PAD (pads->data);
......
......@@ -203,14 +203,14 @@ gst_identity_init (GstIdentity * identity)
gst_element_add_pad (GST_ELEMENT (identity), identity->sinkpad);
gst_pad_set_chain_function (identity->sinkpad,
GST_DEBUG_FUNCPTR (gst_identity_chain));
gst_pad_set_link_function (identity->sinkpad, gst_pad_proxy_pad_link);
//gst_pad_set_link_function (identity->sinkpad, gst_pad_proxy_pad_link);
gst_pad_set_getcaps_function (identity->sinkpad, gst_pad_proxy_getcaps);
identity->srcpad =
gst_pad_new_from_template (gst_static_pad_template_get (&srctemplate),
"src");
gst_element_add_pad (GST_ELEMENT (identity), identity->srcpad);
gst_pad_set_link_function (identity->srcpad, gst_pad_proxy_pad_link);
//gst_pad_set_link_function (identity->srcpad, gst_pad_proxy_pad_link);
gst_pad_set_getcaps_function (identity->srcpad, gst_pad_proxy_getcaps);
identity->loop_based = DEFAULT_LOOP_BASED;
......
......@@ -141,8 +141,7 @@ gst_tee_init (GstTee * tee)
"sink");
gst_element_add_pad (GST_ELEMENT (tee), tee->sinkpad);
gst_pad_set_chain_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_tee_chain));
gst_pad_set_link_function (tee->sinkpad,
GST_DEBUG_FUNCPTR (gst_pad_proxy_pad_link));
//gst_pad_set_link_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_pad_proxy_pad_link));
gst_pad_set_getcaps_function (tee->sinkpad,
GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
......@@ -169,16 +168,15 @@ gst_tee_getcaps (GstPad * _pad)
GstPad *pad;
const GList *pads;
for (pads = gst_element_get_pad_list (GST_ELEMENT (tee));
pads != NULL; pads = pads->next) {
for (pads = GST_ELEMENT (tee)->pads; pads != NULL; pads = pads->next) {
pad = GST_PAD (pads->data);
if (pad == _pad)
continue;
tmp = gst_pad_get_allowed_caps (pad);
res = gst_caps_intersect (caps, tmp);
gst_caps_free (tmp);
gst_caps_free (caps);
gst_caps_unref (tmp);
gst_caps_unref (caps);
caps = res;
}
......@@ -195,8 +193,7 @@ gst_tee_link (GstPad * _pad, const GstCaps * caps)
GST_DEBUG_OBJECT (tee, "Forwarding link to all other pads");
for (pads = gst_element_get_pad_list (GST_ELEMENT (tee));
pads != NULL; pads = pads->next) {
for (pads = GST_ELEMENT (tee)->pads; pads != NULL; pads = pads->next) {
pad = GST_PAD (pads->data);
if (pad == _pad)
continue;
......@@ -231,7 +228,7 @@ gst_tee_request_new_pad (GstElement * element, GstPadTemplate * templ,
tee = GST_TEE (element);
/* try names in order and find one that's not in use atm */
pads = gst_element_get_pad_list (element);
pads = element->pads;
name = NULL;
while (!name) {
......@@ -335,7 +332,7 @@ gst_tee_chain (GstPad * pad, GstData * _data)
gst_buffer_ref_by_count (buf, GST_ELEMENT (tee)->numsrcpads - 1);
pads = gst_element_get_pad_list (GST_ELEMENT (tee));
pads = GST_ELEMENT (tee)->pads;
while (pads) {
GstPad *outpad = GST_PAD (pads->data);
......
......@@ -350,7 +350,7 @@ free_entry (TypeFindEntry * entry)
free_entry_buffers (entry);
if (entry->caps)
gst_caps_free (entry->caps);
gst_caps_unref (entry->caps);
g_free (entry);
}
static void
......
......@@ -814,77 +814,6 @@ init_popt_callback (poptContext context, enum poptCallbackReason reason,
}
}
/**
* gst_use_threads:
* @use_threads: a #gboolean indicating whether threads should be used
*
* Does nothing anymore. GStreamer requires threads to be enabled at all times.
*
* Deprecated: This function is deprecated and should not be used in new code.
*/
void
gst_use_threads (gboolean use_threads)
{
}
/**
* gst_has_threads:
*
* Queries if GStreamer has threads enabled.
*
* Returns: %TRUE if threads are enabled.
* Deprecated: This function is deprecated and should not be used in new code.
*/
gboolean
gst_has_threads (void)
{
return TRUE;
}
static GSList *mainloops = NULL;
/**
* gst_main:
*
* Enters the main GStreamer processing loop.
*
* This function duplicates functionality in glib, and will be removed
* during the 0.9 development series.
*/
void
gst_main (void)
{
GMainLoop *loop;
loop = g_main_loop_new (NULL, FALSE);
mainloops = g_slist_prepend (mainloops, loop);
g_main_loop_run (loop);
}
/**
* gst_main_quit:
*
* Exits the main GStreamer processing loop.
*
* This function duplicates functionality in glib, and will be removed
* during the 0.9 development series.
*/
void
gst_main_quit (void)
{
if (!mainloops)
g_error ("Quit more loops than there are");
else {
GMainLoop *loop = mainloops->data;
mainloops = g_slist_delete_link (mainloops, mainloops);
g_main_loop_quit (loop);
g_main_loop_unref (loop);
}
}
/**
* gst_version:
* @major: pointer to a guint to store the major version number
......
......@@ -42,6 +42,7 @@
#include <gst/gstindex.h>
#include <gst/gstinfo.h>
#include <gst/gstinterface.h>
#include <gst/gstiterator.h>
#include <gst/gstmarshal.h>
#include <gst/gstobject.h>
#include <gst/gstpad.h>
......@@ -95,14 +96,6 @@ gboolean gst_init_check_with_popt_table (int *argc, char **argv[],
const GstPoptOption * gst_init_get_popt_table (void);
#ifndef GST_DISABLE_DEPRECATED
void gst_use_threads (gboolean use_threads);
gboolean gst_has_threads (void);
#endif
void gst_main (void);
void gst_main_quit (void);
G_END_DECLS
#endif /* __GST_H__ */
This diff is collapsed.
......@@ -25,6 +25,7 @@
#define __GST_BIN_H__
#include <gst/gstelement.h>
#include <gst/gstiterator.h>
G_BEGIN_DECLS
......@@ -36,6 +37,7 @@ GST_EXPORT GType _gst_bin_type;
#define GST_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_BIN, GstBinClass))
#define GST_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_BIN, GstBin))
#define GST_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_BIN, GstBinClass))
#define GST_BIN_CAST(obj) ((GstBin*)(obj))
/**
* GstBinFlags:
......@@ -58,45 +60,58 @@ GST_EXPORT GType _gst_bin_type;
* and (un)set using GST_FLAG_SET () and GST_FLAG_UNSET ().
*/
typedef enum {
GST_BIN_FLAG_MANAGER = GST_ELEMENT_FLAG_LAST,
GST_BIN_FLAG_FIXED_CLOCK = GST_ELEMENT_FLAG_LAST,
GST_BIN_FLAG_MANAGER,
GST_BIN_SELF_SCHEDULABLE,
GST_BIN_FLAG_PREFER_COTHREADS,
GST_BIN_FLAG_FIXED_CLOCK,
GST_BIN_STATE_LOCKED,
/* padding */
GST_BIN_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 5
} GstBinFlags;
/*typedef struct _GstBin GstBin; */
/*typedef struct _GstBinClass GstBinClass; */
#define GST_BIN_NUMCHILDREN(bin) (GST_BIN_CAST(bin)->numchildren);
#define GST_BIN_CHILDREN(bin) (GST_BIN_CAST(bin)->children);
#define GST_BIN_CHILDREN_COOKIE(bin) (GST_BIN_CAST(bin)->children_cookie);
struct _GstBin {
GstElement element;
/* our children */
/*< public >*/ /* with LOCK */
/* our children, subclass are supposed to update these
* fields to reflect their state with _iterate_*() */
gint numchildren;
GList *children;
guint32 children_cookie;
GstElementState child_states[GST_NUM_STATES];
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
struct _GstBinClass {
GstElementClass parent_class;
/* vtable */
void (*add_element) (GstBin *bin, GstElement *element);
void (*remove_element) (GstBin *bin, GstElement *element);
void (*child_state_change) (GstBin *bin, GstElementState oldstate,
GstElementState newstate, GstElement *element);
/*< public >*/
/* run a full iteration of operation */
gboolean (*iterate) (GstBin *bin);
void (*child_state_change) (GstBin *bin, GstElementState oldstate,
GstElementState newstate, GstElement *element);
/* signals */
void (*element_added) (GstBin *bin, GstElement *child);
void (*element_removed) (GstBin *bin, GstElement *child);
/*< protected >*/
/* vtable */
gboolean (*add_element) (GstBin *bin, GstElement *element);
gboolean (*remove_element) (GstBin *bin, GstElement *element);
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
......@@ -104,24 +119,23 @@ GType gst_bin_get_type (void);
GstElement* gst_bin_new (const gchar *name);
/* add and remove elements from the bin */
void gst_bin_add (GstBin *bin, GstElement *element);
void gst_bin_add_many (GstBin *bin, GstElement *element_1, ...);
void gst_bin_remove (GstBin *bin, GstElement *element);
void gst_bin_remove_many (GstBin *bin, GstElement *element_1, ...);
/* retrieve a single element or the list of children */
GstElement* gst_bin_get_by_name (GstBin *bin, const gchar *name);
GstElement* gst_bin_get_by_name_recurse_up (GstBin *bin, const gchar *name);
G_CONST_RETURN GList*
gst_bin_get_list (GstBin *bin);
GstElement* gst_bin_get_by_interface (GstBin *bin, GType interface);
GList * gst_bin_get_all_by_interface (GstBin *bin, GType interface);
gboolean gst_bin_add (GstBin *bin, GstElement *element);