Doc updates,

Original commit message from CVS:
* REQUIREMENTS:
* docs/design/part-MT-refcounting.txt:
* docs/design/part-clocks.txt:
* docs/design/part-conventions.txt:
* docs/design/part-gstobject.txt:
* docs/design/part-relations.txt:
* docs/design/part-standards.txt:
* libs/gst/control/dparam.c: (gst_dparam_attach):
* libs/gst/control/dparam.h:
* libs/gst/control/dparammanager.c:
(gst_dpman_add_required_dparam_callback),
(gst_dpman_add_required_dparam_direct),
(gst_dpman_add_required_dparam_array), (gst_dpman_attach_dparam),
(gst_dpman_get_dparam), (gst_dpman_get_dparam_type),
(gst_dpman_get_manager), (gst_dpman_bypass_dparam),
(gst_dpman_preprocess_asynchronous),
(gst_dpman_process_asynchronous), (gst_dpman_process_noop):
* libs/gst/control/dparammanager.h:
* testsuite/clock/clock2.c: (gst_clock_debug), (element_wait),
(main):
* testsuite/threads/signals.c: (run_thread), (main):
* testsuite/threads/thread.c: (main):
* tools/gst-launch.c: (fault_handler_sighandler),
(fault_handler_sigaction), (fault_spin):
Doc updates,
Head backporting.
Fix some testcases.
This commit is contained in:
Wim Taymans 2005-03-10 12:53:16 +00:00
parent fdcf9acd4b
commit bd9cd13058
19 changed files with 244 additions and 62 deletions

View file

@ -1,3 +1,33 @@
2005-03-10 Wim Taymans <wim@fluendo.com>
* REQUIREMENTS:
* docs/design/part-MT-refcounting.txt:
* docs/design/part-clocks.txt:
* docs/design/part-conventions.txt:
* docs/design/part-gstobject.txt:
* docs/design/part-relations.txt:
* docs/design/part-standards.txt:
* libs/gst/control/dparam.c: (gst_dparam_attach):
* libs/gst/control/dparam.h:
* libs/gst/control/dparammanager.c:
(gst_dpman_add_required_dparam_callback),
(gst_dpman_add_required_dparam_direct),
(gst_dpman_add_required_dparam_array), (gst_dpman_attach_dparam),
(gst_dpman_get_dparam), (gst_dpman_get_dparam_type),
(gst_dpman_get_manager), (gst_dpman_bypass_dparam),
(gst_dpman_preprocess_asynchronous),
(gst_dpman_process_asynchronous), (gst_dpman_process_noop):
* libs/gst/control/dparammanager.h:
* testsuite/clock/clock2.c: (gst_clock_debug), (element_wait),
(main):
* testsuite/threads/signals.c: (run_thread), (main):
* testsuite/threads/thread.c: (main):
* tools/gst-launch.c: (fault_handler_sighandler),
(fault_handler_sigaction), (fault_spin):
Doc updates,
Head backporting.
Fix some testcases.
2005-03-09 Wim Taymans <wim@fluendo.com>
* gst/gstpad.c: (gst_pad_get_direction):

View file

