private bool RegisterTrigger(DestroyTrigger trigger, GameObject bindTarget, TimerJob timerJob)
        {
            if (bindTarget == null)
            {
                Debugs.LogError("bindTarget이 이미 null입니다.");
                return(false);
            }

            if (timerJob == null)
            {
                Debugs.LogError("timerJob이 이미 null입니다.");
                return(false);
            }

            if (triggerMap.ContainsKey(trigger.Key))
            {
                triggerMap[trigger.Key].Add(new WeakReference(timerJob));
            }
            else
            {
                triggerList.AddLast(trigger);
                triggerMap.SafetyAdd(trigger.Key, new List <WeakReference>());
                triggerMap[trigger.Key].Add(new WeakReference(timerJob));
            }

            return(true);
        }
        /// <summary>
        /// TimerJob을 Global Dispatch Timer에 등록합니다.
        /// GameObject인 bindTarget이 파괴되면 작업도 취소됩니다.
        /// 이 메서드는 동작 시간의 유효성을 따로 검사해주지 않습니다.
        /// </summary>
        /// <param name="job">실행할 TimerJob 객체</param>
        /// <param name="bindTarget">라이프 사이클을 함께할 GameObject</param>
        public void PushTimerJob(TimerJob job, GameObject bindTarget)
        {
            if (job == null)
            {
                Debugs.LogError("Job이 null입니다.");
                return;
            }

            if (bindTarget == null)
            {
                Debugs.LogError("bindTarget이 null입니다.");
                return;
            }

            var destroyTrigger = bindTarget.GetComponent <DestroyTrigger>();

            if (destroyTrigger == null)
            {
                                #if __USE_TIMER_LOG
                Debugs.Log("Create New DestroyTrigger");
                                #endif
                destroyTrigger = bindTarget.AddComponent <DestroyTrigger>();
            }

                        #if __USE_TIMER_LOG
            Debugs.Log("Attatch DestroyTrigger to gameObject");
                        #endif

            if (RegisterTrigger(destroyTrigger, bindTarget, job))
            {
                timedJobQueue.Enqueue(job);
            }
        }
        /// <summary>
        /// TimerJob을 Global Dispatch Timer에 등록합니다.
        /// 이 메서드는 동작 시간의 유효성을 따로 검사해주지 않습니다.
        /// </summary>
        /// <param name="job">실행할 TimerJob 객체</param>
        public void PushTimerJob(TimerJob job)
        {
            if (job == null)
            {
                Debugs.LogError("Job이 null입니다.");
                return;
            }

            timedJobQueue.Enqueue(job);
        }
        /// <summary>
        /// Global Queue에 delay초 뒤에 실행되도록 작업을 예약합니다.
        /// </summary>
        /// <param name="timerTask">실행할 작업</param>
        /// <param name="delay">실행까지 남은 초(float)</param>
        /// <returns>생성된 TimerJob의 할당 해제 가능한 IDisposable 객체를 반환합니다.</returns>
        public IDisposable PushTimerJob(Action timerTask, float delay)
        {
            if (!ValidateTime(delay))
            {
                return(null);
            }

            var timerJob = new TimerJob(timerTask, CurrentTicks + delay);

            timedJobQueue.Enqueue(timerJob);
            return(timerJob);
        }
        /// <summary>
        /// TimerJob과 라이프 사이클을 같이 하는 GameObject를 순회하면서 체크합니다.
        /// </summary>
        private void CheckDestroyedGameObject()
        {
            var markedList = new List <long>();

            if (triggerList.Count > 0)
            {
                markedList.Clear();

                var ptr = triggerList.First;
                while (ptr != null)
                {
                    var current = ptr;
                    var trigger = current.Value;

                    if (!trigger.IsActivated)
                    {
#if __USE_TIMER_LOG
                        Debugs.Log("Bind된 gameobject Destroy! TimerJob을 비활성화 합니다.");
#endif
                        markedList.Add(trigger.Key);

                        //Linked List 이므로 O(1)...
                        triggerList.Remove(current);
                    }

                    ptr = ptr.Next;
                }

                foreach (var triggerKey in markedList)
                {
                    // priorityQueue를 사용하여 TimerJob 큐를 스케줄링 하므로 TimerJob에 마크만 해두고
                    // 해당 Job의 tick이 되어 실행될때 비활성화 시켜주는게 낫다.
                    List <WeakReference> triggeredJobWeakRefList;
                    if (triggerMap.TryGetValue(triggerKey, out triggeredJobWeakRefList))
                    {
                        foreach (WeakReference triggeredJobRef in triggeredJobWeakRefList)
                        {
                            TimerJob triggeredJob = triggeredJobRef.Target as TimerJob;
                            if (triggeredJob != null)
                            {
                                triggeredJob.MarkDisposed();
                            }
                        }

                        triggerMap.Remove(triggerKey);
                    }
                }
            }
        }
        /// <summary>
        /// Global Queue에 delay초 뒤에 실행되도록 작업을 예약합니다.
        /// GameObject인 bindTarget이 파괴되면 작업도 취소됩니다.
        /// </summary>
        /// <param name="timerTask">실행할 작업</param>
        /// <param name="delay">실행까지 남은 초(float)</param>
        /// <param name="bindTarget">라이프 사이클을 함께할 GameObject</param>
        /// <returns>생성된 TimerJob의 할당 해제 가능한 IDisposable 객체를 반환합니다.</returns>
        public IDisposable PushTimerJob(Action timerTask, float delay, GameObject bindTarget)
        {
            if (!ValidateTime(delay))
            {
                return(null);
            }

            if ((object)bindTarget == null)
            {
                Debugs.LogError("bindTarget이 null입니다.");
                return(null);
            }

            var destroyTrigger = bindTarget.GetComponent <DestroyTrigger>();

            if (destroyTrigger == null)
            {
                                #if __USE_TIMER_LOG
                Debugs.Log("Create New DestroyTrigger");
                                #endif
                destroyTrigger = bindTarget.AddComponent <DestroyTrigger>();
            }

                        #if __USE_TIMER_LOG
            Debugs.Log("Attatch DestroyTrigger to gameObject");
                        #endif

            var timerJob = new TimerJob(timerTask, CurrentTicks + delay);

            if (!RegisterTrigger(destroyTrigger, bindTarget, timerJob))
            {
                return(null);
            }

            timedJobQueue.Enqueue(timerJob);

            return(timerJob);
        }