I'm working in a very large code base that contains a variety of (Unity-provided) async loading for various assets, including prefabs that contain somewhere in them one or more PlayMaker FSMs.
Some of our automated exception reports from our live product show the following truncated stack trace for an IndexOutOfRangeException:
System.Collections.Generic.Dictionary`2[TKey,TValue].TryInsert (TKey key, TValue value, System.Collections.Generic.InsertionBehavior behavior) (at <7ba07f088431485bb722f3b3373e87ee>:0)
System.Collections.Generic.Dictionary`2[TKey,TValue].set_Item (TKey key, TValue value) (at <7ba07f088431485bb722f3b3373e87ee>:0)
HutongGames.PlayMaker.ActionData.GetActionTypeHashCode (System.Type actionType) (at <7e9e696ff1a04620968849cd9bbf2ab4>:0)
HutongGames.PlayMaker.ActionData.CreateAction (HutongGames.PlayMaker.ActionData+Context context, System.Int32 actionIndex) (at <7e9e696ff1a04620968849cd9bbf2ab4>:0)
HutongGames.PlayMaker.ActionData.LoadActions (HutongGames.PlayMaker.FsmState state) (at <7e9e696ff1a04620968849cd9bbf2ab4>:0)
HutongGames.PlayMaker.FsmState.LoadActions () (at <7e9e696ff1a04620968849cd9bbf2ab4>:0) HutongGames.PlayMaker.Fsm.InitData () (at <7e9e696ff1a04620968849cd9bbf2ab4>:0)
PlayMakerFSM.OnAfterDeserialize () (at <7e9e696ff1a04620968849cd9bbf2ab4>:0)
UnityEngine.AssetBundle:LoadAsset(String, Type)
...
The only reason I can think of that a Dictionary would throw an IndexOutOfRangeException during an insert is due to a race condition with multiple inserts from different threads around the same time. It appears that this really is possible in PlayMaker, since ActionData.ActionHashCodeLookup (and some others) are static members, and susceptible to race conditions in this very manner.
Consider the above a bug report. Below, a request for how I might deal with/work around this:
Ideally, this would be fixed inside PlayMaker itself. It's the most logical/clean place for it, but since everything is contained inside a .DLL, such changes cannot be made by us.
While waiting for an update that includes such a bug fix, my first thought was to introduce some locking pattern for thread safety inside our loading code, but we don't want to force all loading to use this pattern, as it would effectively turn the async loading into something not far off of sync loading. It's only necessary for loading levels/prefabs/etc containing PlayMaker FSMs.
Any thoughts on how I might avoid this without incurring a significant loss of async loading performance?