File: System\ServiceModel\Dispatcher\ThreadBehavior.cs
Web Access
Project: src\src\System.ServiceModel.Primitives\src\System.ServiceModel.Primitives.csproj (System.ServiceModel.Primitives)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
 
using System.Runtime;
using System.Threading;
 
namespace System.ServiceModel.Dispatcher
{
    internal class ThreadBehavior
    {
        private SendOrPostCallback _threadAffinityStartCallback;
        private SendOrPostCallback _threadAffinityEndCallback;
        private static Action<object> s_cleanThreadCallback;
        private readonly SynchronizationContext _context;
 
        internal ThreadBehavior(DispatchRuntime dispatch)
        {
            _context = dispatch.SynchronizationContext;
        }
 
        private SendOrPostCallback ThreadAffinityStartCallbackDelegate
        {
            get
            {
                if (_threadAffinityStartCallback == null)
                {
                    _threadAffinityStartCallback = new SendOrPostCallback(SynchronizationContextStartCallback);
                }
                return _threadAffinityStartCallback;
            }
        }
        private SendOrPostCallback ThreadAffinityEndCallbackDelegate
        {
            get
            {
                if (_threadAffinityEndCallback == null)
                {
                    _threadAffinityEndCallback = new SendOrPostCallback(SynchronizationContextEndCallback);
                }
                return _threadAffinityEndCallback;
            }
        }
 
        private static Action<object> CleanThreadCallbackDelegate
        {
            get
            {
                if (ThreadBehavior.s_cleanThreadCallback == null)
                {
                    ThreadBehavior.s_cleanThreadCallback = new Action<object>(ThreadBehavior.CleanThreadCallback);
                }
                return ThreadBehavior.s_cleanThreadCallback;
            }
        }
 
        internal void BindThread(ref MessageRpc rpc)
        {
            BindCore(ref rpc, true);
        }
 
        internal void BindEndThread(ref MessageRpc rpc)
        {
            BindCore(ref rpc, false);
        }
 
        private void BindCore(ref MessageRpc rpc, bool startOperation)
        {
            SynchronizationContext syncContext = GetSyncContext(rpc.InstanceContext);
 
            if (syncContext != null)
            {
                IResumeMessageRpc resume = rpc.Pause();
                if (startOperation)
                {
                    syncContext.OperationStarted();
                    syncContext.Post(ThreadAffinityStartCallbackDelegate, resume);
                }
                else
                {
                    syncContext.Post(ThreadAffinityEndCallbackDelegate, resume);
                }
            }
            else if (rpc.SwitchedThreads)
            {
                IResumeMessageRpc resume = rpc.Pause();
                ActionItem.Schedule(ThreadBehavior.CleanThreadCallbackDelegate, resume);
            }
        }
 
        private SynchronizationContext GetSyncContext(InstanceContext instanceContext)
        {
            Fx.Assert(instanceContext != null, "instanceContext is null !");
            SynchronizationContext syncContext = instanceContext.SynchronizationContext ?? _context;
            return syncContext;
        }
 
        private void SynchronizationContextStartCallback(object state)
        {
            ResumeProcessing((IResumeMessageRpc)state);
        }
        private void SynchronizationContextEndCallback(object state)
        {
            IResumeMessageRpc resume = (IResumeMessageRpc)state;
 
            ResumeProcessing(resume);
 
            SynchronizationContext syncContext = GetSyncContext(resume.GetMessageInstanceContext());
            Fx.Assert(syncContext != null, "syncContext is null !?");
            syncContext.OperationCompleted();
        }
        private void ResumeProcessing(IResumeMessageRpc resume)
        {
            bool alreadyResumedNoLock;
            resume.Resume(out alreadyResumedNoLock);
 
            if (alreadyResumedNoLock)
            {
                string text = SRP.Format(SRP.SFxMultipleCallbackFromSynchronizationContext, _context.GetType().ToString());
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(text));
            }
        }
 
        private static void CleanThreadCallback(object state)
        {
            bool alreadyResumedNoLock;
            ((IResumeMessageRpc)state).Resume(out alreadyResumedNoLock);
 
            if (alreadyResumedNoLock)
            {
                Fx.Assert("IOThreadScheduler called back twice");
            }
        }
 
        internal static SynchronizationContext GetCurrentSynchronizationContext()
        {
            return SynchronizationContext.Current;
        }
    }
}