Makes Mono.Addins safe to use from multiple threads. Most internal collections have been converted to immutable, so normal access to the extension tree is lock-free, so it should be fast. Locks are acquired when the tree is built (on demand).
Some noticeable changes:
AddinDatabase
New AddinDatabaseTransaction class needs to be provided for methods that change the database. It can be obtained calling BeginTransaction.
The logic for getting addins has been simplified a bit and some code duplication removed.
Access to internal data structures is protected with locks.
The list of add-ins is an immutable collection, so there is not need to lock to read it.
New ImmutableAddinHostIndex class that can be created from a AddinHostIndex, and which provides lock-free access to the index from AddinDatabase and other classes.
DatabaseConfiguration
AddinStatus and the internal dictionary that holds instances of it are now immutable
Changes to the configuration require a AddinDatabaseTransaction
Addin
This class was already thread safe, just renamed some variables for clarity.
AddinEngine
Internal structures are now immutable, so read access is lock free. Changes require a ExtensionContextTransaction, obtained using ExtensionContext.BeginTransaction.
Mono.Addins is now thread safe, meaning that it is possible and safe to use AddinManager, AddinEngine and related classes to access the extension model from different threads.
Mono.Addins by itself does not use multiple threads. If the add-in engine is always used from a single thread, then the behavior is the same as it used to be before thread-safety support. There are however several considerations to take in to account when using Mono.Addins in a multi-threaded application.
Extension model queries
The extension model is a mutable data structure. Extension nodes can be added or removed as a result of conditions changing or add-ins being enabled or disabled. Methods like ExtensionContext.GetExtensionNodes() return a snapshot of the nodes at the time of the call, and that snapshot won't change. However, consecutive calls to that method may return different results if the extension model mutates between calls.
Event Handlers
The AddinManager, AddinEngine, ExtensionContext and ExtensionNode classes have events that are raised when the extension model changes. In a multi-threaded application, those events may be raised in different threads, depending on which thread the change was originated. Subscribers of those events must take that into account and take thread safety measures by themselves if necessary.
In any case, Mono.Addins guarantees that events for a specific extension context are raised sequentially. For example, if a child node is added and removed from a node, the corresponding Add event handler will be executed first, and when it completes the Remove handler will be executed next.
Custom extension nodes
It is up to Mono.Addins extenders to guaratee the thread safety of custom extension nodes. The implementation must take into account that nodes can be created in different threads, and that virtual methods such as OnChildNodeAdded or OnChildNodeRemoved can also be invoked from different threads (those methods however are executed sequentially, like explained above for events).
Conditions
Custom conditions may need to be evaluated from different threads, so the ConditionType.Evaluate() method should be thread safe. The ConditionType.NotifyChanged() method, used to notify that the condition has changed, can be invoked from any thread. Extension model change events caused by condition changes will be raised in the thread that invoked NotifyChanged().
Makes Mono.Addins safe to use from multiple threads. Most internal collections have been converted to immutable, so normal access to the extension tree is lock-free, so it should be fast. Locks are acquired when the tree is built (on demand).
Some noticeable changes:
Mono.Addins is now thread safe, meaning that it is possible and safe to use
AddinManager
,AddinEngine
and related classes to access the extension model from different threads.Mono.Addins by itself does not use multiple threads. If the add-in engine is always used from a single thread, then the behavior is the same as it used to be before thread-safety support. There are however several considerations to take in to account when using Mono.Addins in a multi-threaded application.
Extension model queries
The extension model is a mutable data structure. Extension nodes can be added or removed as a result of conditions changing or add-ins being enabled or disabled. Methods like
ExtensionContext.GetExtensionNodes()
return a snapshot of the nodes at the time of the call, and that snapshot won't change. However, consecutive calls to that method may return different results if the extension model mutates between calls.Event Handlers
The
AddinManager
,AddinEngine
,ExtensionContext
andExtensionNode
classes have events that are raised when the extension model changes. In a multi-threaded application, those events may be raised in different threads, depending on which thread the change was originated. Subscribers of those events must take that into account and take thread safety measures by themselves if necessary.In any case, Mono.Addins guarantees that events for a specific extension context are raised sequentially. For example, if a child node is added and removed from a node, the corresponding Add event handler will be executed first, and when it completes the Remove handler will be executed next.
Custom extension nodes
It is up to Mono.Addins extenders to guaratee the thread safety of custom extension nodes. The implementation must take into account that nodes can be created in different threads, and that virtual methods such as
OnChildNodeAdded
orOnChildNodeRemoved
can also be invoked from different threads (those methods however are executed sequentially, like explained above for events).Conditions
Custom conditions may need to be evaluated from different threads, so the
ConditionType.Evaluate()
method should be thread safe. TheConditionType.NotifyChanged()
method, used to notify that the condition has changed, can be invoked from any thread. Extension model change events caused by condition changes will be raised in the thread that invokedNotifyChanged()
.