@ -8,7 +8,7 @@ report at http://sourceforge.net/bugs/?group_id=1936.
Required libraries:
===================
the latest glib2, currently at v2.0.4
glib2
libxml2 (also called gnome-xml, available from http://xmlsoft.org/)
These libraries are all central parts of gnome, and are available from the

View file

@ -123,6 +123,15 @@ Atomic operations
Atomic operations are generally used for refcounting and for the allocation of
small fixed size objects in a memchunk. They can also be used to implement a
lockfree list or stack.
Compare and swap
As part of the atomic operations, compare-and-swap (CAS) can be used to access
or update a single property or pointer in an object without having to take a
lock.
This technique is currently not used in GStreamer but might be added in the
future in performance critical places.
Objects
@ -198,7 +207,8 @@ Objects
corresponding macros. The public object properties are marked in the .h files
with /*< public >*/. The public properties that require a lock to be held are
marked with /*< public >*/ /* with <lock_type> */, where <lock_type> can be
"LOCK" or "STATE_LOCK" to mark the type(s) of lock to be held.
"LOCK" or "STATE_LOCK" or any other lock to mark the type(s) of lock to be
held.
Example:
@ -225,7 +235,7 @@ Objects
* Property lifetime
All properties requiring a lock can change after releasing the associated
lock. This means that as soon as long as you hold the lock, the state of the
lock. This means that as long as you hold the lock, the state of the
object regarding the locked properties is consistent with the information
obtained. As soon as the lock is released, any values required from the
properties might not be valid anymore and can as best be described as a
@ -259,6 +269,16 @@ Objects
anymore of the pad. If you need to be sure it is, you need to extend the
critical section to include the operations on the peer.
The following code is equivalent to the above but with using the functions
to access object properties.
peer = gst_pad_get_parent (pad);
if (peer) {
... use peer ...
gst_object_unref (GST_OBJECT (peer));
}
Example:
Accessing the name of an object makes a copy of the name. The caller of the
@ -270,6 +290,14 @@ Objects
... use name ...
g_free (name);
or:
name = gst_object_get_name (object);
... use name ...
g_free (name);
* Accessor methods
@ -349,7 +377,7 @@ Objects
* GstIterator
GstIterator provides an easier way of retrieving elements in a concurrent
list. The followgin code example is equivalent to the previous example.
list. The following code example is equivalent to the previous example.
Example:

View file

@ -30,7 +30,10 @@ is defined as follows:
- In PLAYING, the stream time is the delta between the absolute time
and the base time. The base time is defined as the absolute time minus
the stream time at the time when the pipeline is set to PLAYING.
- after a seek, the stream time is set to 0 again.
- after a seek, the stream time is set to seek time.
The stream time is completely managed by the GstPipeline object using the
GstClock absolute time.
Timestamps
@ -79,7 +82,8 @@ threads. However, registering the same ID for multiple async notifications is
not possible, the callback will only be called once.
None of the wait operations unref the GstClockID, the application is
responsible for unreffing the ids itself.
responsible for unreffing the ids itself. This holds for both periodic and
single shot notifications.
These clock operations do not operate on the stream time, so the callbacks
will also occur when not in PLAYING state as if the clock just keeps on

View file

@ -30,8 +30,6 @@ ASYNC. Where there is a prefix, as in the element flags, this is usually droppe
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
===================

View file

@ -18,13 +18,15 @@ allows for new additions later.
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()
- GObject refcount is not threadsafe. This will be changed in the future.
GStreamer for now 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
@ -40,6 +42,7 @@ Naming
a more identifiable name. Typically a parent will call _set_name_prefix
on children, taking a lock on them to do so.
Locking
-------
@ -47,26 +50,58 @@ 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.
The object LOCK is a very lowlevel lock that should only be held to access
the object properties for short periods of code.
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.
or writing while the lock is held. All members should be copied or reffed
if they are used after releasing the LOCK.
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
This lock will ideally be used for parentage, flags and naming, which is
reasonable, since they are the only possible things to protect in the
GstObject.
Locking order
-------------
In parent-child situations the lock of the parent must always be taken first
before taking the lock of the child. It is NOT allowed to hold the child
lock before taking the parent lock.
This policy allows for parents to iterate their children and setting properties
on them.
Whenever a nested lock needs to be taken on objects not involved in a
parent-child relation (eg. pads), an explictic locking order has to be defined.
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.
describing the parent hierarchy of a given GstObject.
Flags
-----
Each object in the GStreamer object hierarchy can have flags associated with it,
which are used to describe a state or a feature of the object.
GstObject has flags to mark its lifecycle: FLOATING, DISPOSING and DESTROYED.
Class signals
-------------
It is possible to know when a new object is loaded by connecting to the
GstObjectClass signal. This feature is not very much used and might be removed
at some point.

View file

@ -1,6 +1,10 @@
Object relation types
---------------------
This document describes the relations between objects that exist in GStreamer.
It will also describe the way of handling the relation wrt locking and
refcounting.
1) parent-child relation
+---------+ +-------+

