[gst-devel] Multi-threaded queueing locking issues

Matt Howell matth at ridgerun.com
Tue Mar 20 23:10:59 CET 2001

stuff deleted, comments added...

> > > i agree.  you shouldn't have to use the timed-wait stuff in this case.  also,
> > > you don't want to use system kill/signal to fix thread scheduling.
> > >
> > > i was thinking something like this: (pseudo-code :)
> > > gstpad.c::queue_get() - to the while loop, add
> > >   // somehow, get pendingState
> > >   if (pendingState == PAUSED) { GST_UNLOCK(lock); return NULL; }
> > >
> > > gstpad.c::queue_chain() - to the while loop, add the same thing
> > >   if (pendingState == PAUSED) { GST_UNLOCK(lock); return NULL; }
> > >
> > > gstpad.c::change_state() - add
> > >   if (GST_STATE_PENDING (element) == GST_STATE_PAUSED)
> > >   {
> > >     GST_LOCK(queue);
> > >     signal(emptycond); // wake up waiting queue_get()'s, if any
> > >     signal(fullcond); // wake up waiting queue_chain()'s, if any
> > >     GST_UNLOCK(queue);
> > >   }
> > >
> > > gstscheduler.c::chain_wrapper() - i'm not sure it's necessary, but probably add:
> > >
> > >   if (pendingState == PAUSED) break;  // stop calling pad_pull()'s,
> > >
> > > there may be other places in gstscheduler.c code to do similar.
> > >
> > > thoughts?
> >
> > The problem is that the above solution only works for queues, and makes
> > them intimately aware of the threads and vice versa.  The queues aren't
> > the only things that are going to be blocking.  Anything that attaches to
> > a file descriptor (file, network, etc.) could also block, and we have to
> > come up with some way of interrupting them as well.
> >
> > I'm not sure how one would go about interrupting a blocking read, or the
> > best way to set it up to time out.  I'd like to find a way (even if it's
> > element-specific in the form of a special interuption callback) to
> > interrupt them explicitely, rather than making them spin in a timeout
> > loop.  That forces them to use select, and other messy stuff.

i don't think the check in gstpad.c::change_state() is such a bad thing.  it's
called in the context of the thread (the pipeline?) which was instructed to
go to the pause state by the app, right?  it's in gstpad.c because it's pad-
centric...  maybe i don't understand your concern here.

it doesn't seem bad to use different ways to wake threads that are blocked
for different reasons.  if a thread is blocked because of g_cond_wait(), it
should be waked with g_cond_signal().  it it's blocked because of a system
call (e.g., blocking read) use the system kill().

the spin / timeout stuff could be encapsulated in an element's change_state().
(this requires that all elements that can block on a system call implement
their own change_state().)  as brent points out below, this could use
pthread_cancel() (assuming these elements are in a pthread) as a last resort
(since it is a bit destructive ;-).


> I agree, it would be nice to allow elements to call blocking I/O functions (instead
> of using poll).  It's more elegant and probably more general.  However, I'm not sure
> yet how to cleanly close such elements if they end up blocking indefinitely.  There
> should be a standard computer science answer to this question.  I imagine its an
> issue for many projects.  It may be as simple as pthread_cancel().
> >From the man page...
>        POSIX specifies that a number of system calls (basically, all system  calls
> that  may  block,  such  as  read(2),
>        write(2),  wait(2),  etc.) and library functions that may call these system
> calls (e.g.  fprintf(3)) are cancella­
>        tion points.  LinuxThreads is not yet integrated enough with the C library to
> implement this, and thus none of the
>        C library functions is a cancellation point.
>        For  system  calls  at least, there is a workaround. Cancellation requests are
> transmitted to the target thread by
>        sending it a signal. That signal will interrupt all blocking system calls,
> causing them to return immediately with
>        the  EINTR  error.  So, checking for cancellation during a read system call,
> for instance, can be achieved as fol­
>        lows:
>               pthread_testcancel();
>               retcode = read(fd, buffer, length);
>               pthread_testcancel();
> >
> > If it is feasible to construct an interrupt mechanism for every element
> > that might block, then that may be the solution.  But... note that these
> > interrupt functions will be run from the top-half's context, not the
> > thread's context.  That means that they have to be constructed in a
> > thread-safe manner, which for regular sources is a bit of a stretch.  The
> > other trick is figuring out which of the elements is currently blocked.
> > The iterate() code doesn't keep track of that, and can't anyway without a
> > fair amount of overhead shoving a pointer to the next element somewhere
> > during every switch.
> >
> > The advantage of the thread-based signaling approach is that it works
> > transparently on all elements, because it simply switches them out.  One
> > other potential issue with that is what happens if the I/O comes back from
> > a filehandle while the read or write is switched out...?
> For best results, any element that may block should be on a Linux pthread.  This
> avoids the scenario just described (I/O returns to switched-out cothread).  It
> provides a termination mechanism: pthread_cancel().  And it allows other elements
> (and the client application) to run while the element is blocked.
> To some degree, the autoplugger does this for you by placing thread-bins around
> certain elements.

More information about the gstreamer-devel mailing list