ast_channel locking and reference tracking
- Creating Channels
- A channel is allocated using the ast_channel_alloc() function. When created, it is automatically inserted into the main channels hash table that keeps track of all active channels in the system. The hash key is based on the channel name. Because of this, if you want to change the name, you _must_ use ast_change_name(), not change the name field directly. When ast_channel_alloc() returns a channel pointer, you now hold a reference to that channel. In most cases this reference is given to ast_pbx_run().
- Channel Locking
- There is a lock associated with every ast_channel. It is allocated internally via astobj2. To lock or unlock a channel, you must use the ast_channel_lock() wrappers.
Previously, before ast_channel
was converted to astobj2
, the channel lock was used in some additional ways that are no longer necessary. Before, the only way to ensure that a channel did not disappear out from under you if you were working with a channel outside of the channel thread that owns it, was to hold the channel lock. Now, that is no longer necessary. You simply must hold a reference to the channel to ensure it does not go away.
The channel must be locked if you need to ensure that data that you reading from the channel does not change while you access it. Further, you must hold the channel lock if you are making a non-atomic change to channel data.
- Channel References
- There are multiple ways to get a reference to a channel. The first is that you hold a reference to a channel after creating it. The other ways involve using the channel search or the channel traversal APIs. These functions are the ast_channel_get_*() functions or ast_channel_iterator_*() functions. Once a reference is retrieved by one of these methods, you know that the channel will not go away. So, the channel should only get locked as needed for data access or modification. But, make sure that the reference gets released when you are done with it!
There are different things you can do when you are done with a reference to a channel. The first is to simply release the reference using ast_channel_unref()
. The other option is to call ast_channel_release()
. This function is generally used where ast_channel_free() was used in the past. The release function releases a reference as well as ensures that the channel is no longer in the global channels container. That way, the channel will get destroyed as soon as any other pending references get released.
- Exceptions to the rules
- Even though ast_channel is reference counted, there are some places where pointers to an ast_channel get stored, but the reference count does not reflect it. The reason is mostly historical. The only places where this happens should be places where because of how the code works, we _know_ that the pointer to the channel will get removed before the channel goes away. The main example of this is in channel drivers. Channel drivers generally store a pointer to their owner ast_channel in their technology specific pvt struct. In this case, the channel drivers _know_ that this pointer to the channel will be removed in time, because the channel's hangup callback gets called before the channel goes away.