-----------------------------------------------------------
- GStreamer Scheduling / Synchronization (incsched) Notes -
-----------------------------------------------------------

These notes describe deadlock scenarios and proposed solutions for
GStreamer.  This will be implemented in the INCSCHED1 branch.

I.   Miscellaneous proposals
II.  Liveness problems (sometimes deadlock ;) and proposed solutions
III. State transition approach and responsibility

MattH.

--------------------------------
-   I. Miscalenous proposals   -
--------------------------------

1. Change the names of GstThread and GstQueue to GstPtThread and GstPtQueue
   for pthread versions of Thread and Queue.

2. Change GstPtQueue to check its pads' peers' managers and make sure
   they are different.  If not, fail and generate error message.  (This
   ensures a GstPtQueue straddles a pthread boundary.)

3. Change state transitions to NULL <-> READY <-> PAUSED <-> PLAYING.


---------------------------------------------------
-  II. Deadlock Scenarios and Proposed Solutions  -
-      (in the order they will be implemented)    -
---------------------------------------------------

1. A downstream element "waits" for a buffer from its upstream element,
   a state change happens and "pauses" the upstream element -- the
   downstream element is blocked and cannot execute its change_state.

   Note that this can only happen within a single GstPtQueue!  Either a
   downstream element calls Pull, finds no buffer, and does a
   wait_cond(new buffer) or an upstream element calls Push, finds no
   room, and does a wait_cond(new room).  Thus, GstPtQueue contains all
   the cond_wait / signal code. 
 
   => The managing container (thread, pipeline) "wakes" up any sleep
      conditions of its "bottom half".  (In the scenario described, it
      wakes the blocked downstream element's call to Pull.)  The GstPtQueue
      cond_wait section determines that it woke up due to a pending state
      change and does a cothread_switch(0) to return to the main loop,
      which then executes the state transition.

      Note that a managing container will have only one sleep condition
      in its "bottom half." 


2. Element "blocked" on getting I/O and cannot execute its change_state.

   => We will provide an I/O library for the elements to use that does
      not actually block.  (A retry-loop with timeout or select() on
      2 -- or more -- file descriptors: one the one you want I/O from, 
      the other one that GStreamer uses to "wake" everyone up.)  The
      I/O library determines that it was woken due to a pending state
      change and does a cothread_switch(0) to return to the main loop,
      which then executes the state transition.

      Note that a managing container will have only one elements in
      the middle of doing blocking I/O.


3. Element using a library (code out of its control) which blocks for
   some reason (e.g., using real blocking I/O) so main loop never gets
   run to execute change_state.

   => Build in some timeout in the manging container (the "top half")
      when waiting for bottom half to respond to pending state.  If
      managing container times out, kill the element's thread with a
      signal (or series of signals -- escalating priority).  This
      requires that the element (the "bottom half") have matching
      signal handler(s) that execute(s) the state-transition.


--------------------------------------------------------
- III.  State-transition Approach and Responsibility   -
--------------------------------------------------------

A. The "top half" context of the managing container.  (This is likely the
   context of the application.)

   Call change_state on the managing container (GstPipeline, GstPtThread).
   If its "bottom half" (main_loop) is asleep, signal the condition to
   wake it up.  Then do a cond_wait for the "bottom half" to execute the
   state transition and return (once the state has been changed).

 
B. The main_loop (the "bottom half") of the managing container. 

   Needs to check for pending state transition after every switch back from
   one of its elements.  If a pending state is found, it calls change_state
   on each of its elements, signals the "top half" that the state has been
   changed, then continues executing the plan (if Playing) or puts itself
   to sleep (Paused, Ready).


C. Element.

   Implement a change_state function to make transition for that element.
   The elements' change_state is what actually changes the state variable
   and notifies the scheduler that the state was changed.  This function
   may also do things like close or open resources.
   
   NOTE: when an element goes through certain state transitions (e.g., from
   Paused to Ready) its state (stack) will be wiped out.  If it wants to
   preserve any state or data, it needs to store the information in a safe
   place.


D. Cothread Scheduler.

   Gets notified of state transition by elements' change_state functions
   then (re)set the plan accordingly.  Assuming
   NULL <-> READY <-> PAUSED <-> PLAYING, some would be

   + Paused -> Playing: jump back where you were in the Plan and continue
                        its execution

   + Ready -> Paused: reset the cothread pointers foreach cothread in the 
                      Plan (don't run)
