initial fix of thread synch for queues and state change

Original commit message from CVS:
initial fix of thread synch for queues and state change
This commit is contained in:
Matt Howell 2001-04-27 20:55:47 +00:00
parent 41ca38a36d
commit cb0068581c
7 changed files with 240 additions and 89 deletions

View file

@ -791,6 +791,7 @@ gst_element_get_factory (GstElement *element)
return oclass->elementfactory; return oclass->elementfactory;
} }
/** /**
* gst_element_change_state: * gst_element_change_state:
* @element: element to change state of * @element: element to change state of
@ -822,6 +823,11 @@ GST_ELEMENT_NAME(element),GST_ELEMENT_NAME(GST_ELEMENT_PARENT(element)),GST_ELEM
GST_STATE (element) = GST_STATE_PENDING (element); GST_STATE (element) = GST_STATE_PENDING (element);
GST_STATE_PENDING (element) = GST_STATE_NONE_PENDING; GST_STATE_PENDING (element) = GST_STATE_NONE_PENDING;
// note: queues' state_change is a special case because it needs to lock
// for synchronization (from another thread). since this signal may block
// or (worse) make another state change, the queue needs to unlock before
// calling. thus, gstqueue.c::gst_queue_state_change() blocks, unblocks,
// unlocks, then emits this.
gtk_signal_emit (GTK_OBJECT (element), gst_element_signals[STATE_CHANGE], gtk_signal_emit (GTK_OBJECT (element), gst_element_signals[STATE_CHANGE],
GST_STATE (element)); GST_STATE (element));
return TRUE; return TRUE;
@ -1132,4 +1138,3 @@ gst_element_signal_eos (GstElement *element)
gtk_signal_emit (GTK_OBJECT (element), gst_element_signals[EOS]); gtk_signal_emit (GTK_OBJECT (element), gst_element_signals[EOS]);
GST_FLAG_SET(element,GST_ELEMENT_COTHREAD_STOPPING); GST_FLAG_SET(element,GST_ELEMENT_COTHREAD_STOPPING);
} }

View file

@ -34,6 +34,7 @@
#include "gst_private.h" #include "gst_private.h"
#include "gstqueue.h" #include "gstqueue.h"
#include "gstscheduler.h"
GstElementDetails gst_queue_details = { GstElementDetails gst_queue_details = {
"Queue", "Queue",
@ -55,7 +56,7 @@ enum {
ARG_0, ARG_0,
ARG_LEVEL, ARG_LEVEL,
ARG_MAX_LEVEL, ARG_MAX_LEVEL,
ARG_BLOCK, // ARG_BLOCK,
}; };
@ -115,8 +116,8 @@ gst_queue_class_init (GstQueueClass *klass)
GTK_ARG_READABLE, ARG_LEVEL); GTK_ARG_READABLE, ARG_LEVEL);
gtk_object_add_arg_type ("GstQueue::max_level", GTK_TYPE_INT, gtk_object_add_arg_type ("GstQueue::max_level", GTK_TYPE_INT,
GTK_ARG_READWRITE, ARG_MAX_LEVEL); GTK_ARG_READWRITE, ARG_MAX_LEVEL);
gtk_object_add_arg_type ("GstQueue::block", GTK_TYPE_BOOL, // gtk_object_add_arg_type ("GstQueue::block", GTK_TYPE_BOOL,
GTK_ARG_READWRITE, ARG_BLOCK); // GTK_ARG_READWRITE, ARG_BLOCK);
gtkobject_class->set_arg = gst_queue_set_arg; gtkobject_class->set_arg = gst_queue_set_arg;
gtkobject_class->get_arg = gst_queue_get_arg; gtkobject_class->get_arg = gst_queue_get_arg;
@ -145,7 +146,7 @@ gst_queue_init (GstQueue *queue)
queue->queue = NULL; queue->queue = NULL;
queue->level_buffers = 0; queue->level_buffers = 0;
queue->max_buffers = 100; queue->max_buffers = 100;
queue->block = TRUE; // queue->block = TRUE;
queue->level_bytes = 0; queue->level_bytes = 0;
queue->size_buffers = 0; queue->size_buffers = 0;
queue->size_bytes = 0; queue->size_bytes = 0;
@ -269,10 +270,17 @@ gst_queue_chain (GstPad *pad, GstBuffer *buf)
GST_DEBUG (0,"queue: %s: chain %d %p\n", name, queue->level_buffers, buf); GST_DEBUG (0,"queue: %s: chain %d %p\n", name, queue->level_buffers, buf);
while (queue->level_buffers >= queue->max_buffers) { while (queue->level_buffers >= queue->max_buffers) {
// if there's a pending state change for this queue or its manager, switch
// back to iterator so bottom half of state change executes
if (GST_STATE_PENDING(queue) != GST_STATE_NONE_PENDING ||
GST_STATE_PENDING(GST_SCHEDULE(GST_ELEMENT(queue)->sched)->parent) != GST_STATE_NONE_PENDING)
{
GST_UNLOCK(queue);
cothread_switch(cothread_current_main());
}
GST_DEBUG (0,"queue: %s waiting %d\n", name, queue->level_buffers); GST_DEBUG (0,"queue: %s waiting %d\n", name, queue->level_buffers);
STATUS("%s: O\n"); STATUS("%s: O\n");
//g_cond_timed_wait (queue->fullcond, queue->fulllock, queue->timeval);
//FIXME need to signal other thread in case signals got lost?
g_cond_signal (queue->emptycond); g_cond_signal (queue->emptycond);
g_cond_wait (queue->fullcond, GST_OBJECT(queue)->lock); g_cond_wait (queue->fullcond, GST_OBJECT(queue)->lock);
STATUS("%s: O+\n"); STATUS("%s: O+\n");
@ -281,21 +289,19 @@ gst_queue_chain (GstPad *pad, GstBuffer *buf)
/* put the buffer on the tail of the list */ /* put the buffer on the tail of the list */
queue->queue = g_slist_append (queue->queue, buf); queue->queue = g_slist_append (queue->queue, buf);
// STATUS("%s: +\n");
GST_DEBUG (0,"(%s:%s)+ ",GST_DEBUG_PAD_NAME(pad)); GST_DEBUG (0,"(%s:%s)+ ",GST_DEBUG_PAD_NAME(pad));
/* if we were empty, but aren't any more, signal a condition */ /* if we were empty, but aren't any more, signal a condition */
tosignal = (queue->level_buffers >= 0);
queue->level_buffers++; queue->level_buffers++;
// if (queue->level_buffers >= 0)
if (queue->level_buffers == 1)
{
GST_DEBUG (0,"queue: %s signalling emptycond\n", name);
g_cond_signal (queue->emptycond);
}
/* we can unlock now */
GST_DEBUG (0,"queue: %s chain %d end signal(%d,%p)\n", name, queue->level_buffers, tosignal, queue->emptycond); GST_DEBUG (0,"queue: %s chain %d end signal(%d,%p)\n", name, queue->level_buffers, tosignal, queue->emptycond);
if (tosignal) {
// STATUS("%s: >\n");
g_cond_signal (queue->emptycond);
// STATUS("%s: >>\n");
}
GST_UNLOCK (queue); GST_UNLOCK (queue);
} }
@ -307,6 +313,8 @@ gst_queue_get (GstPad *pad)
GSList *front; GSList *front;
const guchar *name; const guchar *name;
g_assert(pad != NULL);
g_assert(GST_IS_PAD(pad));
g_return_val_if_fail (pad != NULL, NULL); g_return_val_if_fail (pad != NULL, NULL);
g_return_val_if_fail (GST_IS_PAD (pad), NULL); g_return_val_if_fail (GST_IS_PAD (pad), NULL);
@ -320,22 +328,32 @@ gst_queue_get (GstPad *pad)
GST_DEBUG (0,"queue: %s have queue lock\n", name); GST_DEBUG (0,"queue: %s have queue lock\n", name);
// we bail if there's nothing there // we bail if there's nothing there
if (!queue->level_buffers && !queue->block) { // g_assert(queue->block);
// if (!queue->level_buffers && !queue->block) {
// GST_UNLOCK(queue);
// return NULL;
// }
while (!queue->level_buffers) {
if (GST_FLAG_IS_SET (queue->sinkpad, GST_PAD_EOS)) {
STATUS("queue: %s U released lock\n");
GST_UNLOCK(queue); GST_UNLOCK(queue);
gst_pad_set_eos (queue->srcpad);
// this return NULL shouldn't hurt anything...
return NULL; return NULL;
} }
while (!queue->level_buffers) { // if there's a pending state change for this queue or its manager, switch
STATUS("queue: %s U released lock\n"); // back to iterator so bottom half of state change executes
//g_cond_timed_wait (queue->emptycond, queue->emptylock, queue->timeval); if (GST_STATE_PENDING(queue) != GST_STATE_NONE_PENDING ||
if (GST_FLAG_IS_SET (queue->sinkpad, GST_PAD_EOS)) { GST_STATE_PENDING(GST_SCHEDULE(GST_ELEMENT(queue)->sched)->parent) != GST_STATE_NONE_PENDING)
gst_pad_set_eos (queue->srcpad); {
return NULL; GST_UNLOCK(queue);
cothread_switch(cothread_current_main());
} }
//FIXME need to signal other thread in case signals got lost?
g_cond_signal (queue->fullcond); g_cond_signal (queue->fullcond);
g_cond_wait (queue->emptycond, GST_OBJECT(queue)->lock); g_cond_wait (queue->emptycond, GST_OBJECT(queue)->lock);
// STATUS("queue: %s U- getting lock\n");
} }
front = queue->queue; front = queue->queue;
@ -344,32 +362,34 @@ gst_queue_get (GstPad *pad)
queue->queue = g_slist_remove_link (queue->queue, front); queue->queue = g_slist_remove_link (queue->queue, front);
g_slist_free (front); g_slist_free (front);
// if (queue->level_buffers < queue->max_buffers)
if (queue->level_buffers == queue->max_buffers)
{
GST_DEBUG (0,"queue: %s signalling fullcond\n", name);
g_cond_signal (queue->fullcond);
}
queue->level_buffers--; queue->level_buffers--;
// STATUS("%s: -\n");
GST_DEBUG (0,"(%s:%s)- ",GST_DEBUG_PAD_NAME(pad)); GST_DEBUG (0,"(%s:%s)- ",GST_DEBUG_PAD_NAME(pad));
if (queue->level_buffers < queue->max_buffers) {
// STATUS("%s: < \n");
g_cond_signal (queue->fullcond);
// STATUS("%s: << \n");
}
GST_UNLOCK(queue); GST_UNLOCK(queue);
// GST_DEBUG (0,"queue: %s pushing %d %p \n", name, queue->level_buffers, buf);
// gst_pad_push (queue->srcpad, buf);
// GST_DEBUG (0,"queue: %s pushing %d done \n", name, queue->level_buffers);
return buf; return buf;
/* unlock now */
} }
static GstElementStateReturn static GstElementStateReturn
gst_queue_change_state (GstElement *element) gst_queue_change_state (GstElement *element)
{ {
GstQueue *queue; GstQueue *queue;
GstElementStateReturn ret;
g_return_val_if_fail (GST_IS_QUEUE (element), GST_STATE_FAILURE); g_return_val_if_fail (GST_IS_QUEUE (element), GST_STATE_FAILURE);
queue = GST_QUEUE (element); queue = GST_QUEUE (element);
// lock the queue so another thread (not in sync with this thread's state)
// can't call this queue's _get (or whatever)
GST_LOCK (queue);
GST_DEBUG (0,"gstqueue: state pending %d\n", GST_STATE_PENDING (element)); GST_DEBUG (0,"gstqueue: state pending %d\n", GST_STATE_PENDING (element));
/* if going down into NULL state, clear out buffers*/ /* if going down into NULL state, clear out buffers*/
@ -380,9 +400,41 @@ gst_queue_change_state (GstElement *element)
/* if we haven't failed already, give the parent class a chance to ;-) */ /* if we haven't failed already, give the parent class a chance to ;-) */
if (GST_ELEMENT_CLASS (parent_class)->change_state) if (GST_ELEMENT_CLASS (parent_class)->change_state)
return GST_ELEMENT_CLASS (parent_class)->change_state (element); {
gboolean valid_handler = FALSE;
guint state_change_id = gtk_signal_lookup("state_change", GTK_OBJECT_TYPE(element));
return GST_STATE_SUCCESS; // determine whether we need to block the parent (element) class'
// STATE_CHANGE signal so we can UNLOCK before returning. we block
// it if we could find the state_change signal AND there's a signal
// handler attached to it.
//
// note: this assumes that change_state() *only* emits state_change signal.
// if element change_state() emits other signals, they need to be blocked
// as well.
if (state_change_id &&
gtk_signal_handler_pending(GTK_OBJECT(element), state_change_id, FALSE))
valid_handler = TRUE;
if (valid_handler)
gtk_signal_handler_block(GTK_OBJECT(element), state_change_id);
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
if (valid_handler)
gtk_signal_handler_unblock(GTK_OBJECT(element), state_change_id);
// UNLOCK, *then* emit signal (if there's one there)
GST_UNLOCK(queue);
if (valid_handler)
gtk_signal_emit(GTK_OBJECT (element), state_change_id, GST_STATE(element));
}
else
{
ret = GST_STATE_SUCCESS;
GST_UNLOCK(queue);
}
return ret;
} }
@ -400,9 +452,9 @@ gst_queue_set_arg (GtkObject *object, GtkArg *arg, guint id)
case ARG_MAX_LEVEL: case ARG_MAX_LEVEL:
queue->max_buffers = GTK_VALUE_INT (*arg); queue->max_buffers = GTK_VALUE_INT (*arg);
break; break;
case ARG_BLOCK: // case ARG_BLOCK:
queue->block = GTK_VALUE_BOOL (*arg); // queue->block = GTK_VALUE_BOOL (*arg);
break; // break;
default: default:
break; break;
} }
@ -425,9 +477,9 @@ gst_queue_get_arg (GtkObject *object, GtkArg *arg, guint id)
case ARG_MAX_LEVEL: case ARG_MAX_LEVEL:
GTK_VALUE_INT (*arg) = queue->max_buffers; GTK_VALUE_INT (*arg) = queue->max_buffers;
break; break;
case ARG_BLOCK: // case ARG_BLOCK:
GTK_VALUE_BOOL (*arg) = queue->block; // GTK_VALUE_BOOL (*arg) = queue->block;
break; // break;
default: default:
arg->type = GTK_TYPE_INVALID; arg->type = GTK_TYPE_INVALID;
break; break;

