Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make OnCopmplete, OnCancel LinkedList #66

Open
Akeit0 opened this issue Jan 17, 2024 · 0 comments
Open

Make OnCopmplete, OnCancel LinkedList #66

Akeit0 opened this issue Jan 17, 2024 · 0 comments

Comments

@Akeit0
Copy link
Contributor

Akeit0 commented Jan 17, 2024

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;
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant