Make OnCopmplete, OnCancel LinkedList
Opened this issue · 0 comments
Akeit0 commented
Related to #33
Poolable LinkedList will allow multiple complete/cancel callbacks with state.
Action is large class so, this also reduce GC allocation.
Many codes invoking callbacks or wrapping deletages (e.g. unitask integration) will be smarter.
public struct MotionCallbackData
{
public byte StateCount;
public bool IsCallbackRunning;
public bool CancelOnError;
public bool SkipValuesDuringDelay;
public object State1;
public object State2;
public object State3;
public object UpdateAction;
public UnsafeActionList OnCompleteAction;
public UnsafeActionList OnCancelAction;
public void OnComplete(){
OnCompleteAction.InvokeAndDispose();
}
public void OnCancel(){
OnCancel.InvokeAndDispose();
}
}
public struct UnsafeActionList : IDisposable
{
ActionList head;
public void Append([NotNull] Action action)
{
ActionList.Append(ref head, action);
}
public void Append<TTarget>([NotNull] TTarget target, [NotNull] Action<TTarget> action) where TTarget : class
{
ActionList.Append(ref head, target, action);
}
public void RemoveTarget(object target)
{
ActionList.RemoveTarget(ref head, target);
}
public void Remove(object target,object action)
{
ActionList.Remove(ref head, target,action);
}
public void Invoke()
{
if (head == null) return;
head.Invoke();
head = null;
}
public void InvokeAndDispose()
{
if (head == null) return;
head.InvokeAndDispose();
head = null;
}
public void Dispose()
{
if (head == null) return;
head.Dispose();
head = null;
}
}
public sealed class ActionList : IDisposable
{
static ActionList pool;
object target;
object action;
ActionList nextNode;
public ActionList LastNode
{
get
{
var next = this;
while (next.nextNode != null)
{
next = next.nextNode;
}
return next;
}
}
ActionList()
{
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static ActionList CreateOrGet()
{
if (pool == null)
{
return new ActionList();
}
else
{
ref var poolRef = ref pool;
var result = poolRef;
poolRef = result.nextNode;
result.nextNode = null;
return result;
}
}
public static void Append(ref ActionList head, [NotNull] Action action)
{
if (action == null) return;
if (head == null)
{
head = CreateOrGet(null, action);
}
else
{
head.LastNode.nextNode = CreateOrGet(null, action);
}
}
public static void Append<TTarget>(ref ActionList head, [NotNull] TTarget target, [NotNull] Action<TTarget> action)
where TTarget : class
{
if (target == null)
{
Debug.LogError("target is null");
return;
}
if (action == null) return;
if (head == null)
{
head = CreateOrGet(target, action);
}
else
{
head.LastNode.nextNode = CreateOrGet(target, action);
}
}
public static void RemoveTarget(ref ActionList head, object target)
{
if (head == null) return;
if (head.target == target)
{
head = head.nextNode;
return;
}
var next = head;
while (next.nextNode != null)
{
if (next.nextNode.target == target)
{
next.nextNode = next.nextNode.nextNode;
}
next = next.nextNode;
}
}
public static void Remove(ref ActionList head, object target,object action)
{
if (head == null) return;
if (head.target == target&&head.action==action)
{
head = head.nextNode;
return;
}
var next = head;
while (next.nextNode != null)
{
if (next.nextNode.target == target&&next.nextNode.action==action)
{
next.nextNode = next.nextNode.nextNode;
}
next = next.nextNode;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ActionList CreateOrGet(object target, object action)
{
var result = CreateOrGet();
result.target = target;
result.action = action;
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Invoke()
{
var next = this;
InvokeLabel:
try
{
if (next.target != null)
UnsafeUtility.As<object, Action<object>>(ref next.action)(next.target);
else UnsafeUtility.As<object, Action>(ref next.action)();
}
catch (Exception ex)
{
Debug.LogException(ex);
}
var nextNext = next.nextNode;
if (nextNext != null)
{
next = nextNext;
goto InvokeLabel;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void InvokeAndDispose()
{
var next = this;
InvokeLabel:
try
{
if (next.target != null)
UnsafeUtility.As<object, Action<object>>(ref next.action)(next.target);
else UnsafeUtility.As<object, Action>(ref next.action)();
}
catch (Exception ex)
{
MotionDispatcher.GetUnhandledExceptionHandler()?.Invoke(ex);
}
next.action = null;
next.target = null;
var nextNext = next.nextNode;
if (nextNext != null)
{
next = nextNext;
goto InvokeLabel;
}
next.nextNode = pool;
pool = this;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
var next = this;
InvokeLabel:
next.action = null;
next.target = null;
var nextNext = next.nextNode;
if (nextNext != null)
{
next = nextNext;
goto InvokeLabel;
}
next.nextNode = pool;
pool = next;
}
}