View file

@ -61,7 +61,7 @@ struct _GstQueue {
gint level_buffers; /* number of buffers queued here */ gint level_buffers; /* number of buffers queued here */
gint max_buffers; /* maximum number of buffers queued here */ gint max_buffers; /* maximum number of buffers queued here */
gboolean block; /* if set to FALSE, _get returns NULL if queue empty */ // gboolean block; /* if set to FALSE, _get returns NULL if queue empty */
gint level_bytes; /* number of bytes queued here */ gint level_bytes; /* number of bytes queued here */
gint size_buffers; /* size of queue in buffers */ gint size_buffers; /* size of queue in buffers */
gint size_bytes; /* size of queue in bytes */ gint size_bytes; /* size of queue in bytes */

View file

@ -1297,6 +1297,20 @@ g_return_val_if_fail (chains != NULL, FALSE);
GST_DEBUG (GST_CAT_DATAFLOW,"set COTHREAD_STOPPING flag on \"%s\"(@%p)\n", GST_DEBUG (GST_CAT_DATAFLOW,"set COTHREAD_STOPPING flag on \"%s\"(@%p)\n",
GST_ELEMENT_NAME (entry),entry); GST_ELEMENT_NAME (entry),entry);
cothread_switch (entry->threadstate); cothread_switch (entry->threadstate);
// following is a check to see if the chain was interrupted due to a
// top-half state_change(). (i.e., if there's a pending state.)
//
// if it was, return to gstthread.c::gst_thread_main_loop() to
// execute the state change.
GST_DEBUG (GST_CAT_DATAFLOW,"cothread switch ended or interrupted\n");
if (GST_STATE_PENDING(GST_SCHEDULE(sched)->parent) != GST_STATE_NONE_PENDING)
{
GST_DEBUG (GST_CAT_DATAFLOW,"handle pending state %d\n",
GST_STATE_PENDING(GST_SCHEDULE(sched)->parent));
return 0;
}
} else { } else {
GST_INFO (GST_CAT_DATAFLOW,"no entry into chain!"); GST_INFO (GST_CAT_DATAFLOW,"no entry into chain!");
} }

