mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 00:36:51 +00:00
126 lines
5.2 KiB
Text
126 lines
5.2 KiB
Text
|
-----------------------------------------------------------
|
||
|
- GStreamer Scheduling / Synchronization (incsched) Notes -
|
||
|
-----------------------------------------------------------
|
||
|
|
||
|
These notes describe deadlock scenarios and proposed solutions for
|
||
|
GStreamer. This will be implemented in the INCSCHED1 branch.
|
||
|
|
||
|
I. Miscelaneous proposals
|
||
|
II. Liveness problems (sometimes deadlock ;) and propsed 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)
|