revert state change changes as agreed so we can rework them gradually

Original commit message from CVS:
revert state change changes as agreed so we can rework them gradually
This commit is contained in:
Thomas Vander Stichele 2004-07-29 20:33:49 +00:00
parent 2ed6c06492
commit 8f0fda26d0
6 changed files with 167 additions and 189 deletions

View file

@ -1,3 +1,18 @@
2004-07-29 Thomas Vander Stichele <thomas at apestaart dot org>
* gst/gstbin.c: (gst_bin_get_type), (gst_bin_class_init),
(gst_bin_add_func), (gst_bin_remove_func),
(gst_bin_child_state_change), (gst_bin_child_state_change_func),
(set_kid_state_func), (gst_bin_change_state), (gst_bin_set_state),
(gst_bin_change_state_norecurse), (gst_bin_dispose),
(gst_bin_sync_children_state):
* gst/gstbin.h:
* gst/gstthread.c: (gst_thread_class_init), (gst_thread_release),
(gst_thread_change_state):
* testsuite/states/Makefile.am:
revert state change patches as agreed so we can rework them
gradually
2004-07-29 Benjamin Otte <otte@gnome.org>
* libs/gst/control/Makefile.am:

View file

@ -34,14 +34,6 @@
#include "gstindex.h"
#include "gstutils.h"
GST_DEBUG_CATEGORY_STATIC (bin_debug);
#define GST_CAT_DEFAULT bin_debug
#define GST_LOG_BIN_CONTENTS(bin, text) GST_LOG_OBJECT ((bin), \
text ": %d elements: %u PLAYING, %u PAUSED, %u READY, %u NULL, own state: %s", \
(bin)->numchildren, (guint) (bin)->child_states[3], \
(guint) (bin)->child_states[2], (bin)->child_states[1], \
(bin)->child_states[0], gst_element_state_get_name (GST_STATE (bin)))
static GstElementDetails gst_bin_details = GST_ELEMENT_DETAILS ("Generic bin",
"Generic/Bin",
"Simple container object",
@ -54,6 +46,7 @@ static gboolean _gst_boolean_did_something_accumulator (GSignalInvocationHint *
static void gst_bin_dispose (GObject * object);
static GstElementStateReturn gst_bin_change_state (GstElement * element);
static GstElementStateReturn gst_bin_change_state_norecurse (GstBin * bin);
#ifndef GST_DISABLE_INDEX
@ -123,8 +116,6 @@ gst_bin_get_type (void)
_gst_bin_type =
g_type_register_static (GST_TYPE_ELEMENT, "GstBin", &bin_info, 0);
GST_DEBUG_CATEGORY_INIT (bin_debug, "bin", GST_DEBUG_BOLD,
"debugging info for the 'bin' container element");
}
return _gst_bin_type;
}
@ -172,6 +163,7 @@ gst_bin_class_init (GstBinClass * klass)
GST_DEBUG_FUNCPTR (gst_bin_restore_thyself);
#endif
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_bin_change_state);
gstelement_class->set_state = GST_DEBUG_FUNCPTR (gst_bin_set_state);
#ifndef GST_DISABLE_INDEX
gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_bin_set_index);
@ -449,34 +441,6 @@ gst_bin_add_many (GstBin * bin, GstElement * element_1, ...)
va_end (args);
}
/* after adding or removing elements, we need to fix the state of the bin
* in a reentrant way. This is done here */
static void
gst_bin_fix_state (GstBin * bin)
{
gint i;
GstElementState desired;
while (TRUE) {
/* find the highest child state */
for (i = GST_NUM_STATES - 1; i > 0; i--) {
if (bin->child_states[i] > 0)
break;
}
g_assert (i < GST_NUM_STATES && i >= 0);
desired = 1 << i;
if (desired == GST_STATE (bin)) {
break;
} else if (desired < GST_STATE (bin)) {
GST_STATE_PENDING (bin) = GST_STATE (bin) >> 1;
} else { /* if (desired > GST_STATE (bin)) */
GST_STATE_PENDING (bin) = GST_STATE (bin) << 1;
}
/* this part is reentrant */
gst_bin_change_state_norecurse (bin);
}
}
static void
gst_bin_add_func (GstBin * bin, GstElement * element)
{
@ -495,14 +459,15 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
return;
}
/* ref to guard unrefs in callbacks */
gst_object_ref (GST_OBJECT (bin));
gst_object_ref (GST_OBJECT (element));
if (GST_STATE (element) > GST_STATE (bin)) {
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
"setting state to receive element \"%s\"", GST_OBJECT_NAME (element));
gst_element_set_state ((GstElement *) bin, GST_STATE (element));
}
/* set the element's parent and add the element to the bin's list of children */
gst_object_set_parent (GST_OBJECT (element), GST_OBJECT (bin));
GST_LOG_BIN_CONTENTS (bin, "before adding element");
bin->children = g_list_append (bin->children, element);
bin->numchildren++;
@ -520,16 +485,10 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
gst_bin_set_element_sched (element, sched);
}
/* check if we need to bump state because a high state element was added */
gst_bin_fix_state (bin);
GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "added element \"%s\"",
GST_OBJECT_NAME (element));
GST_LOG_BIN_CONTENTS (bin, "after adding element");
g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_ADDED], 0, element);
/* we reffed above */
gst_object_unref (GST_OBJECT (element));
gst_object_unref (GST_OBJECT (bin));
}
/**
@ -578,13 +537,9 @@ gst_bin_remove_func (GstBin * bin, GstElement * element)
return;
}
/* ref to guard unrefs in callbacks */
gst_object_ref (GST_OBJECT (bin));
/* remove this element from the list of managed elements */
gst_bin_unset_element_sched (element, GST_ELEMENT_SCHED (bin));
GST_LOG_BIN_CONTENTS (bin, "before removing element");
/* now remove the element from the list of elements */
bin->children = g_list_remove (bin->children, element);
bin->numchildren--;
@ -595,22 +550,30 @@ gst_bin_remove_func (GstBin * bin, GstElement * element)
state_idx++;
bin->child_states[state_idx]--;
GST_CAT_INFO_OBJECT (GST_CAT_PARENTAGE, bin,
"removed child \"%s\", %d children left", GST_OBJECT_NAME (element),
bin->numchildren);
GST_CAT_INFO_OBJECT (GST_CAT_PARENTAGE, bin, "removed child \"%s\"",
GST_OBJECT_NAME (element));
/* ref as we're going to emit a signal */
gst_object_ref (GST_OBJECT (element));
gst_object_unparent (GST_OBJECT (element));
/* check the state */
gst_bin_fix_state (bin);
GST_LOG_BIN_CONTENTS (bin, "after removing element");
/* if we're down to zero children, force state to NULL */
while (bin->numchildren == 0 && GST_ELEMENT_SCHED (bin) != NULL &&
GST_STATE (bin) > GST_STATE_NULL) {
GstElementState next = GST_STATE (bin) >> 1;
GST_STATE_PENDING (bin) = next;
gst_bin_change_state_norecurse (bin);
if (!GST_STATE (bin) == next) {
g_warning ("bin %s failed state change to %d", GST_ELEMENT_NAME (bin),
next);
break;
}
}
g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_REMOVED], 0, element);
/* element is really out of our control now */
gst_object_unref (GST_OBJECT (element));
gst_object_unref (GST_OBJECT (bin));
}
/**
@ -693,8 +656,7 @@ gst_bin_child_state_change (GstBin * bin, GstElementState oldstate,
g_return_if_fail (GST_IS_ELEMENT (child));
GST_CAT_LOG (GST_CAT_STATES, "child %s changed state in bin %s from %s to %s",
GST_ELEMENT_NAME (child) ? GST_ELEMENT_NAME (child) : "(null)",
GST_ELEMENT_NAME (bin) ? GST_ELEMENT_NAME (bin) : "(null)",
GST_ELEMENT_NAME (child), GST_ELEMENT_NAME (bin),
gst_element_state_get_name (oldstate),
gst_element_state_get_name (newstate));
@ -722,7 +684,7 @@ gst_bin_child_state_change_func (GstBin * bin, GstElementState oldstate,
while (new >>= 1)
new_idx++;
GST_LOG_BIN_CONTENTS (bin, "before child state change");
GST_LOCK (bin);
bin->child_states[old_idx]--;
bin->child_states[new_idx]++;
@ -735,17 +697,18 @@ gst_bin_child_state_change_func (GstBin * bin, GstElementState oldstate,
"highest child state is %s, changing bin state accordingly",
gst_element_state_get_name (state));
GST_STATE_PENDING (bin) = state;
GST_UNLOCK (bin);
gst_bin_change_state_norecurse (bin);
if (state != GST_STATE (bin)) {
GST_INFO_OBJECT (bin, "state change in callback %d %d",
state, GST_STATE (bin));
g_warning ("%s: state change in callback %d %d",
GST_ELEMENT_NAME (bin), state, GST_STATE (bin));
}
return;
}
break;
}
}
GST_LOG_BIN_CONTENTS (bin, "after child state change");
GST_UNLOCK (bin);
}
typedef gboolean (*GstBinForeachFunc) (GstBin * bin, GstElement * element,
@ -794,12 +757,10 @@ gst_bin_foreach (GstBin * bin, GstBinForeachFunc func, gpointer data)
typedef struct
{
GstElementState intermediate;
GstElementState pending;
GstElementStateReturn result;
}
SetKidStateData;
static int
set_kid_state_func (GstBin * bin, GstElement * child, gpointer user_data)
{
@ -812,49 +773,17 @@ set_kid_state_func (GstBin * bin, GstElement * child, gpointer user_data)
old_child_state = GST_STATE (child);
/* are we moving up or down? */
if (data->intermediate < data->pending) {
/* up, check if we are already closer to target */
if (old_child_state >= data->intermediate &&
old_child_state < data->pending) {
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
"state of child %s is %s, inbetween intermediate %s and pending %s",
GST_ELEMENT_NAME (child),
gst_element_state_get_name (old_child_state),
gst_element_state_get_name (data->intermediate),
gst_element_state_get_name (data->pending));
return TRUE;
}
} else if (data->intermediate > data->pending) {
/* down, check if we are already closer to target */
if (old_child_state < data->intermediate) {
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
"state of child %s is %s, inbetween intermediate %s and pending %s",
GST_ELEMENT_NAME (child),
gst_element_state_get_name (old_child_state),
gst_element_state_get_name (data->intermediate),
gst_element_state_get_name (data->pending));
return TRUE;
}
} else {
/* same state */
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
"setting final state on child %s, now in %s, going to %s",
GST_ELEMENT_NAME (child), gst_element_state_get_name (old_child_state),
gst_element_state_get_name (data->intermediate));
}
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
"changing state of child %s from current %s to intermediate %s",
"changing state of child %s from current %s to pending %s",
GST_ELEMENT_NAME (child), gst_element_state_get_name (old_child_state),
gst_element_state_get_name (data->intermediate));
gst_element_state_get_name (data->pending));
switch (gst_element_set_state (child, data->intermediate)) {
switch (gst_element_set_state (child, data->pending)) {
case GST_STATE_FAILURE:
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin,
"child '%s' failed to go to state %d(%s)",
GST_ELEMENT_NAME (child),
data->intermediate, gst_element_state_get_name (data->intermediate));
data->pending, gst_element_state_get_name (data->pending));
gst_element_set_state (child, old_child_state);
return FALSE; /* error out to the caller */
@ -863,14 +792,14 @@ set_kid_state_func (GstBin * bin, GstElement * child, gpointer user_data)
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin,
"child '%s' is changing state asynchronously",
GST_ELEMENT_NAME (child));
data->result = GST_STATE_ASYNC;
return TRUE;
case GST_STATE_SUCCESS:
GST_CAT_DEBUG (GST_CAT_STATES,
"child '%s' changed state to %d(%s) successfully",
GST_ELEMENT_NAME (child), data->intermediate,
gst_element_state_get_name (data->intermediate));
data->result = GST_STATE_SUCCESS;
GST_ELEMENT_NAME (child), data->pending,
gst_element_state_get_name (data->pending));
return TRUE;
default:
@ -879,66 +808,87 @@ set_kid_state_func (GstBin * bin, GstElement * child, gpointer user_data)
}
}
static GstElementStateReturn
gst_bin_change_state (GstElement * element)
{
GstBin *bin;
GstElementStateReturn ret;
GstElementState old_state, pending;
SetKidStateData data;
g_return_val_if_fail (GST_IS_BIN (element), GST_STATE_FAILURE);
bin = GST_BIN (element);
old_state = GST_STATE (element);
pending = GST_STATE_PENDING (element);
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
"changing state of children from %s to %s",
gst_element_state_get_name (old_state),
gst_element_state_get_name (pending));
if (pending == GST_STATE_VOID_PENDING)
return GST_STATE_SUCCESS;
data.pending = pending;
data.result = GST_STATE_SUCCESS;
if (!gst_bin_foreach (bin, set_kid_state_func, &data)) {
GST_STATE_PENDING (element) = old_state;
return GST_STATE_FAILURE;
}
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
"done changing bin's state from %s to %s, now in %s",
gst_element_state_get_name (old_state),
gst_element_state_get_name (pending),
gst_element_state_get_name (GST_STATE (element)));
if (data.result == GST_STATE_ASYNC)
ret = GST_STATE_ASYNC;
else {
/* FIXME: this should have been done by the children already, no? */
if (parent_class->change_state) {
ret = parent_class->change_state (element);
} else
ret = GST_STATE_SUCCESS;
}
return ret;
}
GstElementStateReturn
gst_bin_set_state (GstElement * element, GstElementState state)
{
GstBin *bin = GST_BIN (element);
SetKidStateData data;
GstElementStateReturn ret = GST_STATE_FAILURE;
GstElementState intermediate;
GstElementState pending;
/* we start with the state of the bin */
intermediate = GST_STATE (element);
pending = state;
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
"setting state of bin %s from current %s to pending %s",
GST_ELEMENT_NAME (element), gst_element_state_get_name (intermediate),
gst_element_state_get_name (pending));
do {
data.intermediate = intermediate;
data.pending = pending;
data.result = GST_STATE_ASYNC;
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
"setting state of children to intermediate %s",
gst_element_state_get_name (intermediate));
if (GST_STATE (bin) == state) {
SetKidStateData data;
data.pending = state;
data.result = GST_STATE_SUCCESS;
if (!gst_bin_foreach (bin, set_kid_state_func, &data)) {
ret = GST_STATE_FAILURE;
/* break out of the loop after failure */
break;
return GST_STATE_FAILURE;
} else {
ret = data.result;
return data.result;
}
/* if we have reached the target state, we can stop */
if (intermediate == pending)
break;
/* move intermediate state closer to target state */
if (intermediate < pending)
intermediate <<= 1;
else
intermediate >>= 1;
} else {
return GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, set_state, (element,
state), GST_STATE_FAILURE);
}
while (TRUE);
return ret;
}
static GstElementStateReturn
gst_bin_change_state_norecurse (GstBin * bin)
{
GstElementClass *klass;
GstElementStateReturn ret;
klass = GST_ELEMENT_GET_CLASS (bin);
g_return_val_if_fail (klass->change_state, GST_STATE_FAILURE);
if (parent_class->change_state) {
GST_CAT_LOG_OBJECT (GST_CAT_STATES, bin, "setting bin's own state");
ret = parent_class->change_state (GST_ELEMENT (bin));
GST_CAT_LOG_OBJECT (GST_CAT_STATES, bin, "setting bin's own state");
return klass->change_state (GST_ELEMENT (bin));
return ret;
} else
return GST_STATE_FAILURE;
}
static void
@ -955,10 +905,6 @@ gst_bin_dispose (GObject * object)
}
g_assert (bin->children == NULL);
g_assert (bin->numchildren == 0);
g_assert (bin->child_states[3] == 0); /* playing */
g_assert (bin->child_states[2] == 0); /* paused */
g_assert (bin->child_states[1] == 0); /* ready */
g_assert (bin->child_states[0] == 0); /* null */
G_OBJECT_CLASS (parent_class)->dispose (object);
}
@ -1141,10 +1087,40 @@ gst_bin_get_all_by_interface (GstBin * bin, GType interface)
GstElementStateReturn
gst_bin_sync_children_state (GstBin * bin)
{
GList *children;
GstElement *element;
GstElementState state;
GstElementStateReturn ret = GST_STATE_SUCCESS;
g_return_val_if_fail (GST_IS_BIN (bin), GST_STATE_FAILURE);
return gst_element_set_state (GST_ELEMENT (bin),
gst_element_get_state (GST_ELEMENT (bin)));
state = GST_STATE (bin);
children = bin->children;
GST_CAT_INFO (GST_CAT_STATES,
"syncing state of children with bin \"%s\"'s state %s",
GST_ELEMENT_NAME (bin), gst_element_state_get_name (state));
while (children) {
element = GST_ELEMENT (children->data);
children = children->next;
if (GST_STATE (element) != state) {
switch (gst_element_set_state (element, state)) {
case GST_STATE_SUCCESS:
break;
case GST_STATE_ASYNC:
if (ret == GST_STATE_SUCCESS)
ret = GST_STATE_ASYNC;
break;
case GST_STATE_FAILURE:
ret = GST_STATE_FAILURE;
default:
/* make sure gst_element_set_state never returns this */
g_assert_not_reached ();
}
}
}
return ret;
}
#ifndef GST_DISABLE_LOADSAVE

