using System; using Unity.Profiling; namespace UnityEngine.InputSystem.Utilities { internal static class DelegateHelpers { // InvokeCallbacksSafe protects both against the callback getting removed while being called // and against exceptions being thrown by the callback. public static void InvokeCallbacksSafe(ref CallbackArray callbacks, ProfilerMarker marker, string callbackName, object context = null) { if (callbacks.length == 0) return; marker.Begin(); callbacks.LockForChanges(); for (var i = 0; i < callbacks.length; ++i) { try { callbacks[i](); } catch (Exception exception) { Debug.LogException(exception); if (context != null) Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks of '{context}'"); else Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks"); } } callbacks.UnlockForChanges(); marker.End(); } public static void InvokeCallbacksSafe(ref CallbackArray> callbacks, TValue argument, string callbackName, object context = null) { if (callbacks.length == 0) return; Profiling.Profiler.BeginSample(callbackName); callbacks.LockForChanges(); for (var i = 0; i < callbacks.length; ++i) { try { callbacks[i](argument); } catch (Exception exception) { Debug.LogException(exception); if (context != null) Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks of '{context}'"); else Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks"); } } callbacks.UnlockForChanges(); Profiling.Profiler.EndSample(); } public static void InvokeCallbacksSafe(ref CallbackArray> callbacks, TValue1 argument1, TValue2 argument2, ProfilerMarker marker, string callbackName, object context = null) { if (callbacks.length == 0) return; marker.Begin(); callbacks.LockForChanges(); for (var i = 0; i < callbacks.length; ++i) { try { callbacks[i](argument1, argument2); } catch (Exception exception) { Debug.LogException(exception); if (context != null) Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks of '{context}'"); else Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks"); } } callbacks.UnlockForChanges(); marker.End(); } public static bool InvokeCallbacksSafe_AnyCallbackReturnsTrue(ref CallbackArray> callbacks, TValue1 argument1, TValue2 argument2, string callbackName, object context = null) { if (callbacks.length == 0) return true; Profiling.Profiler.BeginSample(callbackName); callbacks.LockForChanges(); for (var i = 0; i < callbacks.length; ++i) { try { if (callbacks[i](argument1, argument2)) { callbacks.UnlockForChanges(); Profiling.Profiler.EndSample(); return true; } } catch (Exception exception) { Debug.LogException(exception); if (context != null) Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks of '{context}'"); else Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks"); } } callbacks.UnlockForChanges(); Profiling.Profiler.EndSample(); return false; } /// /// Invokes the given callbacks and also invokes any callback returned from the result of the first. /// /// /// /// Allows an chaining up an additional, optional block of code to the original callback /// and allow the external code make the decision about whether this code should be executed. /// public static void InvokeCallbacksSafe_AndInvokeReturnedActions( ref CallbackArray> callbacks, TValue argument, string callbackName, object context = null) { if (callbacks.length == 0) return; Profiling.Profiler.BeginSample(callbackName); callbacks.LockForChanges(); for (var i = 0; i < callbacks.length; ++i) { try { callbacks[i](argument)?.Invoke(); } catch (Exception exception) { Debug.LogException(exception); if (context != null) Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks of '{context}'"); else Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks"); } } callbacks.UnlockForChanges(); Profiling.Profiler.EndSample(); } /// /// Invokes the given callbacks and returns true if any of them returned a non-null result. /// /// /// Returns false if every callback invocation returned null. /// public static bool InvokeCallbacksSafe_AnyCallbackReturnsObject( ref CallbackArray> callbacks, TValue argument, string callbackName, object context = null) { if (callbacks.length == 0) return false; Profiling.Profiler.BeginSample(callbackName); callbacks.LockForChanges(); for (var i = 0; i < callbacks.length; ++i) { try { var ret = callbacks[i](argument); if (ret != null) { callbacks.UnlockForChanges(); Profiling.Profiler.EndSample(); return true; } } catch (Exception exception) { Debug.LogException(exception); if (context != null) Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks of '{context}'"); else Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks"); } } callbacks.UnlockForChanges(); Profiling.Profiler.EndSample(); return false; } } }