View file

@ -1,16 +1,40 @@
Ownership of dynamic objects
----------------------------
Any object-oriented system or language that doesn't have automatic garbage collection has many potential pitfalls as
far as the pointers go. Therefore, some standards must be adhered to as far as who owns what.
Any object-oriented system or language that doesn't have automatic garbage
collection has many potential pitfalls as far as the pointers go. Therefore,
some standards must be adhered to as far as who owns what.
Strings:
Arguments passed into a function are owned by the caller, and the function will make a copy of the string for its own
internal use. The string should be const gchar *. Strings returned from a function remain the property of the
function called, and the caller must make a copy if it is to use the string for an extended duration.
Strings
-------
Arguments passed into a function are owned by the caller, and the function
will make a copy of the string for its own internal use. The string should
be const gchar *. Strings returned from a function are always a copy of the
original and should be freed after usage by the caller.
ex:
name = gst_element_get_name (element); /* copy of name is made */
.. use name ..
g_free (name); /* free after usage */
Objects
-------
Objects passed into a function are owned by the caller, any additional
reference held to the object after leaving the function should increase the
refcount of that object.
Objects returned from a function are owned by the caller. This means that the
called should _free() or _unref() the object after usage.
ex:
peer = gst_pad_get_peer (pad); /* peer with increased refcount */
if (peer) {
.. use peer ..
gst_object_unref (GST_OBJECT (peer)); /* unref peer after usage */
}
Objects:
The ownership of an object during a function call depends on the type of function. If the function is simply returning
something from the object, such as _get_name(), the caller retains ownership. If the object passed is to be
manipulated in some way, it is generally the case that the function will take over the ownership. This should be
expressed as a reference increment on that object, but isn't in the general case (yet).

View file

