Asterisk - The Open Source Telephony Project
18.5.0
|
Stasis introduces a number of objects, which are tightly related to one another. Because we rely on ref-counting for memory management, understanding these relationships is important to understanding this code.
The most troubling thing in this chart is the cyclic reference between stasis_topic and stasis_subscription. This is both unfortunate, and necessary. Topics need the subscription in order to dispatch messages; subscriptions need the topics to unsubscribe and check subscription status.
The cycle is broken by stasis_unsubscribe(). The unsubscribe will remove the topic's reference to a subscription. When the subcription is destroyed, it will remove its reference to the topic.
This means that until a subscription has be explicitly unsubscribed, it will not be destroyed. Neither will a topic be destroyed while it has subscribers. The destructors of both have assertions regarding this to catch ref-counting problems where a subscription or topic has had an extra ao2_cleanup().
The dispatch object is a transient object, which is posted to a subscription's taskprocessor to send a message to the subscriber. They have short life cycles, allocated on one thread, destroyed on another.
During shutdown, or the deletion of a domain object, there are a flurry of ao2_cleanup()s on subscriptions and topics, as the final in-flight messages are processed. Any one of these cleanups could be the one to actually destroy a given object, so care must be taken to ensure that an object isn't referenced after an ao2_cleanup(). This includes the implicit ao2_unlock() that might happen when a RAII_VAR() goes out of scope.
Subscribers are sensitive to shutdown sequencing, specifically in how the reference message types. This is fully detailed on the wiki at https://wiki.asterisk.org/wiki/x/K4BqAQ.
In short, the lifetime of the data (and callback, if in a module) must be held until the stasis_subscription_final_message() has been received. Depending on the structure of the subscriber code, this can be handled by using stasis_subscription_final_message() to free resources on the final message, or using stasis_subscription_join()/stasis_unsubscribe_and_join() to block until the unsubscribe has completed.