Commit 087dee1f authored by Wim Taymans's avatar Wim Taymans

This is an attempt at not segfaulting on errors but reporting some usefull info instead.

Original commit message from CVS:
This is an attempt at not segfaulting on errors but reporting some
usefull info instead.
- bin changes so errors can propagate.
- changed the _FAST macros to _CAST because that is what they do.
- removed all references to cothreads out of the core, they are
really a scheduler issue, handler with a sched_private gpointer.
- added a live buffer count, for debugging buffer leaks.
- added error checking in gst_scheduler_state_transition this solves the
"out of cothreads" problem.
- GST_ELEMENT_NO_ENTRY == GST_ELEMENT_INFINITE_LOOP
- added 2 private element flasg for use by the scheduler
(_COTHREAD_STOPPING) is now
- added scheduler entry points:
- _yield : to create possible scheduling points.
- _interrupt: to stop execution of an element.
- _error: to signal en error condition to the scheduler.
- improved error messages for pads.
- signal gst_element_error where appropriate.
- added the a new bin to the parent before entering it so one can reference
its children.
- queue memleak fixes on dispose.
- added possible deadlock detection in queue (turned off be default)
- GstBasicScheduler is a real class of its own now, hiding its internal
variables.
- GST_ELEMENT_IS_COTHREAD_STOPPING is gone. either call explicit _yield
operations, or make a sane loop.
- Better state change handling in filesrc. Better error reporting/recovery
too.
- updated core plugins.
- detect non decoupled elements on scheduler boundries and error.
parent 11456df8
......@@ -203,72 +203,69 @@ gst_autoplugcache_loop (GstElement *element)
* the playout pointer hits the end of cache again it has to start pulling.
*/
do {
/* the first time through, the current_playout pointer is going to be NULL */
if (cache->current_playout == NULL) {
/* get a buffer */
buf = gst_pad_pull (cache->sinkpad);
/* the first time through, the current_playout pointer is going to be NULL */
if (cache->current_playout == NULL) {
/* get a buffer */
buf = gst_pad_pull (cache->sinkpad);
/* add it to the cache, though cache == NULL */
gst_buffer_ref (buf);
cache->cache = g_list_prepend (cache->cache, buf);
cache->buffer_count++;
/* add it to the cache, though cache == NULL */
gst_buffer_ref (buf);
cache->cache = g_list_prepend (cache->cache, buf);
cache->buffer_count++;
/* set the current_playout pointer */
cache->current_playout = cache->cache;
/* set the current_playout pointer */
cache->current_playout = cache->cache;
g_signal_emit (G_OBJECT(cache), gst_autoplugcache_signals[FIRST_BUFFER], 0, buf);
/* send the buffer on its way */
gst_pad_push (cache->srcpad, buf);
}
/* the steady state is where the playout is at the front of the cache */
else if (g_list_previous(cache->current_playout) == NULL) {
g_signal_emit (G_OBJECT(cache), gst_autoplugcache_signals[FIRST_BUFFER], 0, buf);
/* if we've been told to fire an empty signal (after a reset) */
if (cache->fire_empty) {
int oldstate = GST_STATE(cache);
GST_DEBUG(0,"at front of cache, about to pull, but firing signal\n");
/* send the buffer on its way */
gst_pad_push (cache->srcpad, buf);
}
/* the steady state is where the playout is at the front of the cache */
else if (g_list_previous(cache->current_playout) == NULL) {
/* if we've been told to fire an empty signal (after a reset) */
if (cache->fire_empty) {
int oldstate = GST_STATE(cache);
GST_DEBUG(0,"at front of cache, about to pull, but firing signal\n");
gst_object_ref (GST_OBJECT (cache));
g_signal_emit (G_OBJECT(cache), gst_autoplugcache_signals[CACHE_EMPTY], 0, NULL);
if (GST_STATE(cache) != oldstate) {
gst_object_ref (GST_OBJECT (cache));
g_signal_emit (G_OBJECT(cache), gst_autoplugcache_signals[CACHE_EMPTY], 0, NULL);
if (GST_STATE(cache) != oldstate) {
gst_object_ref (GST_OBJECT (cache));
GST_DEBUG(GST_CAT_AUTOPLUG, "state changed during signal, aborting\n");
cothread_switch(cothread_current_main());
}
gst_object_unref (GST_OBJECT (cache));
GST_DEBUG(GST_CAT_AUTOPLUG, "state changed during signal, aborting\n");
cothread_switch(cothread_current_main());
}
gst_object_unref (GST_OBJECT (cache));
}
/* get a buffer */
buf = gst_pad_pull (cache->sinkpad);
/* get a buffer */
buf = gst_pad_pull (cache->sinkpad);
/* add it to the front of the cache */
gst_buffer_ref (buf);
cache->cache = g_list_prepend (cache->cache, buf);
cache->buffer_count++;
/* add it to the front of the cache */
gst_buffer_ref (buf);
cache->cache = g_list_prepend (cache->cache, buf);
cache->buffer_count++;
/* set the current_playout pointer */
cache->current_playout = cache->cache;
/* set the current_playout pointer */
cache->current_playout = cache->cache;
/* send the buffer on its way */
gst_pad_push (cache->srcpad, buf);
}
/* otherwise we're trundling through existing cached buffers */
else {
/* move the current_playout pointer */
cache->current_playout = g_list_previous (cache->current_playout);
/* send the buffer on its way */
gst_pad_push (cache->srcpad, buf);
}
if (cache->fire_first) {
g_signal_emit (G_OBJECT(cache), gst_autoplugcache_signals[FIRST_BUFFER], 0, buf);
cache->fire_first = FALSE;
}
/* otherwise we're trundling through existing cached buffers */
else {
/* move the current_playout pointer */
cache->current_playout = g_list_previous (cache->current_playout);
/* push that buffer */
gst_pad_push (cache->srcpad, GST_BUFFER(cache->current_playout->data));
if (cache->fire_first) {
g_signal_emit (G_OBJECT(cache), gst_autoplugcache_signals[FIRST_BUFFER], 0, buf);
cache->fire_first = FALSE;
}
} while (!GST_FLAG_IS_SET (element, GST_ELEMENT_COTHREAD_STOPPING));
/* push that buffer */
gst_pad_push (cache->srcpad, GST_BUFFER(cache->current_playout->data));
}
}
static GstPadNegotiateReturn
......
......@@ -166,7 +166,6 @@ cothread_create (cothread_context *ctx)
cothread_destroy (ctx->threads[slot]);
break;
}
}
sp = CURRENT_STACK_FRAME;
......
......@@ -272,46 +272,44 @@ gst_aggregator_loop (GstElement *element)
aggregator = GST_AGGREGATOR (element);
do {
if (aggregator->sched == AGGREGATOR_LOOP ||
aggregator->sched == AGGREGATOR_LOOP_PEEK) {
GList *pads = aggregator->sinkpads;
while (pads) {
GstPad *pad = GST_PAD (pads->data);
pads = g_list_next (pads);
if (aggregator->sched == AGGREGATOR_LOOP_PEEK) {
buf = gst_pad_peek (pad);
if (buf == NULL)
continue;
g_assert (buf == gst_pad_pull (pad));
debug = "loop_peek";
}
else {
buf = gst_pad_pull (pad);
debug = "loop";
}
gst_aggregator_push (aggregator, pad, buf, debug);
}
}
else {
if (aggregator->sched == AGGREGATOR_LOOP_SELECT) {
GstPad *pad;
if (aggregator->sched == AGGREGATOR_LOOP ||
aggregator->sched == AGGREGATOR_LOOP_PEEK) {
GList *pads = aggregator->sinkpads;
debug = "loop_select";
while (pads) {
GstPad *pad = GST_PAD (pads->data);
pads = g_list_next (pads);
pad = gst_pad_select (aggregator->sinkpads);
buf = gst_pad_pull (pad);
if (aggregator->sched == AGGREGATOR_LOOP_PEEK) {
buf = gst_pad_peek (pad);
if (buf == NULL)
continue;
gst_aggregator_push (aggregator, pad, buf, debug);
g_assert (buf == gst_pad_pull (pad));
debug = "loop_peek";
}
else {
g_assert_not_reached ();
buf = gst_pad_pull (pad);
debug = "loop";
}
gst_aggregator_push (aggregator, pad, buf, debug);
}
}
else {
if (aggregator->sched == AGGREGATOR_LOOP_SELECT) {
GstPad *pad;
debug = "loop_select";
pad = gst_pad_select (aggregator->sinkpads);
buf = gst_pad_pull (pad);
gst_aggregator_push (aggregator, pad, buf, debug);
}
} while (!GST_ELEMENT_IS_COTHREAD_STOPPING (element));
else {
g_assert_not_reached ();
}
}
}
/**
......
......@@ -387,7 +387,7 @@ gst_fakesrc_set_property (GObject *object, guint prop_id, const GValue *value, G
case ARG_OUTPUT:
break;
case ARG_DATA:
src->data = g_value_get_int (value);
src->data = g_value_get_enum (value);
switch (src->data) {
case FAKESRC_DATA_ALLOCATE:
if (src->parent) {
......@@ -403,7 +403,7 @@ gst_fakesrc_set_property (GObject *object, guint prop_id, const GValue *value, G
}
break;
case ARG_SIZETYPE:
src->sizetype = g_value_get_int (value);
src->sizetype = g_value_get_enum (value);
break;
case ARG_SIZEMIN:
src->sizemin = g_value_get_int (value);
......@@ -415,7 +415,7 @@ gst_fakesrc_set_property (GObject *object, guint prop_id, const GValue *value, G
src->parentsize = g_value_get_int (value);
break;
case ARG_FILLTYPE:
src->filltype = g_value_get_int (value);
src->filltype = g_value_get_enum (value);
break;
case ARG_PATTERN:
break;
......@@ -455,13 +455,13 @@ gst_fakesrc_get_property (GObject *object, guint prop_id, GValue *value, GParamS
g_value_set_boolean (value, src->loop_based);
break;
case ARG_OUTPUT:
g_value_set_int (value, src->output);
g_value_set_enum (value, src->output);
break;
case ARG_DATA:
g_value_set_int (value, src->data);
g_value_set_enum (value, src->data);
break;
case ARG_SIZETYPE:
g_value_set_int (value, src->sizetype);
g_value_set_enum (value, src->sizetype);
break;
case ARG_SIZEMIN:
g_value_set_int (value, src->sizemin);
......@@ -473,7 +473,7 @@ gst_fakesrc_get_property (GObject *object, guint prop_id, GValue *value, GParamS
g_value_set_int (value, src->parentsize);
break;
case ARG_FILLTYPE:
g_value_set_int (value, src->filltype);
g_value_set_enum (value, src->filltype);
break;
case ARG_PATTERN:
g_value_set_string (value, src->pattern);
......@@ -689,49 +689,46 @@ static void
gst_fakesrc_loop(GstElement *element)
{
GstFakeSrc *src;
GList *pads;
g_return_if_fail(element != NULL);
g_return_if_fail(GST_IS_FAKESRC(element));
src = GST_FAKESRC (element);
do {
GList *pads;
pads = GST_ELEMENT (src)->pads;
pads = gst_element_get_pad_list (element);
while (pads) {
GstPad *pad = GST_PAD (pads->data);
GstBuffer *buf;
while (pads) {
GstPad *pad = GST_PAD (pads->data);
GstBuffer *buf;
if (src->rt_num_buffers == 0) {
src->eos = TRUE;
}
else {
if (src->rt_num_buffers > 0)
src->rt_num_buffers--;
}
if (src->rt_num_buffers == 0) {
src->eos = TRUE;
}
else {
if (src->rt_num_buffers > 0)
src->rt_num_buffers--;
}
if (src->eos) {
gst_pad_push(pad, GST_BUFFER(gst_event_new (GST_EVENT_EOS)));
return;
}
if (src->eos) {
gst_pad_push(pad, GST_BUFFER(gst_event_new (GST_EVENT_EOS)));
return;
}
buf = gst_fakesrc_create_buffer (src);
GST_BUFFER_TIMESTAMP (buf) = src->buffer_count++;
buf = gst_fakesrc_create_buffer (src);
GST_BUFFER_TIMESTAMP (buf) = src->buffer_count++;
if (!src->silent) {
gst_element_info (element, "fakesrc: loop ******* (%s:%s) > (%d bytes, %llu)",
GST_DEBUG_PAD_NAME (pad), GST_BUFFER_SIZE (buf), GST_BUFFER_TIMESTAMP (buf));
}
if (!src->silent) {
gst_element_info (element, "fakesrc: loop ******* (%s:%s) > (%d bytes, %llu)",
GST_DEBUG_PAD_NAME (pad), GST_BUFFER_SIZE (buf), GST_BUFFER_TIMESTAMP (buf));
}
g_signal_emit (G_OBJECT (src), gst_fakesrc_signals[SIGNAL_HANDOFF], 0,
g_signal_emit (G_OBJECT (src), gst_fakesrc_signals[SIGNAL_HANDOFF], 0,
buf, pad);
gst_pad_push (pad, buf);
gst_pad_push (pad, buf);
pads = g_list_next (pads);
}
} while (!GST_ELEMENT_IS_COTHREAD_STOPPING (element));
pads = g_list_next (pads);
}
}
static GstElementStateReturn
......
......@@ -30,6 +30,7 @@
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>
/**********************************************************************
......@@ -327,10 +328,10 @@ gst_filesrc_map_region (GstFileSrc *src, off_t offset, size_t size)
/* mmap() the data into this new buffer */
GST_BUFFER_DATA(buf) = mmap (NULL, size, PROT_READ, MAP_SHARED, src->fd, offset);
if (GST_BUFFER_DATA(buf) == NULL) {
fprintf (stderr, "ERROR: gstfilesrc couldn't map file!\n");
gst_element_error (GST_ELEMENT (src), "couldn't map file");
} else if (GST_BUFFER_DATA(buf) == MAP_FAILED) {
g_error ("gstfilesrc mmap(0x%x, %d, 0x%llx) : %s",
size, src->fd, offset, sys_errlist[errno]);
gst_element_error (GST_ELEMENT (src), "mmap (0x%x, %d, 0x%llx) : %s",
size, src->fd, offset, strerror (errno));
}
#ifdef MADV_SEQUENTIAL
/* madvise to tell the kernel what to do with it */
......@@ -533,8 +534,8 @@ gst_filesrc_open_file (GstFileSrc *src)
/* open the file */
src->fd = open (src->filename, O_RDONLY);
if (src->fd < 0) {
perror ("open");
gst_element_error (GST_ELEMENT (src), g_strconcat("opening file \"", src->filename, "\"", NULL));
gst_element_error (GST_ELEMENT (src), "opening file \"%s\" (%s)",
src->filename, strerror (errno), NULL);
return FALSE;
} else {
/* find the file length */
......@@ -557,7 +558,6 @@ gst_filesrc_close_file (GstFileSrc *src)
{
g_return_if_fail (GST_FLAG_IS_SET (src, GST_FILESRC_OPEN));
g_print ("close\n");
/* close the file */
close (src->fd);
......@@ -565,6 +565,8 @@ gst_filesrc_close_file (GstFileSrc *src)
src->fd = 0;
src->filelen = 0;
src->curoffset = 0;
if (src->mapbuf)
gst_buffer_unref (src->mapbuf);
GST_FLAG_UNSET (src, GST_FILESRC_OPEN);
}
......@@ -575,17 +577,22 @@ gst_filesrc_change_state (GstElement *element)
{
GstFileSrc *src = GST_FILESRC(element);
if (GST_STATE_PENDING (element) == GST_STATE_NULL) {
if (GST_FLAG_IS_SET (element, GST_FILESRC_OPEN))
gst_filesrc_close_file (GST_FILESRC (element));
} if (GST_STATE_PENDING (element) == GST_STATE_READY) {
src->curoffset = 0;
} else {
if (!GST_FLAG_IS_SET (element, GST_FILESRC_OPEN)) {
if (!gst_filesrc_open_file (GST_FILESRC (element)))
return GST_STATE_FAILURE;
}
switch (GST_STATE_TRANSITION (element)) {
case GST_STATE_NULL_TO_READY:
if (!GST_FLAG_IS_SET (element, GST_FILESRC_OPEN)) {
if (!gst_filesrc_open_file (GST_FILESRC (element)))
return GST_STATE_FAILURE;
}
break;
case GST_STATE_READY_TO_NULL:
if (GST_FLAG_IS_SET (element, GST_FILESRC_OPEN))
gst_filesrc_close_file (GST_FILESRC (element));
break;
case GST_STATE_READY_TO_PAUSED:
case GST_STATE_PAUSED_TO_READY:
src->curoffset = 0;
default:
break;
}
if (GST_ELEMENT_CLASS (parent_class)->change_state)
......
......@@ -205,27 +205,24 @@ gst_identity_loop (GstElement *element)
identity = GST_IDENTITY (element);
do {
buf = gst_pad_pull (identity->sinkpad);
buf = gst_pad_pull (identity->sinkpad);
for (i=identity->duplicate; i; i--) {
if (!identity->silent)
g_print("identity: loop ******* (%s:%s)i (%d bytes, %llu) \n",
for (i=identity->duplicate; i; i--) {
if (!identity->silent)
g_print("identity: loop ******* (%s:%s)i (%d bytes, %llu) \n",
GST_DEBUG_PAD_NAME (identity->sinkpad), GST_BUFFER_SIZE (buf), GST_BUFFER_TIMESTAMP (buf));
g_signal_emit (G_OBJECT (identity), gst_identity_signals[SIGNAL_HANDOFF], 0,
g_signal_emit (G_OBJECT (identity), gst_identity_signals[SIGNAL_HANDOFF], 0,
buf);
if (i>1)
gst_buffer_ref (buf);
gst_pad_push (identity->srcpad, buf);
if (i>1)
gst_buffer_ref (buf);
if (identity->sleep_time)
usleep (identity->sleep_time);
}
gst_pad_push (identity->srcpad, buf);
} while (!GST_ELEMENT_IS_COTHREAD_STOPPING(element));
if (identity->sleep_time)
usleep (identity->sleep_time);
}
}
static void
......
......@@ -138,7 +138,6 @@ gst_bin_init (GstBin * bin)
bin->numchildren = 0;
bin->children = NULL;
bin->eoscond = g_cond_new ();
}
/**
......@@ -364,8 +363,8 @@ gst_bin_remove (GstBin * bin, GstElement * element)
}
void
gst_bin_child_state_change (GstBin * bin, GstElementState old, GstElementState new,
GstElement * child)
gst_bin_child_state_change (GstBin *bin, GstElementState old, GstElementState new,
GstElement *child)
{
gint old_idx = 0, new_idx = 0, i;
......@@ -394,6 +393,22 @@ gst_bin_child_state_change (GstBin * bin, GstElementState old, GstElementState n
GST_UNLOCK (bin);
}
void
gst_bin_child_error (GstBin *bin, GstElement *child)
{
if (GST_STATE (bin) != GST_STATE_NULL) {
/*
GST_STATE_PENDING (bin) = ((GST_STATE (bin) >> 1));
if (gst_element_set_state (bin, GST_STATE (bin)>>1) != GST_STATE_SUCCESS) {
gst_element_error (GST_ELEMENT (bin), "bin \"%s\" couldn't change state on error from child \"%s\"",
GST_ELEMENT_NAME (bin), GST_ELEMENT_NAME (child));
}
*/
gst_element_info (GST_ELEMENT (bin), "bin \"%s\" stopped because child \"%s\" signalled an error",
GST_ELEMENT_NAME (bin), GST_ELEMENT_NAME (child));
}
}
static void
gst_bin_send_event (GstElement *element, GstEvent *event)
{
......@@ -437,6 +452,9 @@ gst_bin_change_state (GstElement * element)
gst_element_set_state (child, old_state);
if (GST_ELEMENT_SCHED (child) == GST_ELEMENT_SCHED (element)) {
/* reset to what is was */
GST_STATE_PENDING (element) = old_state;
gst_bin_change_state (element);
return GST_STATE_FAILURE;
}
break;
......@@ -551,8 +569,6 @@ gst_bin_dispose (GObject * object)
bin->children = NULL;
bin->numchildren = 0;
g_cond_free (bin->eoscond);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
......
......@@ -38,15 +38,15 @@ extern GType _gst_bin_type;
# define GST_IS_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_BIN))
# define GST_IS_BIN_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_BIN))
#define GST_BIN_FAST(obj) ((GstBin*)(obj))
#define GST_BIN_CLASS_FAST(klass) ((GstBinClass*)(klass))
#define GST_BIN_CAST(obj) ((GstBin*)(obj))
#define GST_BIN_CLASS_CAST(klass) ((GstBinClass*)(klass))
#ifdef GST_TYPE_PARANOID
# 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))
#else
# define GST_BIN GST_BIN_FAST
# define GST_BIN_CLASS GST_BIN_CLASS_FAST
# define GST_BIN GST_BIN_CAST
# define GST_BIN_CLASS GST_BIN_CLASS_CAST
#endif
typedef enum {
......@@ -71,11 +71,10 @@ struct _GstBin {
/* our children */
gint numchildren;
GList *children;
GCond *eoscond;
GstElementState child_states[GST_NUM_STATES];
cothread_context *threadcontext;
gpointer sched_private;
};
struct _GstBinClass {
......@@ -113,6 +112,7 @@ gboolean gst_bin_iterate (GstBin *bin);
/* internal */
void gst_bin_child_state_change (GstBin *bin, GstElementState oldstate,
GstElementState newstate, GstElement *child);
void gst_bin_child_error (GstBin *bin, GstElement *child);
#ifdef __cplusplus
}
......
......@@ -31,6 +31,7 @@ GType _gst_buffer_type;
static GMemChunk *_gst_buffer_chunk;
static GMutex *_gst_buffer_chunk_lock;
static gint _gst_buffer_live;
void
_gst_buffer_initialize (void)
......@@ -58,6 +59,20 @@ _gst_buffer_initialize (void)
_gst_buffer_chunk_lock = g_mutex_new ();
_gst_buffer_type = g_type_register_static (G_TYPE_INT, "GstBuffer", &buffer_info, 0);
_gst_buffer_live = 0;
}
/**
* gst_buffer_print_stats:
*
* Print statistics about live buffers.
*/
void
gst_buffer_print_stats (void)
{
g_log (g_log_domain_gstreamer, G_LOG_LEVEL_INFO,
"%d live buffers", _gst_buffer_live);
}
/**
......@@ -74,6 +89,7 @@ gst_buffer_new (void)
g_mutex_lock (_gst_buffer_chunk_lock);
buffer = g_mem_chunk_alloc (_gst_buffer_chunk);
_gst_buffer_live++;
g_mutex_unlock (_gst_buffer_chunk_lock);
GST_INFO (GST_CAT_BUFFER,"creating new buffer %p",buffer);
......@@ -153,11 +169,12 @@ gst_buffer_create_sub (GstBuffer *parent,
g_mutex_lock (_gst_buffer_chunk_lock);
buffer = g_mem_chunk_alloc (_gst_buffer_chunk);
GST_DATA_TYPE(buffer) = _gst_buffer_type;
_gst_buffer_live++;
g_mutex_unlock (_gst_buffer_chunk_lock);
GST_INFO (GST_CAT_BUFFER,"creating new subbuffer %p from parent %p (size %u, offset %u)",
buffer, parent, size, offset);
GST_DATA_TYPE(buffer) = _gst_buffer_type;
buffer->lock = g_mutex_new ();
#ifdef HAVE_ATOMIC_H
atomic_set (&buffer->refcount, 1);
......@@ -292,6 +309,7 @@ gst_buffer_destroy (GstBuffer *buffer)