View file

@ -27,7 +27,7 @@
#include "gstthread.h" #include "gstthread.h"
#include "gstscheduler.h" #include "gstscheduler.h"
#include "gstqueue.h"
GstElementDetails gst_thread_details = { GstElementDetails gst_thread_details = {
"Threaded container", "Threaded container",
@ -252,7 +252,8 @@ gst_thread_change_state (GstElement *element)
GST_DEBUG (GST_CAT_THREAD, "creating thread \"%s\"\n", GST_DEBUG (GST_CAT_THREAD, "creating thread \"%s\"\n",
GST_ELEMENT_NAME (GST_ELEMENT (element))); GST_ELEMENT_NAME (GST_ELEMENT (element)));
g_mutex_lock (thread->lock); g_mutex_lock(thread->lock);
// create the thread // create the thread
pthread_create (&thread->thread_id, NULL, pthread_create (&thread->thread_id, NULL,
gst_thread_main_loop, thread); gst_thread_main_loop, thread);
@ -260,8 +261,8 @@ gst_thread_change_state (GstElement *element)
// wait for it to 'spin up' // wait for it to 'spin up'
GST_DEBUG (GST_CAT_THREAD, "sync: waiting for spinup\n"); GST_DEBUG (GST_CAT_THREAD, "sync: waiting for spinup\n");
g_cond_wait(thread->cond,thread->lock); g_cond_wait(thread->cond,thread->lock);
g_mutex_unlock(thread->lock);
GST_DEBUG (GST_CAT_THREAD, "sync: thread claims to be up\n"); GST_DEBUG (GST_CAT_THREAD, "sync: thread claims to be up\n");
g_mutex_unlock(thread->lock);
} else { } else {
GST_INFO (GST_CAT_THREAD, "NOT starting thread \"%s\"", GST_INFO (GST_CAT_THREAD, "NOT starting thread \"%s\"",
GST_ELEMENT_NAME (GST_ELEMENT (element))); GST_ELEMENT_NAME (GST_ELEMENT (element)));
@ -284,6 +285,30 @@ gst_thread_change_state (GstElement *element)
GST_INFO (GST_CAT_THREAD,"pausing thread \"%s\"", GST_INFO (GST_CAT_THREAD,"pausing thread \"%s\"",
GST_ELEMENT_NAME (GST_ELEMENT (element))); GST_ELEMENT_NAME (GST_ELEMENT (element)));
// the following code ensures that the bottom half of thread will run
// to perform each elements' change_state() (by calling gstbin.c::
// change_state()).
// + the pending state was already set by gstelement.c::set_state()
// + find every queue we manage, and signal its empty and full conditions
{
GList *elements = (element->sched)->elements;
while (elements)
{
GstElement *e = GST_ELEMENT(elements->data);
g_assert(e);
elements = g_list_next(elements);
if (GST_IS_QUEUE(e))
{
//FIXME make this more efficient by only waking queues that are asleep
//FIXME and only waking the appropriate condition (depending on if it's
//FIXME on up- or down-stream side)
//
//FIXME also make this more efficient by keeping list of managed queues
g_cond_signal((GST_QUEUE(e)->emptycond));
g_cond_signal((GST_QUEUE(e)->fullcond));
}
}
}
gst_thread_signal_thread(thread,FALSE); gst_thread_signal_thread(thread,FALSE);
break; break;
case GST_STATE_PLAYING_TO_READY: case GST_STATE_PLAYING_TO_READY:
@ -375,7 +400,6 @@ gst_thread_main_loop (void *arg)
GST_DEBUG(0,"sync: removed spinning state due to failed iteration\n"); GST_DEBUG(0,"sync: removed spinning state due to failed iteration\n");
} }
} }
GST_DEBUG (GST_CAT_THREAD, "sync: waiting at bottom of while for signal from main process\n"); GST_DEBUG (GST_CAT_THREAD, "sync: waiting at bottom of while for signal from main process\n");
g_mutex_lock (thread->lock); g_mutex_lock (thread->lock);
GST_DEBUG (GST_CAT_THREAD, "sync: signaling that the thread is out of the SPINNING loop\n"); GST_DEBUG (GST_CAT_THREAD, "sync: signaling that the thread is out of the SPINNING loop\n");
@ -399,6 +423,10 @@ gst_thread_main_loop (void *arg)
} }
// the set flag is to say whether it should set TRUE or FALSE // the set flag is to say whether it should set TRUE or FALSE
//
// WARNING: this has synchronization built in! if you remove or add any
// locks, waits, signals, or unlocks you need to be sure they match the
// code above (in gst_thread_main_loop()). basically, don't change anything.
static void static void
gst_thread_signal_thread (GstThread *thread, gboolean spinning) gst_thread_signal_thread (GstThread *thread, gboolean spinning)
{ {

View file

@ -34,6 +34,7 @@
#include "gst_private.h" #include "gst_private.h"
#include "gstqueue.h" #include "gstqueue.h"
#include "gstscheduler.h"
GstElementDetails gst_queue_details = { GstElementDetails gst_queue_details = {
"Queue", "Queue",
@ -55,7 +56,7 @@ enum {
ARG_0, ARG_0,
ARG_LEVEL, ARG_LEVEL,
ARG_MAX_LEVEL, ARG_MAX_LEVEL,
ARG_BLOCK, // ARG_BLOCK,
}; };
@ -115,8 +116,8 @@ gst_queue_class_init (GstQueueClass *klass)
GTK_ARG_READABLE, ARG_LEVEL); GTK_ARG_READABLE, ARG_LEVEL);
gtk_object_add_arg_type ("GstQueue::max_level", GTK_TYPE_INT, gtk_object_add_arg_type ("GstQueue::max_level", GTK_TYPE_INT,
GTK_ARG_READWRITE, ARG_MAX_LEVEL); GTK_ARG_READWRITE, ARG_MAX_LEVEL);
gtk_object_add_arg_type ("GstQueue::block", GTK_TYPE_BOOL, // gtk_object_add_arg_type ("GstQueue::block", GTK_TYPE_BOOL,
GTK_ARG_READWRITE, ARG_BLOCK); // GTK_ARG_READWRITE, ARG_BLOCK);
gtkobject_class->set_arg = gst_queue_set_arg; gtkobject_class->set_arg = gst_queue_set_arg;
gtkobject_class->get_arg = gst_queue_get_arg; gtkobject_class->get_arg = gst_queue_get_arg;
@ -145,7 +146,7 @@ gst_queue_init (GstQueue *queue)
queue->queue = NULL; queue->queue = NULL;
queue->level_buffers = 0; queue->level_buffers = 0;
queue->max_buffers = 100; queue->max_buffers = 100;
queue->block = TRUE; // queue->block = TRUE;
queue->level_bytes = 0; queue->level_bytes = 0;
queue->size_buffers = 0; queue->size_buffers = 0;
queue->size_bytes = 0; queue->size_bytes = 0;
@ -269,10 +270,17 @@ gst_queue_chain (GstPad *pad, GstBuffer *buf)
GST_DEBUG (0,"queue: %s: chain %d %p\n", name, queue->level_buffers, buf); GST_DEBUG (0,"queue: %s: chain %d %p\n", name, queue->level_buffers, buf);
while (queue->level_buffers >= queue->max_buffers) { while (queue->level_buffers >= queue->max_buffers) {
// if there's a pending state change for this queue or its manager, switch
// back to iterator so bottom half of state change executes
if (GST_STATE_PENDING(queue) != GST_STATE_NONE_PENDING ||
GST_STATE_PENDING(GST_SCHEDULE(GST_ELEMENT(queue)->sched)->parent) != GST_STATE_NONE_PENDING)
{
GST_UNLOCK(queue);
cothread_switch(cothread_current_main());
}
GST_DEBUG (0,"queue: %s waiting %d\n", name, queue->level_buffers); GST_DEBUG (0,"queue: %s waiting %d\n", name, queue->level_buffers);
STATUS("%s: O\n"); STATUS("%s: O\n");
//g_cond_timed_wait (queue->fullcond, queue->fulllock, queue->timeval);
//FIXME need to signal other thread in case signals got lost?
g_cond_signal (queue->emptycond); g_cond_signal (queue->emptycond);
g_cond_wait (queue->fullcond, GST_OBJECT(queue)->lock); g_cond_wait (queue->fullcond, GST_OBJECT(queue)->lock);
STATUS("%s: O+\n"); STATUS("%s: O+\n");
@ -281,21 +289,19 @@ gst_queue_chain (GstPad *pad, GstBuffer *buf)
/* put the buffer on the tail of the list */ /* put the buffer on the tail of the list */
queue->queue = g_slist_append (queue->queue, buf); queue->queue = g_slist_append (queue->queue, buf);
// STATUS("%s: +\n");
GST_DEBUG (0,"(%s:%s)+ ",GST_DEBUG_PAD_NAME(pad)); GST_DEBUG (0,"(%s:%s)+ ",GST_DEBUG_PAD_NAME(pad));
/* if we were empty, but aren't any more, signal a condition */ /* if we were empty, but aren't any more, signal a condition */
tosignal = (queue->level_buffers >= 0);
queue->level_buffers++; queue->level_buffers++;
// if (queue->level_buffers >= 0)
if (queue->level_buffers == 1)
{
GST_DEBUG (0,"queue: %s signalling emptycond\n", name);
g_cond_signal (queue->emptycond);
}
/* we can unlock now */
GST_DEBUG (0,"queue: %s chain %d end signal(%d,%p)\n", name, queue->level_buffers, tosignal, queue->emptycond); GST_DEBUG (0,"queue: %s chain %d end signal(%d,%p)\n", name, queue->level_buffers, tosignal, queue->emptycond);
if (tosignal) {
// STATUS("%s: >\n");
g_cond_signal (queue->emptycond);
// STATUS("%s: >>\n");
}
GST_UNLOCK (queue); GST_UNLOCK (queue);
} }
@ -307,6 +313,8 @@ gst_queue_get (GstPad *pad)
GSList *front; GSList *front;
const guchar *name; const guchar *name;
g_assert(pad != NULL);
g_assert(GST_IS_PAD(pad));
g_return_val_if_fail (pad != NULL, NULL); g_return_val_if_fail (pad != NULL, NULL);
g_return_val_if_fail (GST_IS_PAD (pad), NULL); g_return_val_if_fail (GST_IS_PAD (pad), NULL);
@ -320,22 +328,32 @@ gst_queue_get (GstPad *pad)
GST_DEBUG (0,"queue: %s have queue lock\n", name); GST_DEBUG (0,"queue: %s have queue lock\n", name);
// we bail if there's nothing there // we bail if there's nothing there
if (!queue->level_buffers && !queue->block) { // g_assert(queue->block);
// if (!queue->level_buffers && !queue->block) {
// GST_UNLOCK(queue);
// return NULL;
// }
while (!queue->level_buffers) {
if (GST_FLAG_IS_SET (queue->sinkpad, GST_PAD_EOS)) {
STATUS("queue: %s U released lock\n");
GST_UNLOCK(queue); GST_UNLOCK(queue);
gst_pad_set_eos (queue->srcpad);
// this return NULL shouldn't hurt anything...
return NULL; return NULL;
} }
while (!queue->level_buffers) { // if there's a pending state change for this queue or its manager, switch
STATUS("queue: %s U released lock\n"); // back to iterator so bottom half of state change executes
//g_cond_timed_wait (queue->emptycond, queue->emptylock, queue->timeval); if (GST_STATE_PENDING(queue) != GST_STATE_NONE_PENDING ||
if (GST_FLAG_IS_SET (queue->sinkpad, GST_PAD_EOS)) { GST_STATE_PENDING(GST_SCHEDULE(GST_ELEMENT(queue)->sched)->parent) != GST_STATE_NONE_PENDING)
gst_pad_set_eos (queue->srcpad); {
return NULL; GST_UNLOCK(queue);
cothread_switch(cothread_current_main());
} }
//FIXME need to signal other thread in case signals got lost?
g_cond_signal (queue->fullcond); g_cond_signal (queue->fullcond);
g_cond_wait (queue->emptycond, GST_OBJECT(queue)->lock); g_cond_wait (queue->emptycond, GST_OBJECT(queue)->lock);
// STATUS("queue: %s U- getting lock\n");
} }
front = queue->queue; front = queue->queue;
@ -344,32 +362,34 @@ gst_queue_get (GstPad *pad)
queue->queue = g_slist_remove_link (queue->queue, front); queue->queue = g_slist_remove_link (queue->queue, front);
g_slist_free (front); g_slist_free (front);
// if (queue->level_buffers < queue->max_buffers)
if (queue->level_buffers == queue->max_buffers)
{
GST_DEBUG (0,"queue: %s signalling fullcond\n", name);
g_cond_signal (queue->fullcond);
}
queue->level_buffers--; queue->level_buffers--;
// STATUS("%s: -\n");
GST_DEBUG (0,"(%s:%s)- ",GST_DEBUG_PAD_NAME(pad)); GST_DEBUG (0,"(%s:%s)- ",GST_DEBUG_PAD_NAME(pad));
if (queue->level_buffers < queue->max_buffers) {
// STATUS("%s: < \n");
g_cond_signal (queue->fullcond);
// STATUS("%s: << \n");
}
GST_UNLOCK(queue); GST_UNLOCK(queue);
// GST_DEBUG (0,"queue: %s pushing %d %p \n", name, queue->level_buffers, buf);
// gst_pad_push (queue->srcpad, buf);
// GST_DEBUG (0,"queue: %s pushing %d done \n", name, queue->level_buffers);
return buf; return buf;
/* unlock now */
} }
static GstElementStateReturn static GstElementStateReturn
gst_queue_change_state (GstElement *element) gst_queue_change_state (GstElement *element)
{ {
GstQueue *queue; GstQueue *queue;
GstElementStateReturn ret;
g_return_val_if_fail (GST_IS_QUEUE (element), GST_STATE_FAILURE); g_return_val_if_fail (GST_IS_QUEUE (element), GST_STATE_FAILURE);
queue = GST_QUEUE (element); queue = GST_QUEUE (element);
// lock the queue so another thread (not in sync with this thread's state)
// can't call this queue's _get (or whatever)
GST_LOCK (queue);
GST_DEBUG (0,"gstqueue: state pending %d\n", GST_STATE_PENDING (element)); GST_DEBUG (0,"gstqueue: state pending %d\n", GST_STATE_PENDING (element));
/* if going down into NULL state, clear out buffers*/ /* if going down into NULL state, clear out buffers*/
@ -380,9 +400,41 @@ gst_queue_change_state (GstElement *element)
/* if we haven't failed already, give the parent class a chance to ;-) */ /* if we haven't failed already, give the parent class a chance to ;-) */
if (GST_ELEMENT_CLASS (parent_class)->change_state) if (GST_ELEMENT_CLASS (parent_class)->change_state)
return GST_ELEMENT_CLASS (parent_class)->change_state (element); {
gboolean valid_handler = FALSE;
guint state_change_id = gtk_signal_lookup("state_change", GTK_OBJECT_TYPE(element));
return GST_STATE_SUCCESS; // determine whether we need to block the parent (element) class'
// STATE_CHANGE signal so we can UNLOCK before returning. we block
// it if we could find the state_change signal AND there's a signal
// handler attached to it.
//
// note: this assumes that change_state() *only* emits state_change signal.
// if element change_state() emits other signals, they need to be blocked
// as well.
if (state_change_id &&
gtk_signal_handler_pending(GTK_OBJECT(element), state_change_id, FALSE))
valid_handler = TRUE;
if (valid_handler)
gtk_signal_handler_block(GTK_OBJECT(element), state_change_id);
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
if (valid_handler)
gtk_signal_handler_unblock(GTK_OBJECT(element), state_change_id);
// UNLOCK, *then* emit signal (if there's one there)
GST_UNLOCK(queue);
if (valid_handler)
gtk_signal_emit(GTK_OBJECT (element), state_change_id, GST_STATE(element));
}
else
{
ret = GST_STATE_SUCCESS;
GST_UNLOCK(queue);
}
return ret;
} }
@ -400,9 +452,9 @@ gst_queue_set_arg (GtkObject *object, GtkArg *arg, guint id)
case ARG_MAX_LEVEL: case ARG_MAX_LEVEL:
queue->max_buffers = GTK_VALUE_INT (*arg); queue->max_buffers = GTK_VALUE_INT (*arg);
break; break;
case ARG_BLOCK: // case ARG_BLOCK:
queue->block = GTK_VALUE_BOOL (*arg); // queue->block = GTK_VALUE_BOOL (*arg);
break; // break;
default: default:
break; break;
} }
@ -425,9 +477,9 @@ gst_queue_get_arg (GtkObject *object, GtkArg *arg, guint id)
case ARG_MAX_LEVEL: case ARG_MAX_LEVEL:
GTK_VALUE_INT (*arg) = queue->max_buffers; GTK_VALUE_INT (*arg) = queue->max_buffers;
break; break;
case ARG_BLOCK: // case ARG_BLOCK:
GTK_VALUE_BOOL (*arg) = queue->block; // GTK_VALUE_BOOL (*arg) = queue->block;
break; // break;
default: default:
arg->type = GTK_TYPE_INVALID; arg->type = GTK_TYPE_INVALID;
break; break;

View file

@ -61,7 +61,7 @@ struct _GstQueue {
gint level_buffers; /* number of buffers queued here */ gint level_buffers; /* number of buffers queued here */
gint max_buffers; /* maximum number of buffers queued here */ gint max_buffers; /* maximum number of buffers queued here */
gboolean block; /* if set to FALSE, _get returns NULL if queue empty */ // gboolean block; /* if set to FALSE, _get returns NULL if queue empty */
gint level_bytes; /* number of bytes queued here */ gint level_bytes; /* number of bytes queued here */
gint size_buffers; /* size of queue in buffers */ gint size_buffers; /* size of queue in buffers */
gint size_bytes; /* size of queue in bytes */ gint size_bytes; /* size of queue in bytes */