| 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. |
| |
| This design decision has implications 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. |
| |
| Refcounting: |
| |
| All shared 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. |
| |
| Refcounting is also used to ensure that mutable data structures are only |
| modified when they are owned by the calling code. |
| |
| It is a requirement that when two threads have a handle on an object, the |
| refcount must be more than 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. |
| |
| Shared data structures and writability: |
| |
| 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. |
| |
| Each thread having a refcount to the object can safely read from the object. |
| but modifications made to the object should be preceded with a |
| _get_writable() 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 modifiable 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. |
| |
| 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 |
| cases _get_writable() can avoid a real copy because the calling method is the |
| only one holding a reference, which makes read/write very cheap. |
| |
| The drawback is that sometimes 1 needless copy can be done. This would happen |
| when N threads call _get_writable() at the same time, all seeing that N |
| references are held on 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. |
| |
| Mutable substructures: |
| |
| Special techniques are necessary to ensure the consistency of compound shared |
| objects. As mentioned above, shared objects need to have a reference count of |
| 1 if they are to be modified. Implicit in this assumption is that all parts of |
| the shared object belong only to the object. For example, a GstStructure in |
| one GstCaps object should not belong to any other GstCaps object. This |
| condition suggests a parent-child relationship: structures can only be added |
| to parent object if they do not already have a parent object. |
| |
| In addition, these substructures must not be modified while more than one code |
| segment has a reference on the parent object. For example, if the user creates |
| a GstStructure, adds it to a GstCaps, and the GstCaps is then referenced by |
| other code segments, the GstStructure should then become immutable, so that |
| changes to that data structure do not affect other parts of the code. This |
| means that the child is only mutable when the parent's reference count is 1, |
| as well as when the child structure has no parent. |
| |
| The general solution to this problem is to include a field in child structures |
| pointing to the parent's atomic reference count. When set to NULL, this |
| indicates that the child has no parent. Otherwise, procedures that modify the |
| child structure must check if the parent's refcount is 1, and otherwise must |
| cause an error to be signaled. |
| |
| Note that this is an internal implementation detail; application or plugin |
| code that calls _get_writable() on an object is guaranteed to receive an |
| object of refcount 1, which must then be writable. The only trick is that a |
| pointer to a child structure of an object is only valid while the calling code |
| has a reference on the parent object, because the parent is the owner of the |
| child. |
| |
| 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. |
| |
| 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 can be done with recursive locks or regular mutexes. Object |
| locks in GStreamer are implemented with mutexes which cause deadlocks when |
| locked recursively from the same thread. This is done because regular mutexes |
| are cheaper. |
| |
| 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. |
| |
| The advantages are mostly speed related since there are no heavyweight locks |
| involved. Most of these 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 |
| small fixed size objects in a memchunk. They can also be used to implement a |
| lockfree list or stack. |
| |
| Compare and swap |
| |
| As part of the atomic operations, compare-and-swap (CAS) can be used to access |
| or update a single property or pointer in an object without having to take a |
| lock. |
| |
| This technique is currently not used in GStreamer but might be added in the |
| future in performance critical places. |
| |
| |
| Objects |
| ~~~~~~~ |
| |
| * Locking involved: |
| |
| - atomic operations for refcounting |
| - object locking |
| |
| 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_OBJECT_LOCK() and GST_OBJECT_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. |
| |
| 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 through 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. |
| |
| The float/sink process is very useful when initializing elements that will |
| then be placed under control of a parent. The floating ref keeps the object |
| alive until it is parented, and once the object is parented you can forget |
| about it. |
| |
| also see part-relations.txt |
| |
| * parent-child relations |
| |
| One can create parent-child relationships with the _object_set_parent() |
| method. This method refs and 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 responsibilities 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. |
| - 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. |
| - remove the object from the list. |
| |
| also see part-relations.txt |
| |
| * 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" or any other 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. |
| |
| struct _GstRealPad { |
| ... |
| /*< public >*/ /* with LOCK */ |
| ... |
| GstPadDirection direction; |
| ... |
| }; |
| |
| #define GST_RPAD_DIRECTION(pad) (GST_REAL_PAD_CAST(pad)->direction) |
| |
| Accessing the property is therefore allowed with the following code example: |
| |
| GST_OBJECT_LOCK (pad); |
| direction = GST_RPAD_DIRECTION (pad); |
| GST_OBJECT_UNLOCK (pad); |
| |
| * Property lifetime |
| |
| All properties requiring a lock can change after releasing the associated |
| lock. This means that 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 acquired 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. |
| |
| Most object provide a _get_<property>() method to get a copy or refcounted |
| instance of the property value. The caller should not wory about any locks |
| but should unref/free the object after usage. |
| |
| 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. |
| |
| GST_OBJECT_LOCK (pad); |
| peer = GST_RPAD_PEER (pad); |
| if (peer) |
| gst_object_ref (GST_OBJECT (peer)); |
| GST_OBJECT_UNLOCK (pad); |
| ... use peer ... |
| |
| 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. |
| |
| The following code is equivalent to the above but with using the functions |
| to access object properties. |
| |
| peer = gst_pad_get_peer (pad); |
| if (peer) { |
| ... use peer ... |
| |
| gst_object_unref (GST_OBJECT (peer)); |
| } |
| |
| Example: |
| |
| Accessing the name of an object makes a copy of the name. The caller of the |
| function should g_free() the name after usage. |
| |
| GST_OBJECT_LOCK (object) |
| name = g_strdup (GST_OBJECT_NAME (object)); |
| GST_OBJECT_UNLOCK (object) |
| ... use name ... |
| |
| g_free (name); |
| |
| or: |
| |
| name = gst_object_get_name (object); |
| |
| ... use name ... |
| |
| g_free (name); |
| |
| |
| * Accessor methods |
| |
| For aplications it is encouraged to use the public methods of the object. Most |
| useful 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 caller 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: |
| |
| - 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. |
| |
| 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 |
| potentially 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. |
| |
| GST_OBJECT_LOCK (lock); |
| /* grab list and cookie */ |
| cookie = object->list_cookie; |
| list = object-list; |
| while (list) { |
| GstObject *item = GST_OBJECT (list->data); |
| /* need to ref the item before releasing the lock */ |
| gst_object_ref (item); |
| GST_OBJECT_UNLOCK (lock); |
| |
| ... use/change item here... |
| |
| /* release item here */ |
| gst_object_unref (item); |
| |
| GST_OBJECT_LOCK (lock); |
| if (cookie != object->list_cookie) { |
| /* handle rollback caused by concurrent modification |
| * of the list here */ |
| |
| ...rollback changes to items... |
| |
| /* grab new cookie and list */ |
| cookie = object->list_cookie; |
| list = object->list; |
| } |
| else { |
| list = g_list_next (list); |
| } |
| } |
| GST_OBJECT_UNLOCK (lock); |
| |
| * GstIterator |
| |
| GstIterator provides an easier way of retrieving elements in a concurrent |
| list. The following code example is equivalent to the previous example. |
| |
| Example: |
| |
| it = _get_iterator(object); |
| while (!done) { |
| switch (gst_iterator_next (it, &item)) { |
| case GST_ITERATOR_OK: |
| |
| ... use/change item here... |
| |
| /* release item here */ |
| gst_object_unref (item); |
| break; |
| case GST_ITERATOR_RESYNC: |
| /* handle rollback caused by concurrent modification |
| * of the list here */ |
| |
| ...rollback changes to items... |
| |
| /* resync iterator to start again */ |
| gst_iterator_resync (it); |
| break; |
| case GST_ITERATOR_DONE: |
| done = TRUE; |
| break; |
| } |
| } |
| gst_iterator_free (it); |
| |