mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-18 04:05:34 +00:00
docs/design/part-MT-refcounting.txt: Fill to 80 columns.
Original commit message from CVS: 2005-02-10 Andy Wingo <wingo@pobox.com> * docs/design/part-MT-refcounting.txt: Fill to 80 columns.
This commit is contained in:
parent
6e0dfb6f50
commit
b1d9edff2f
2 changed files with 131 additions and 122 deletions
|
@ -1,5 +1,7 @@
|
|||
2005-02-10 Andy Wingo <wingo@pobox.com>
|
||||
|
||||
* docs/design/part-MT-refcounting.txt: Fill to 80 columns.
|
||||
|
||||
* gst/gstcaps.h:
|
||||
* gst/gstcaps.c (gst_caps_get_writable): Change from
|
||||
copy_on_write.
|
||||
|
|
|
@ -1,52 +1,53 @@
|
|||
Conventions for thread a safe API
|
||||
---------------------------------
|
||||
|
||||
The GStreamer API is designed to be thread safe. This means that API functions can be
|
||||
called from multiple threads at the same time. GStreamer internally uses threads to
|
||||
perform the data passing and various asynchronous services such as the clock can also
|
||||
use threads.
|
||||
The GStreamer API is designed to be thread safe. This means that API functions
|
||||
can be called from multiple threads at the same time. GStreamer internally uses
|
||||
threads to perform the data passing and various asynchronous services such as
|
||||
the clock can also use threads.
|
||||
|
||||
This design decision has implication for the usage of the API and the objects which
|
||||
this document explains.
|
||||
This design decision has implication for the usage of the API and the objects
|
||||
which this document explains.
|
||||
|
||||
MT safety techniques
|
||||
--------------------
|
||||
|
||||
Several design patterns are used to guarantee object consistency in GStreamer. This
|
||||
is an overview of the methods used in various GStreamer subsystems.
|
||||
Several design patterns are used to guarantee object consistency in GStreamer.
|
||||
This is an overview of the methods used in various GStreamer subsystems.
|
||||
|
||||
Refcounting
|
||||
|
||||
All object have a refcount associated with them. Each reference obtained to the
|
||||
object should increase the refcount and each reference lost should decrease the
|
||||
refcount.
|
||||
All objects have a refcount associated with them. Each reference obtained to
|
||||
the object should increase the refcount and each reference lost should
|
||||
decrease the refcount.
|
||||
|
||||
The refcounting is used to make sure that when another thread destroys the object,
|
||||
the ones which still hold a reference to the object do not read from invalid
|
||||
memory when accessing the object.
|
||||
The refcounting is used to make sure that when another thread destroys the
|
||||
object, the ones which still hold a reference to the object do not read from
|
||||
invalid memory when accessing the object.
|
||||
|
||||
It is a requirement that when two threads have a handle to an object, the
|
||||
refcount must be one, this means that when one thread passes an object to another
|
||||
thread it must increase the refcount. This requirement makes sure that one
|
||||
thread cannot suddenly dispose the object making the other thread crash when it
|
||||
tries to access the pointer to invalid memory.
|
||||
refcount must be one, this means that when one thread passes an object to
|
||||
another thread it must increase the refcount. This requirement makes sure that
|
||||
one thread cannot suddenly dispose the object making the other thread crash
|
||||
when it tries to access the pointer to invalid memory.
|
||||
|
||||
Copy-on-write:
|
||||
|
||||
All object have a refcount associated with them. Each reference obtained to the
|
||||
object should increase the refcount and each reference lost should decrease the
|
||||
refcount.
|
||||
All object have a refcount associated with them. Each reference obtained to
|
||||
the object should increase the refcount and each reference lost should
|
||||
decrease the refcount.
|
||||
|
||||
Each thread having a refcount to the object can safely read from the object. but
|
||||
modifications made to the object should be preceeded with a _copy_on_write()
|
||||
function call. This function will check the refcount of the object and if the
|
||||
object is referenced by more than one instance, a copy is made of the object that
|
||||
is then by definition only referenced from the calling thread. This new copy
|
||||
is then modifyable without being visible to other refcount holders.
|
||||
Each thread having a refcount to the object can safely read from the object.
|
||||
but modifications made to the object should be preceeded with a
|
||||
_copy_on_write() function call. This function will check the refcount of the
|
||||
object and if the object is referenced by more than one instance, a copy is
|
||||
made of the object that is then by definition only referenced from the calling
|
||||
thread. This new copy is then modifyable without being visible to other
|
||||
refcount holders.
|
||||
|
||||
This technique is used for information objects that, once created, never change
|
||||
their values. The lifetime of these objects is generally short, the objects are
|
||||
usually simple and cheap to copy/create.
|
||||
This technique is used for information objects that, once created, never
|
||||
change their values. The lifetime of these objects is generally short, the
|
||||
objects are usually simple and cheap to copy/create.
|
||||
|
||||
The advantage of this method is that no reader/writers locks are needed. all
|
||||
threads can concurrently read but writes happen locally on a new copy. In most
|
||||
|
@ -54,36 +55,38 @@ Copy-on-write:
|
|||
only one holding a reference, wich makes read/writes very cheap.
|
||||
|
||||
The drawback is that sometimes 1 needless copy can be done. This would happen
|
||||
when N threads call _copy_on_write() at the same time, all seeing that N references
|
||||
are held to the object. In this case 1 copy too many will be done. This is not
|
||||
a problem in any practical situation because the copy operation is fast.
|
||||
when N threads call _copy_on_write() at the same time, all seeing that N
|
||||
references are held to the object. In this case 1 copy too many will be done.
|
||||
This is not a problem in any practical situation because the copy operation is
|
||||
fast.
|
||||
|
||||
Object locking:
|
||||
|
||||
For objects that contain state information and generally have a longer lifetime,
|
||||
object locking is used to update the information contained in the object.
|
||||
For objects that contain state information and generally have a longer
|
||||
lifetime, object locking is used to update the information contained in the
|
||||
object.
|
||||
|
||||
All readers and writers acquire the lock before accessing the object. Only one
|
||||
thread is allowed access the protected structures at a time.
|
||||
|
||||
Object locking is used for all objects extending from GstObject such as GstElement,
|
||||
GstPad.
|
||||
Object locking is used for all objects extending from GstObject such as
|
||||
GstElement, GstPad.
|
||||
|
||||
Atomic operations
|
||||
|
||||
Atomic operations are operations that are performed as one consistent operation
|
||||
even when executed by multiple threads. They do however not use the conventional
|
||||
aproach of using mutexes to protect the critical section but rely on CPU features
|
||||
and instructions.
|
||||
Atomic operations are operations that are performed as one consistent
|
||||
operation even when executed by multiple threads. They do however not use the
|
||||
conventional aproach of using mutexes to protect the critical section but rely
|
||||
on CPU features and instructions.
|
||||
|
||||
The advantages are mostly speed related since there are no heavyweight locks
|
||||
The advantages are mostly speed related since there are no heavyweight locks
|
||||
involved. Most of this instructions also do not cause a context switch in case
|
||||
of concurrent access but use a retry mechanism or spinlocking.
|
||||
|
||||
Disadvantages are that each of these instructions usually cause a cache flush
|
||||
on multi-CPU machines when two processors perform concurrent access.
|
||||
|
||||
Atomic operations are generally used for refcounting and for the allocation of
|
||||
Atomic operations are generally used for refcounting and for the allocation of
|
||||
small fixed size objects in a memchunk. They can also be used to implement a
|
||||
lockfree list or stack.
|
||||
|
||||
|
@ -99,72 +102,74 @@ Objects
|
|||
All objects should have a lock associated with them. This lock is used to keep
|
||||
internal consistency when multiple threads call API function on the object.
|
||||
|
||||
For objects that extend the GStreamer base object class this lock can be obtained with
|
||||
the macros GST_LOCK() and GST_UNLOCK(). For other object that do not extend from the
|
||||
base GstObject class these macros can be different.
|
||||
For objects that extend the GStreamer base object class this lock can be
|
||||
obtained with the macros GST_LOCK() and GST_UNLOCK(). For other object that do
|
||||
not extend from the base GstObject class these macros can be different.
|
||||
|
||||
* refcounting
|
||||
|
||||
All new objects created have the FLOATING flag set. This means that the object is
|
||||
not owned or managed yet by anybody other than the one holding a reference to
|
||||
the object. The object in this state has a reference count of 1.
|
||||
All new objects created have the FLOATING flag set. This means that the object
|
||||
is not owned or managed yet by anybody other than the one holding a reference
|
||||
to the object. The object in this state has a reference count of 1.
|
||||
|
||||
Various object methods can take ownership of another object, this means that after
|
||||
calling a method on object A with an object B as an argument, the object B is made
|
||||
sole property of object A. This means that after the method call you are not allowed
|
||||
to access the object anymore unless you keep an extra reference to the object.
|
||||
An example of such a method is the _bin_add() method. As soon as this function is
|
||||
called in a Bin, the element passed as an argument is owned by the bin and you
|
||||
are not allowed to access it anymore without taking a _ref() before adding it
|
||||
to the bin. The reason being that after the _bin_add() call disposing the bin
|
||||
also destroys the element.
|
||||
Various object methods can take ownership of another object, this means that
|
||||
after calling a method on object A with an object B as an argument, the object
|
||||
B is made sole property of object A. This means that after the method call you
|
||||
are not allowed to access the object anymore unless you keep an extra
|
||||
reference to the object. An example of such a method is the _bin_add() method.
|
||||
As soon as this function is called in a Bin, the element passed as an argument
|
||||
is owned by the bin and you are not allowed to access it anymore without
|
||||
taking a _ref() before adding it to the bin. The reason being that after the
|
||||
_bin_add() call disposing the bin also destroys the element.
|
||||
|
||||
Taking ownership of an object happens trough the process of "sinking" the object.
|
||||
the _sink() method on an object will decrease the refcount of the object if the
|
||||
FLOATING flag is set. The act of taking ownership of an object is then performed
|
||||
as a _ref() followed by a _sink() call on the object.
|
||||
Taking ownership of an object happens trough the process of "sinking" the
|
||||
object. the _sink() method on an object will decrease the refcount of the
|
||||
object if the FLOATING flag is set. The act of taking ownership of an object
|
||||
is then performed as a _ref() followed by a _sink() call on the object.
|
||||
|
||||
Sinking objects is very usefull because you don't have to worry about the lifetime
|
||||
of these sunken objects anymore since it is managed by the owner element.
|
||||
Sinking objects is very usefull because you don't have to worry about the
|
||||
lifetime of these sunken objects anymore since it is managed by the owner
|
||||
element.
|
||||
|
||||
* parent-child relations
|
||||
|
||||
One can create parent child relationships with the _object_set_parent() method. This
|
||||
method sinks the object and assigns its parent property to that of the managing
|
||||
parent.
|
||||
One can create parent child relationships with the _object_set_parent()
|
||||
method. This method sinks the object and assigns its parent property to that
|
||||
of the managing parent.
|
||||
|
||||
The child is said to have a weak link to the parent since the refcount of the parent
|
||||
is not increased in this process. This means that if the parent is disposed it has
|
||||
to unset itself as the parent of the object before disposing itself, else the child
|
||||
object holds a parent pointer to invalid memory.
|
||||
The child is said to have a weak link to the parent since the refcount of the
|
||||
parent is not increased in this process. This means that if the parent is
|
||||
disposed it has to unset itself as the parent of the object before disposing
|
||||
itself, else the child object holds a parent pointer to invalid memory.
|
||||
|
||||
The responsabilites for an object that sinks other objects are summarised as:
|
||||
|
||||
- taking ownership of the object
|
||||
- call _object_set_parent() to set itself as the object parent, this call will
|
||||
_ref() and _sink() the object.
|
||||
- call _object_set_parent() to set itself as the object parent, this call
|
||||
will _ref() and _sink() the object.
|
||||
- keep reference to object in a datastructure such as a list or array.
|
||||
|
||||
- on dispose
|
||||
- call _object_unparent() to reset the parent property and unref the object.
|
||||
- call _object_unparent() to reset the parent property and unref the
|
||||
object.
|
||||
- remove the object from the list.
|
||||
|
||||
|
||||
* Properties
|
||||
|
||||
Most objects also expose state information with public properties in the object. Two
|
||||
types of properties might exist: accessible with or without holding the object lock.
|
||||
All properties should only be accessed with their corresponding macros.
|
||||
The public object properties are marked in the .h files with /*< public >*/. The
|
||||
public properties that require a lock to be held are marked with
|
||||
/*< public >*/ /* with <lock_type> */, where <lock_type> can be "LOCK" or "STATE_LOCK"
|
||||
to mark the type(s) of lock to be held.
|
||||
Most objects also expose state information with public properties in the
|
||||
object. Two types of properties might exist: accessible with or without
|
||||
holding the object lock. All properties should only be accessed with their
|
||||
corresponding macros. The public object properties are marked in the .h files
|
||||
with /*< public >*/. The public properties that require a lock to be held are
|
||||
marked with /*< public >*/ /* with <lock_type> */, where <lock_type> can be
|
||||
"LOCK" or "STATE_LOCK" to mark the type(s) of lock to be held.
|
||||
|
||||
Example:
|
||||
|
||||
in GstPad there is a public property "direction". It can be found in the section
|
||||
marked as public and requiring the LOCK to be held. There exists also a macro
|
||||
to access the property.
|
||||
in GstPad there is a public property "direction". It can be found in the
|
||||
section marked as public and requiring the LOCK to be held. There exists
|
||||
also a macro to access the property.
|
||||
|
||||
struct _GstRealPad {
|
||||
...
|
||||
|
@ -174,7 +179,7 @@ Objects
|
|||
...
|
||||
};
|
||||
|
||||
#define GST_RPAD_DIRECTION(pad) (GST_REAL_PAD_CAST(pad)->direction)
|
||||
#define GST_RPAD_DIRECTION(pad) (GST_REAL_PAD_CAST(pad)->direction)
|
||||
|
||||
Accessing the property is therefore allowed with the following code example:
|
||||
|
||||
|
@ -184,22 +189,22 @@ Objects
|
|||
|
||||
* Property lifetime
|
||||
|
||||
All properties requiring a lock can change after releasing the associated lock.
|
||||
This means that as soon as long as you hold the lock, the state of the object
|
||||
regarding the locked properties is consistent with the information obtained. As
|
||||
soon as the lock is released, any values required from the properties might not
|
||||
be valid anymore and can as best be described as a snapshot of the state when
|
||||
the lock was held.
|
||||
All properties requiring a lock can change after releasing the associated
|
||||
lock. This means that as soon as long as you hold the lock, the state of the
|
||||
object regarding the locked properties is consistent with the information
|
||||
obtained. As soon as the lock is released, any values required from the
|
||||
properties might not be valid anymore and can as best be described as a
|
||||
snapshot of the state when the lock was held.
|
||||
|
||||
This means that all properties that require access beyond the scope of the critial
|
||||
section should be copied or refcounted before releasing the lock.
|
||||
This means that all properties that require access beyond the scope of the
|
||||
critial section should be copied or refcounted before releasing the lock.
|
||||
|
||||
Example:
|
||||
|
||||
the following example correctly gets the peer pad of an element. It is required
|
||||
to increase the refcount of the peer pad because as soon as the lock is released,
|
||||
the peer could be unreffed and disposed, making the pointer obtained in the critical
|
||||
section point to invalid memory.
|
||||
the following example correctly gets the peer pad of an element. It is
|
||||
required to increase the refcount of the peer pad because as soon as the
|
||||
lock is released, the peer could be unreffed and disposed, making the
|
||||
pointer obtained in the critical section point to invalid memory.
|
||||
|
||||
GST_LOCK (pad);
|
||||
peer = GST_RPAD_PEER (pad);
|
||||
|
@ -211,49 +216,51 @@ Objects
|
|||
if (peer)
|
||||
gst_object_unref (GST_OBJECT (peer));
|
||||
|
||||
Note that after releasing the lock the peer might not actually be the peer anymore
|
||||
of the pad. If you need to be sure it is, you need to extend the critical section
|
||||
to include the operations on the peer.
|
||||
Note that after releasing the lock the peer might not actually be the peer
|
||||
anymore of the pad. If you need to be sure it is, you need to extend the
|
||||
critical section to include the operations on the peer.
|
||||
|
||||
* Accessor methods
|
||||
|
||||
For aplications it is encouraged to use the public methods of the object. Most usefull
|
||||
operations can be performed with the methods so it is seldom required to access the
|
||||
public fields manually.
|
||||
For aplications it is encouraged to use the public methods of the object. Most
|
||||
usefull operations can be performed with the methods so it is seldom required
|
||||
to access the public fields manually.
|
||||
|
||||
All accessor methods that return an object should increase the refcount of the returned
|
||||
object. The called should _unref() the object after usage. Each method should state this
|
||||
refcounting policy in the documentation.
|
||||
All accessor methods that return an object should increase the refcount of the
|
||||
returned object. The called should _unref() the object after usage. Each
|
||||
method should state this refcounting policy in the documentation.
|
||||
|
||||
* Accessing lists
|
||||
|
||||
If the object property is a list, concurrent list iteration is needed to get the
|
||||
contents of the list. GStreamer uses the cookie mechanism to mark the last update
|
||||
of a list. The list and the cookie are protected by the same lock. Each update to
|
||||
a list requires the following actions:
|
||||
If the object property is a list, concurrent list iteration is needed to get
|
||||
the contents of the list. GStreamer uses the cookie mechanism to mark the last
|
||||
update of a list. The list and the cookie are protected by the same lock. Each
|
||||
update to a list requires the following actions:
|
||||
|
||||
- acquire lock
|
||||
- update list
|
||||
- update cookie
|
||||
- release lock
|
||||
|
||||
Updating the cookie is usually done by incrementing its value by one. Since cookies
|
||||
use guint32 its wraparound is for all practical reasons is not a problem.
|
||||
Updating the cookie is usually done by incrementing its value by one. Since
|
||||
cookies use guint32 its wraparound is for all practical reasons is not a
|
||||
problem.
|
||||
|
||||
Iterating a list can safely be done by surrounding the list iteration with a
|
||||
lock/unlock of the lock.
|
||||
|
||||
In some cases it is not a good idea to hold the lock for a long time while iterating
|
||||
the list. The state change code for a bin in GStreamer, for example, has to iterate
|
||||
over each element and perform a blocking call on each of them causing infinite bin
|
||||
locking. In this case the cookie can be used to iterate a list.
|
||||
In some cases it is not a good idea to hold the lock for a long time while
|
||||
iterating the list. The state change code for a bin in GStreamer, for example,
|
||||
has to iterate over each element and perform a blocking call on each of them
|
||||
causing infinite bin locking. In this case the cookie can be used to iterate a
|
||||
list.
|
||||
|
||||
Example:
|
||||
|
||||
The following algorithm iterates a list and reverses the updates in the case a
|
||||
concurrent update was done to the list while iterating. The idea is that whenever
|
||||
we reacquire the lock, we check for updates to the cookie to decide if we are
|
||||
still iterating the right list.
|
||||
The following algorithm iterates a list and reverses the updates in the
|
||||
case a concurrent update was done to the list while iterating. The idea is
|
||||
that whenever we reacquire the lock, we check for updates to the cookie to
|
||||
decide if we are still iterating the right list.
|
||||
|
||||
GST_LOCK (lock);
|
||||
/* grab list and cookie */
|
||||
|
@ -289,8 +296,8 @@ Objects
|
|||
|
||||
* GstIterator
|
||||
|
||||
GstIterator provides an easier way of retrieving elements in a concurrent list.
|
||||
The followgin code example is equivalent to the previous example.
|
||||
GstIterator provides an easier way of retrieving elements in a concurrent
|
||||
list. The followgin code example is equivalent to the previous example.
|
||||
|
||||
Example:
|
||||
|
||||
|
|
Loading…
Reference in a new issue