@ -1,7 +1,7 @@
/* GStreamer
* Copyright (C) 2001 Steve Baker <stevebaker_org@yahoo.co.uk>
*
* gstdparam.c: Dynamic Parameter functionality
* gstdparam.c: Dynamic Parameter
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -247,10 +247,10 @@ gst_dparam_set_property (GObject * object, guint prop_id, const GValue * value,
/**
* gst_dparam_do_update_default:
* @dparam:
* @timestamp:
* @value:
* @update_info:
* @dparam: the parameter to update
* @timestamp: when should the update take place
* @value: the new value
* @update_info: unused here
*
* Default implementation for changing a dynamic parameter.
* Subclasses might overwrite the behaviour of this.
@ -321,6 +321,7 @@ void
gst_dparam_attach (GstDParam * dparam, GstDParamManager * manager,
GParamSpec * param_spec, gchar * unit_name)
{
GValue value = { 0, };
g_return_if_fail (dparam != NULL);
g_return_if_fail (GST_IS_DPARAM (dparam));
@ -339,6 +340,29 @@ gst_dparam_attach (GstDParam * dparam, GstDParamManager * manager,
GST_DPARAM_IS_LOG (dparam) = gst_unitconv_unit_is_logarithmic (unit_name);
GST_DEBUG ("attaching %s to dparam %p", GST_DPARAM_NAME (dparam), dparam);
// get default value from param-spec and set in dparam
g_value_init (&value, param_spec->value_type);
g_param_value_set_default (param_spec, &value);
switch (G_PARAM_SPEC_VALUE_TYPE (param_spec)) {
case G_TYPE_FLOAT:
dparam->value_float = g_value_get_float (&value);
break;
case G_TYPE_DOUBLE:
dparam->value_double = g_value_get_double (&value);
break;
case G_TYPE_INT:
dparam->value_int = g_value_get_int (&value);
break;
case G_TYPE_INT64:
dparam->value_int64 = g_value_get_int64 (&value);
break;
default:
break;
}
}
/**

View file

@ -55,7 +55,6 @@ G_BEGIN_DECLS
typedef struct _GstDParamClass GstDParamClass;
typedef enum {
GST_DPARAM_UPDATE_FIRST,
GST_DPARAM_UPDATE_NORMAL

View file

@ -205,7 +205,10 @@ gst_dpman_add_required_dparam_callback (GstDParamManager * dpman,
dpwrap =
gst_dpman_new_wrapper (dpman, param_spec, unit_name, GST_DPMAN_CALLBACK);
g_return_val_if_fail (dpwrap != NULL, FALSE);
if (!dpwrap) {
GST_INFO ("failed to obtain a new dparam wrapper");
return FALSE;
}
GST_DEBUG ("adding required callback dparam '%s'",
g_param_spec_get_name (param_spec));
@ -244,7 +247,10 @@ gst_dpman_add_required_dparam_direct (GstDParamManager * dpman,
dpwrap =
gst_dpman_new_wrapper (dpman, param_spec, unit_name, GST_DPMAN_DIRECT);
g_return_val_if_fail (dpwrap != NULL, FALSE);
if (!dpwrap) {
GST_INFO ("failed to obtain a new dparam wrapper");
return FALSE;
}
GST_DEBUG ("adding required direct dparam '%s'",
g_param_spec_get_name (param_spec));
@ -282,7 +288,10 @@ gst_dpman_add_required_dparam_array (GstDParamManager * dpman,
dpwrap =
gst_dpman_new_wrapper (dpman, param_spec, unit_name, GST_DPMAN_ARRAY);
g_return_val_if_fail (dpwrap != NULL, FALSE);
if (!dpwrap) {
GST_INFO ("failed to obtain a new dparam wrapper");
return FALSE;
}
GST_DEBUG ("adding required array dparam '%s'",
g_param_spec_get_name (param_spec));
@ -353,7 +362,12 @@ gst_dpman_attach_dparam (GstDParamManager * dpman, const gchar * dparam_name,
dpwrap = gst_dpman_get_wrapper (dpman, dparam_name);
g_return_val_if_fail (dpwrap != NULL, FALSE);
if (!dpwrap) {
GST_INFO ("failed to obtain get the dparam wrapper for parameter '%s'",
dparam_name);
return FALSE;
}
// FIXME: if these are triggered convert them to messages + returns as well
g_return_val_if_fail (dpwrap->value != NULL, FALSE);
g_return_val_if_fail (G_PARAM_SPEC_VALUE_TYPE (dpwrap->param_spec) ==
GST_DPARAM_TYPE (dparam), FALSE);
@ -407,7 +421,12 @@ gst_dpman_get_dparam (GstDParamManager * dpman, const gchar * dparam_name)
g_return_val_if_fail (dparam_name != NULL, NULL);
dpwrap = gst_dpman_get_wrapper (dpman, dparam_name);
g_return_val_if_fail (dpwrap != NULL, NULL);
if (!dpwrap) {
GST_INFO ("failed to obtain get the dparam wrapper for parameter '%s'",
dparam_name);
return NULL;
}
return dpwrap->dparam;
}
@ -431,7 +450,12 @@ gst_dpman_get_dparam_type (GstDParamManager * dpman, const gchar * dparam_name)
g_return_val_if_fail (dparam_name != NULL, 0);
dpwrap = gst_dpman_get_wrapper (dpman, dparam_name);
g_return_val_if_fail (dpwrap != NULL, 0);
if (!dpwrap) {
GST_INFO ("failed to obtain get the dparam wrapper for parameter '%s'",
dparam_name);
return 0;
}
return G_VALUE_TYPE (dpwrap->value);
}
@ -612,7 +636,7 @@ gst_dpman_set_parent (GstDParamManager * dpman, GstElement * parent)
* Fetch the GstElement that parameters are handled by this manager.
*
* Returns: the GstDParamManager which belongs to this element or NULL
* if it doesn't exist
* if it doesn't exist. Do not call g_object_unref() on it.
*/
GstDParamManager *
gst_dpman_get_manager (GstElement * parent)
@ -623,6 +647,7 @@ gst_dpman_get_manager (GstElement * parent)
g_return_val_if_fail (GST_IS_ELEMENT (parent), NULL);
dpman = (GstDParamManager *) g_hash_table_lookup (_element_registry, parent);
/* FIXME: shouldn't this be g_object_ref(dpman); */
return dpman;
}
@ -649,7 +674,7 @@ gst_dpman_bypass_dparam (GstDParamManager * dpman, const gchar * dparam_name)
g_return_if_fail (dpwrap != NULL);
if (dpwrap->dparam != NULL) {
g_warning ("Bypassing attached dparam '%s'. It will be detached",
GST_WARNING ("Bypassing attached dparam '%s'. It will be detached",
dparam_name);
gst_dpman_detach_dparam (dpman, dparam_name);
}
@ -835,7 +860,7 @@ gst_dpman_preprocess_asynchronous (GstDParamManager * dpman, guint frames,
if (GST_DPMAN_RATE (dpman) == 0) {
g_warning ("The element hasn't given GstDParamManager a frame rate");
GST_WARNING ("The element hasn't given GstDParamManager a frame rate");
return FALSE;
}
dpman->rate_ratio = (guint) (1000000000LL / (gint64) GST_DPMAN_RATE (dpman));
@ -950,14 +975,14 @@ gst_dpman_process_asynchronous (GstDParamManager * dpman, guint frame_count)
GST_DEBUG ("in gst_dpman_process_asynchronous");
if (frame_count >= dpman->num_frames) {
g_warning ("there is no more buffer to process");
GST_WARNING ("there is no more buffer to process");
dpman->next_update_frame = dpman->num_frames;
dpman->frames_to_process = 0;
return FALSE;
}
if (frame_count != dpwrap->next_update_frame) {
g_warning ("frame count %u does not match update frame %u",
GST_WARNING ("frame count %u does not match update frame %u",
frame_count, dpwrap->next_update_frame);
}
@ -1040,7 +1065,7 @@ gst_dpman_preprocess_noop (GstDParamManager * dpman, guint frames,
static gboolean
gst_dpman_process_noop (GstDParamManager * dpman, guint frame_count)
{
g_warning
GST_WARNING
("gst_dpman_process_noop should never be called - something might be wrong with your processing loop");
return FALSE;
}

View file

@ -134,6 +134,9 @@ struct _GstDParamAsyncToUpdate {
(dpman->next_update_frame < dpman->num_frames \
&& (GST_DPMAN_PROCESSFUNC(dpman)(dpman, frame_count))))
/* FIXME: this should pass dpwrap->dparam as the first arg
* the first arg in callback is usually object that triggered the callback
*/
#define GST_DPMAN_CALLBACK_UPDATE(dpwrap, value) ((dpwrap->update_func)(value, dpwrap->update_data))
void _gst_dpman_initialize(void);

View file

@ -8,8 +8,6 @@
#include <gst/gst.h>
static GstClock *clock = NULL;
void
gst_clock_debug (GstClock * clock, GstElement * fakesink)
{
@ -34,6 +32,7 @@ element_wait (GstElement * element, GstClockTime time)
int
main (int argc, char *argv[])
{
GstClock *clock = NULL;
GstElement *pipeline, *fakesrc, *fakesink;
gst_init (&argc, &argv);

View file

@ -201,8 +201,10 @@ run_thread (GstTest * test)
gst_test_do_signal2 (test);
if (TESTNUM == 3)
gst_test_do_prop (test);
if ((i++ % 10000) == 0)
if ((i++ % 10000) == 0) {
g_print (".");
g_usleep (1); /* context switch */
}
}
return NULL;
@ -219,7 +221,7 @@ main (int argc, char **argv)
test1 = g_object_new (GST_TYPE_TEST, NULL);
test2 = g_object_new (GST_TYPE_TEST, NULL);
for (i = 0; i < 100; i++) {
for (i = 0; i < 20; i++) {
g_thread_create ((GThreadFunc) run_thread, test1, TRUE, NULL);
g_thread_create ((GThreadFunc) run_thread, test2, TRUE, NULL);
}

View file

@ -95,6 +95,7 @@ main (gint argc, gchar * argv[])
gst_element_set_state (pipeline, GST_STATE_PLAYING);
g_print ("running ...\n");
while (gst_bin_iterate (GST_BIN (pipeline)));
g_print ("done ...\n");
gst_element_set_state (pipeline, GST_STATE_NULL);
}
if (TESTNUM == 4) {

View file

@ -8,8 +8,6 @@
#include <gst/gst.h>
static GstClock *clock = NULL;
void
gst_clock_debug (GstClock * clock, GstElement * fakesink)
{
@ -34,6 +32,7 @@ element_wait (GstElement * element, GstClockTime time)
int
main (int argc, char *argv[])
{
GstClock *clock = NULL;
GstElement *pipeline, *fakesrc, *fakesink;
gst_init (&argc, &argv);

View file

@ -201,8 +201,10 @@ run_thread (GstTest * test)
gst_test_do_signal2 (test);
if (TESTNUM == 3)
gst_test_do_prop (test);
if ((i++ % 10000) == 0)
if ((i++ % 10000) == 0) {
g_print (".");
g_usleep (1); /* context switch */
}
}
return NULL;
@ -219,7 +221,7 @@ main (int argc, char **argv)
test1 = g_object_new (GST_TYPE_TEST, NULL);
test2 = g_object_new (GST_TYPE_TEST, NULL);
for (i = 0; i < 100; i++) {
for (i = 0; i < 20; i++) {
g_thread_create ((GThreadFunc) run_thread, test1, TRUE, NULL);
g_thread_create ((GThreadFunc) run_thread, test2, TRUE, NULL);
}

View file

@ -95,6 +95,7 @@ main (gint argc, gchar * argv[])
gst_element_set_state (pipeline, GST_STATE_PLAYING);
g_print ("running ...\n");
while (gst_bin_iterate (GST_BIN (pipeline)));
g_print ("done ...\n");
gst_element_set_state (pipeline, GST_STATE_NULL);
}
if (TESTNUM == 4) {

View file

@ -143,15 +143,17 @@ fault_handler_sighandler (int signum)
{
fault_restore ();
/* printf is used instead of g_print(), since it's less likely to
* deadlock */
switch (signum) {
case SIGSEGV:
g_print ("Caught SIGSEGV\n");
printf ("Caught SIGSEGV\n");
break;
case SIGQUIT:
g_print ("Caught SIGQUIT\n");
printf ("Caught SIGQUIT\n");
break;
default:
g_print ("signo: %d\n", signum);
printf ("signo: %d\n", signum);
break;
}
@ -165,17 +167,19 @@ fault_handler_sigaction (int signum, siginfo_t * si, void *misc)
{
fault_restore ();
/* printf is used instead of g_print(), since it's less likely to
* deadlock */
switch (si->si_signo) {
case SIGSEGV:
g_print ("Caught SIGSEGV accessing address %p\n", si->si_addr);
printf ("Caught SIGSEGV accessing address %p\n", si->si_addr);
break;
case SIGQUIT:
g_print ("Caught SIGQUIT\n");
printf ("Caught SIGQUIT\n");
break;
default:
g_print ("signo: %d\n", si->si_signo);
g_print ("errno: %d\n", si->si_errno);
g_print ("code: %d\n", si->si_code);
printf ("signo: %d\n", si->si_signo);
printf ("errno: %d\n", si->si_errno);
printf ("code: %d\n", si->si_code);
break;
}
@ -194,7 +198,7 @@ fault_spin (void)
wait (NULL);
/* FIXME how do we know if we were run by libtool? */
g_print ("Spinning. Please run 'gdb gst-launch %d' to continue debugging, "
printf ("Spinning. Please run 'gdb gst-launch %d' to continue debugging, "
"Ctrl-C to quit, or Ctrl-\\ to dump core.\n", (gint) getpid ());
while (spinning)
g_usleep (1000000);