View file

@ -63,7 +63,6 @@ struct _GstBin {
gint numchildren;
GList *children;
/* FIXME 0.9: important!! make this guint instead of GstElementState */
GstElementState child_states[GST_NUM_STATES];
gpointer _gst_reserved[GST_PADDING];
@ -111,9 +110,7 @@ void gst_bin_use_clock (GstBin *bin, GstClock *clock);
GstClock* gst_bin_get_clock (GstBin *bin);
void gst_bin_auto_clock (GstBin *bin);
#ifndef GST_DISABLE_DEPRECATED
GstElementStateReturn gst_bin_sync_children_state (GstBin *bin);
#endif
/* internal */
/* one of our childs signaled a state change */

View file

@ -27,7 +27,6 @@
#include "gstmarshal.h"
#include "gstscheduler.h"
#include "gstinfo.h"
#include "gstutils.h"
#define GST_CAT_DEFAULT GST_CAT_THREAD
#define STACK_SIZE 0x200000
@ -72,8 +71,6 @@ static void gst_thread_set_property (GObject * object, guint prop_id,
static void gst_thread_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static GstElementStateReturn gst_thread_change_state (GstElement * element);
static GstElementStateReturn gst_thread_set_state (GstElement * element,
GstElementState state);
static void gst_thread_child_state_change (GstBin * bin,
GstElementState oldstate, GstElementState newstate, GstElement * element);
@ -184,7 +181,6 @@ gst_thread_class_init (gpointer g_class, gpointer class_data)
#endif
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_thread_change_state);
gstelement_class->set_state = GST_DEBUG_FUNCPTR (gst_thread_set_state);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_thread_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_thread_get_property);
@ -406,34 +402,11 @@ static void
gst_thread_release (GstThread * thread)
{
if (thread != gst_thread_get_current ()) {
GST_DEBUG_OBJECT (thread, "releasing lock");
g_cond_signal (thread->cond);
g_mutex_unlock (thread->lock);
}
}
static GstElementStateReturn
gst_thread_set_state (GstElement * element, GstElementState state)
{
GstElementStateReturn result;
GstThread *thread = GST_THREAD (element);
if (thread != gst_thread_get_current ()) {
gst_thread_catch (thread);
GST_DEBUG_OBJECT (thread, "releasing lock");
g_mutex_unlock (thread->lock);
}
result =
GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, set_state, (element,
state), GST_STATE_FAILURE);
if (thread != gst_thread_get_current ()) {
GST_DEBUG_OBJECT (thread, "grabbing lock");
g_mutex_lock (thread->lock);
gst_thread_release (thread);
}
return result;
}
static GstElementStateReturn
gst_thread_change_state (GstElement * element)
{
@ -478,13 +451,30 @@ gst_thread_change_state (GstElement * element)
break;
case GST_STATE_PAUSED_TO_PLAYING:
{
/* FIXME: recurse into sub-bins */
GList *elements = (GList *) gst_bin_get_list (GST_BIN (thread));
while (elements) {
gst_element_enable_threadsafe_properties ((GstElement *) elements->
data);
elements = g_list_next (elements);
}
/* reset self to spinning */
if (thread == gst_thread_get_current ())
GST_FLAG_SET (thread, GST_THREAD_STATE_SPINNING);
break;
}
case GST_STATE_PLAYING_TO_PAUSED:
{
GList *elements = (GList *) gst_bin_get_list (GST_BIN (thread));
while (elements) {
gst_element_disable_threadsafe_properties ((GstElement *) elements->
data);
elements = g_list_next (elements);
}
break;
}
case GST_STATE_PAUSED_TO_READY:
break;
case GST_STATE_READY_TO_NULL:

View file

@ -1,5 +1,5 @@
include ../Rules
tests_pass = locked parent bin
tests_pass = locked parent
tests_fail =
tests_ignore =
tests_ignore = bin

View file

@ -1,5 +1,5 @@
include ../Rules
tests_pass = locked parent bin
tests_pass = locked parent
tests_fail =
tests_ignore =
tests_ignore = bin