public void Dispose()
        {
            if (this._pipelineDispatcher != null)
            {
                this._pipelineDispatcher.Dispose();
                this._pipelineDispatcher = null;
            }

            if (this._backgroundDispatcher != null)
            {
                this._backgroundDispatcher.Dispose();
                this._backgroundDispatcher = null;
            }

            if (this._gameThreadDispatcher != null)
            {
                this._gameThreadDispatcher.Dispose();
                this._gameThreadDispatcher = null;
            }
        }
 public DispatchManager(IDecalEventsProxy decalEventsProxy)
 {
     this._backgroundDispatcher = new BackgroundDispatcher();
     this._gameThreadDispatcher = new GameThreadDispatcher(decalEventsProxy);
     this._pipelineDispatcher = new PipelineDispatcher(decalEventsProxy);
 }
        public void VerifyQueueActionWillQueueAndRunOnceRenderFrameFired()
        {
            var fakeDecalEventsProxy = new Fakes.FakeDecalEventsProxy();
            using (var fakePipelineAction = new Fakes.FakePipelineAction())
            {
                using (var dispatcher = new PipelineDispatcher(fakeDecalEventsProxy))
                {
                    dispatcher.EnqueueAction(fakePipelineAction);

                    Assert.AreEqual(1, dispatcher.QueueCount);
                    Assert.IsFalse(dispatcher.HasPendingAction);

                    // Nothing should be called as a result of queueing
                    Assert.AreEqual(0, fakePipelineAction.InitCallCount);
                    Assert.AreEqual(0, fakePipelineAction.BeginInvokeCallCount);
                    Assert.AreEqual(0, fakePipelineAction.EndInvokeCallCount);
                    Assert.IsFalse(fakePipelineAction.IsComplete);

                    // Fire the render event
                    fakeDecalEventsProxy.FireRenderFrame(new EventArgs());

                    // Step 1 is to Init.  Verify that happened.
                    Assert.IsTrue(dispatcher.HasPendingAction);

                    Assert.AreEqual(1, fakePipelineAction.InitCallCount);
                    Assert.AreEqual(0, fakePipelineAction.BeginInvokeCallCount);
                    Assert.AreEqual(0, fakePipelineAction.EndInvokeCallCount);
                    Assert.IsFalse(fakePipelineAction.IsComplete);

                    // Fire the render event
                    fakeDecalEventsProxy.FireRenderFrame(new EventArgs());

                    // Step 2 is to BeginInvoke & Wait in the Background
                    Assert.IsTrue(dispatcher.HasPendingAction);

                    Assert.AreEqual(1, fakePipelineAction.InitCallCount);
                    Assert.AreEqual(1, fakePipelineAction.BeginInvokeCallCount);
                    Assert.AreEqual(0, fakePipelineAction.EndInvokeCallCount);
                    Assert.IsFalse(fakePipelineAction.IsComplete);

                    // Now tell the background wait to release.
                    fakePipelineAction.ForTesting_SetComplete(false);

                    // The action should report complete, but the dispatcher shouldn't have processed it yet
                    // since we haven't fired render frame yet.
                    Assert.AreEqual(1, fakePipelineAction.InitCallCount);
                    Assert.AreEqual(1, fakePipelineAction.BeginInvokeCallCount);
                    Assert.AreEqual(0, fakePipelineAction.EndInvokeCallCount);
                    Assert.IsTrue(fakePipelineAction.IsComplete);

                    // Fire the render event
                    fakeDecalEventsProxy.FireRenderFrame(new EventArgs());
                    Assert.IsFalse(dispatcher.HasPendingAction);

                    // Now the action should have been fully processed
                    Assert.AreEqual(1, fakePipelineAction.InitCallCount);
                    Assert.AreEqual(1, fakePipelineAction.BeginInvokeCallCount);
                    Assert.AreEqual(1, fakePipelineAction.EndInvokeCallCount);
                    Assert.IsTrue(fakePipelineAction.IsComplete);

                    Assert.AreEqual(0, dispatcher.QueueCount);

                    // Check the thread id's that everything was invoked on to verify they were correct.
                    // Doing this at the end to simplying timing of caching this info
                    Assert.AreEqual(Thread.CurrentThread.ManagedThreadId, fakePipelineAction.InitThreadId);
                    Assert.AreEqual(Thread.CurrentThread.ManagedThreadId, fakePipelineAction.BeginInvokeThreadId);
                    Assert.AreEqual(Thread.CurrentThread.ManagedThreadId, fakePipelineAction.EndInvokeThreadId);

                    Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, fakePipelineAction.WaitForCompleteThreadId);
                }
            }
        }
        public void VerifyReadyBehavior_WhenNotReady_ThenBecomesReady()
        {
            var fakeDecalEventsProxy = new Fakes.FakeDecalEventsProxy();
            using (var fakePipelineAction = new Fakes.FakePipelineAction(false))
            {
                using (var dispatcher = new PipelineDispatcher(fakeDecalEventsProxy))
                {
                    dispatcher.EnqueueAction(fakePipelineAction);

                    Assert.AreEqual(0, fakePipelineAction.ReadyCallCount);

                    // Fire the render event
                    fakeDecalEventsProxy.FireRenderFrame(new EventArgs());

                    // Step 1 is to Init.  Ready should not have been called yet
                    Assert.AreEqual(0, fakePipelineAction.ReadyCallCount);

                    // Fire the render event
                    fakeDecalEventsProxy.FireRenderFrame(new EventArgs());

                    // Step 2 is to Call Ready, Perform if so, and Wait in the Background
                    Assert.IsTrue(dispatcher.HasPendingAction);

                    Assert.AreEqual(1, fakePipelineAction.ReadyCallCount);
                    Assert.AreEqual(0, fakePipelineAction.BeginInvokeCallCount);
                    Assert.AreEqual(0, fakePipelineAction.EndInvokeCallCount);
                    Assert.IsFalse(fakePipelineAction.IsComplete);

                    // So set it as ready so the next time around we can perform it
                    fakePipelineAction.ForTesting_Ready = true;

                    // Fire the render event
                    fakeDecalEventsProxy.FireRenderFrame(new EventArgs());

                    Assert.AreEqual(2, fakePipelineAction.ReadyCallCount);
                    Assert.AreEqual(1, fakePipelineAction.BeginInvokeCallCount);
                    Assert.AreEqual(0, fakePipelineAction.EndInvokeCallCount);
                    Assert.IsFalse(fakePipelineAction.IsComplete);

                    // Now tell the background wait to release.
                    fakePipelineAction.ForTesting_SetComplete(false);

                    // The action should report complete, but the dispatcher shouldn't have processed it yet
                    // since we haven't fired render frame yet.
                    Assert.AreEqual(1, fakePipelineAction.InitCallCount);
                    Assert.AreEqual(2, fakePipelineAction.ReadyCallCount);
                    Assert.AreEqual(1, fakePipelineAction.BeginInvokeCallCount);
                    Assert.AreEqual(0, fakePipelineAction.EndInvokeCallCount);
                    Assert.IsTrue(fakePipelineAction.IsComplete);

                    // Fire the render event
                    fakeDecalEventsProxy.FireRenderFrame(new EventArgs());
                    Assert.IsFalse(dispatcher.HasPendingAction);

                    // Now the action should have been fully processed
                    Assert.AreEqual(1, fakePipelineAction.InitCallCount);
                    Assert.AreEqual(2, fakePipelineAction.ReadyCallCount);
                    Assert.AreEqual(1, fakePipelineAction.BeginInvokeCallCount);
                    Assert.AreEqual(1, fakePipelineAction.EndInvokeCallCount);
                    Assert.IsTrue(fakePipelineAction.IsComplete);

                    Assert.AreEqual(0, dispatcher.QueueCount);
                }
            }
        }
        public void VerifyReadyBehavior_WhenNotReady()
        {
            var fakeDecalEventsProxy = new Fakes.FakeDecalEventsProxy();
            using (var fakePipelineAction = new Fakes.FakePipelineAction(false))
            {
                using (var dispatcher = new PipelineDispatcher(fakeDecalEventsProxy))
                {
                    dispatcher.EnqueueAction(fakePipelineAction);

                    // Fire the render event
                    fakeDecalEventsProxy.FireRenderFrame(new EventArgs());

                    // Step 1 is to Init.   Ready should not have been called yet
                    Assert.AreEqual(0, fakePipelineAction.ReadyCallCount);

                    // Fire the render event
                    fakeDecalEventsProxy.FireRenderFrame(new EventArgs());

                    // Step 2 is to Call Ready, Perform if so, and Wait in the Background
                    Assert.IsTrue(dispatcher.HasPendingAction);

                    Assert.AreEqual(1, fakePipelineAction.ReadyCallCount);
                    Assert.AreEqual(0, fakePipelineAction.BeginInvokeCallCount);
                    Assert.AreEqual(0, fakePipelineAction.EndInvokeCallCount);
                    Assert.IsFalse(fakePipelineAction.IsComplete);
                }
            }
        }
        public void VerifyReadyBehaviorWhenReadyInitially()
        {
            var fakeDecalEventsProxy = new Fakes.FakeDecalEventsProxy();
            using (var fakePipelineAction = new Fakes.FakePipelineAction())
            {
                using (var dispatcher = new PipelineDispatcher(fakeDecalEventsProxy))
                {
                    dispatcher.EnqueueAction(fakePipelineAction);

                    // Nothing should be called as a result of queueing
                    Assert.AreEqual(0, fakePipelineAction.ReadyCallCount);

                    // Fire the render event
                    fakeDecalEventsProxy.FireRenderFrame(new EventArgs());

                    // Step 1 is to Init.  Ready should not have been called yet
                    Assert.AreEqual(0, fakePipelineAction.ReadyCallCount);

                    // Fire the render event
                    fakeDecalEventsProxy.FireRenderFrame(new EventArgs());

                    // Step 2 is to BeginInvoke & Wait in the Background
                    Assert.IsTrue(dispatcher.HasPendingAction);

                    Assert.AreEqual(1, fakePipelineAction.ReadyCallCount);
                    Assert.AreEqual(1, fakePipelineAction.BeginInvokeCallCount);

                    // Now tell the background wait to release.
                    fakePipelineAction.ForTesting_SetComplete(false);

                    // Fire the render event
                    fakeDecalEventsProxy.FireRenderFrame(new EventArgs());
                    Assert.IsFalse(dispatcher.HasPendingAction);

                    // Now the action should have been fully processed
                    Assert.AreEqual(1, fakePipelineAction.InitCallCount);
                    Assert.AreEqual(1, fakePipelineAction.BeginInvokeCallCount);
                    Assert.AreEqual(1, fakePipelineAction.EndInvokeCallCount);
                    Assert.IsTrue(fakePipelineAction.IsComplete);

                    Assert.AreEqual(0, dispatcher.QueueCount);
                }
            }
        }
        public void VerifyQueueActionCanRetry()
        {
            var fakeDecalEventsProxy = new Fakes.FakeDecalEventsProxy();
            using (var fakePipelineAction = new Fakes.FakePipelineAction())
            {
                using (var dispatcher = new PipelineDispatcher(fakeDecalEventsProxy))
                {
                    dispatcher.EnqueueAction(fakePipelineAction);

                    // Fire the render event - Gets us init'ed
                    fakeDecalEventsProxy.FireRenderFrame(new EventArgs());

                    // Fire the render event again - bets us begin invoked
                    fakeDecalEventsProxy.FireRenderFrame(new EventArgs());

                    // Now tell the background wait to release and request a retry
                    fakePipelineAction.ForTesting_SetComplete(true);

                    // Now we are into the situation this unit test is focused on.  Start making assertions.
                    Assert.IsTrue(dispatcher.HasPendingAction);

                    // Fire the render event - This should trip the Retry check and Reset
                    fakeDecalEventsProxy.FireRenderFrame(new EventArgs());

                    // Init should not be called again, so make sure that count is still 1.
                    Assert.AreEqual(1, fakePipelineAction.InitCallCount);
                    // Begin invoke shouldn't have been called again yet.  So the count should still be 1.
                    Assert.AreEqual(1, fakePipelineAction.BeginInvokeCallCount);
                    // We are retrying, not completing, so no EndInvoke.
                    Assert.AreEqual(0, fakePipelineAction.EndInvokeCallCount);
                    
                    // Now, what should have changed due to the lastest firing.
                    Assert.AreEqual(1, fakePipelineAction.RetryCallCount);
                    Assert.AreEqual(1, fakePipelineAction.ResetForRetryCallCount);

                    Assert.IsTrue(dispatcher.HasPendingAction);

                    // Fire the render event - Now begin invoke will be called again.
                    fakeDecalEventsProxy.FireRenderFrame(new EventArgs());

                    // Init should never be called again, so make sure that count is still 1.
                    Assert.AreEqual(1, fakePipelineAction.InitCallCount);
                    // Begin invoke will have been called again
                    Assert.AreEqual(2, fakePipelineAction.BeginInvokeCallCount);
                    // We are retrying, not completing, so no EndInvoke.
                    Assert.AreEqual(0, fakePipelineAction.EndInvokeCallCount);

                    // These are unchanged
                    Assert.AreEqual(1, fakePipelineAction.RetryCallCount);
                    Assert.AreEqual(1, fakePipelineAction.ResetForRetryCallCount);

                    // Now complete the action for real
                    fakePipelineAction.ForTesting_SetComplete(false);

                    // Fire the render event - This should cause everything to complete.
                    fakeDecalEventsProxy.FireRenderFrame(new EventArgs());


                    // Now the action should have been fully processed
                    Assert.AreEqual(1, fakePipelineAction.InitCallCount);
                    Assert.AreEqual(2, fakePipelineAction.BeginInvokeCallCount);
                    Assert.AreEqual(1, fakePipelineAction.EndInvokeCallCount);
                    Assert.IsTrue(fakePipelineAction.IsComplete);
                    Assert.AreEqual(2, fakePipelineAction.RetryCallCount);
                    Assert.AreEqual(1, fakePipelineAction.ResetForRetryCallCount);

                    Assert.AreEqual(0, dispatcher.QueueCount);

                    // Check the thread id's that everything was invoked on to verify they were correct.
                    // Doing this at the end to simplying timing of caching this info
                    Assert.AreEqual(Thread.CurrentThread.ManagedThreadId, fakePipelineAction.InitThreadId);
                    Assert.AreEqual(Thread.CurrentThread.ManagedThreadId, fakePipelineAction.BeginInvokeThreadId);
                    Assert.AreEqual(Thread.CurrentThread.ManagedThreadId, fakePipelineAction.EndInvokeThreadId);
                    Assert.AreEqual(Thread.CurrentThread.ManagedThreadId, fakePipelineAction.ResetForRetryThreadId);

                    Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, fakePipelineAction.WaitForCompleteThreadId);
                }
            }
        }