File: system\runtime\remoting\context.cs
Project: ndp\clr\src\bcl\mscorlib.csproj (mscorlib)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
/*============================================================
**
** File:     Context.cs
**
**
** Purpose: Remoting Context core implementation.
**
**
===========================================================*/
namespace System.Runtime.Remoting.Contexts {
    
    using System;
    using System.Security;
    using System.Security.Permissions;
    using System.Threading;
    using System.Runtime.InteropServices;
    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Activation;
    using System.Runtime.Remoting.Messaging;
    using System.Runtime.Serialization;
    using System.Runtime.CompilerServices;
    using System.Runtime.Versioning;
    using System.Globalization;
    using System.Diagnostics.Contracts;
    //  CallBacks provide a facility to request execution of some code
    //  in another context.
    //  CrossContextDelegate type is defined for context call backs. 
    //  Each context has a CallBackObject which can be used to perform
    //  callbacks on the context. The delegate used to request a callback
    //  through the CallBackObject must be of CrossContextDelegate type.
    /// <internalonly/>
    [System.Runtime.InteropServices.ComVisible(true)]
    public delegate void CrossContextDelegate();
    
    /// <internalonly/>
    // deliberately not [serializable]
    [System.Runtime.InteropServices.ComVisible(true)]
    public class Context
    {
        // flags to mark the state of the context object
        // marks the context as the default context
        internal const int CTX_DEFAULT_CONTEXT   = 0x00000001;
        
        // marks the context as frozen
        internal const int CTX_FROZEN                = 0x00000002;
        
        // Tells the context channel that the context has properties 
        // that use the threadPool themselves.
        // In that case, the channel does not itself use threadPool.
        // This is OFF by default
        internal const int CTX_THREADPOOL_AWARE = 0x00000004;
    
        private const int GROW_BY                        = 0x8;
        private const int STATICS_BUCKET_SIZE           = 0x8;
        
        private IContextProperty[] _ctxProps;    // array of name-value pairs of properties        
        private DynamicPropertyHolder _dphCtx;  // Support for Dynamic Sinks
        private volatile LocalDataStoreHolder _localDataStore;
        private IMessageSink _serverContextChain;   
        private IMessageSink _clientContextChain;
        private AppDomain _appDomain;       // AppDomain property of the context
        private Object[] _ctxStatics;       // Holder for context statics
 
        //**********************
        // This needs to be the first NON-OBJECT field!
        private IntPtr _internalContext; // address of the VM context object!
        //**********************
 
        // at this point we just differentiate contexts based on context ID
        private int _ctxID;
        private int _ctxFlags;
        private int _numCtxProps;   // current count of properties
 
        // Context statics stuff
        private int _ctxStaticsCurrentBucket;
        private int _ctxStaticsFreeIndex;
 
        // Support for dynamic properties.
        private static DynamicPropertyHolder _dphGlobal = new DynamicPropertyHolder();
    
        // Support for Context Local Storage
        private static LocalDataStoreMgr _localDataStoreMgr = new LocalDataStoreMgr();
 
        // ContextID counter (for public context id)        
        private static int _ctxIDCounter = 0; 
        // <
 
 
        /// <internalonly/>
        [System.Security.SecurityCritical]  // auto-generated_required
        public Context()
            : this(0)
        {
        }
        
        [System.Security.SecurityCritical]  // auto-generated
        private Context(int flags) {
                                            
            _ctxFlags = flags;
            if ((_ctxFlags & CTX_DEFAULT_CONTEXT) != 0)
            {
                _ctxID = 0;     // ID 0 == default context
            }
            else
            {
                _ctxID = Interlocked.Increment(ref _ctxIDCounter);
            }
            
            // Every context inherits the appdomain level properties
            // Get the properties for the appdomain and add it to
            // this context.
            DomainSpecificRemotingData data = Thread.GetDomain().RemotingData;
            if(null != data)
            {
                IContextProperty[] ctxProps = data.AppDomainContextProperties;
                if(null != ctxProps)
                {
                    for(int i = 0; i < ctxProps.Length; i++)
                    {
                        SetProperty(ctxProps[i]);
                    }
                }                                    
            }
            
            // Freeze the default context now
            if ((_ctxFlags & CTX_DEFAULT_CONTEXT) != 0)
            {
                this.Freeze();
            }
            // This call will set up the cycles between the VM & managed context
            // It will also set the managed context's AppDomain property to 
            // the current AppDomain.Will also publish ifthe default ctx, so should be 
            // in the very end
            SetupInternalContext((_ctxFlags&CTX_DEFAULT_CONTEXT) == CTX_DEFAULT_CONTEXT);
                        
            Message.DebugOut("Creating Context with ID " + _ctxID + " and flags " + flags + " " + Environment.NewLine);
        }
 
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private extern void SetupInternalContext(bool bDefault);
 
    /// <internalonly/>
        [System.Security.SecuritySafeCritical]  // auto-generated
        ~Context()
        {
            // We clean up the backing objects only for the non-default
            // contexts. For default contexts these are cleaned up during
            // AppDomain shutdown.
            if (_internalContext != IntPtr.Zero && (_ctxFlags & CTX_DEFAULT_CONTEXT) == 0)
            {
                CleanupInternalContext();
            }
        }
        
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private extern void CleanupInternalContext();
            
        /// <internalonly/>
        public virtual Int32 ContextID 
        {
            [System.Security.SecurityCritical]  // auto-generated_required
            get 
            {
                return _ctxID;
            }
        }
 
        internal virtual IntPtr InternalContextID
        {
            get 
            {
                return _internalContext;
            }
        }
    
        internal virtual AppDomain AppDomain
        {
            get {return _appDomain;}
        }
 
        internal bool IsDefaultContext
        {
            get { return _ctxID == 0; }
        }
 
        /// <internalonly/>
        public static Context DefaultContext 
        {
            [System.Security.SecurityCritical]  // auto-generated_required
            get 
            {
                return Thread.GetDomain().GetDefaultContext();
            }
        }
 
        [System.Security.SecurityCritical]  // auto-generated
        internal static Context CreateDefaultContext()
        {
            return new Context(CTX_DEFAULT_CONTEXT);
        }
        
        /// <internalonly/>
        [System.Security.SecurityCritical]  // auto-generated_required
        public virtual IContextProperty GetProperty(String name)
        {
            if (_ctxProps == null || name == null)
            {
                return null;
            }
            IContextProperty prop = null;
            for (int i=0; i<_numCtxProps; i++)
            {
                if (_ctxProps[i].Name.Equals(name))
                {
                    prop = _ctxProps[i];
                    break;
                }
            }
            return prop;
        }
    
        /// <internalonly/>
        [System.Security.SecurityCritical]  // auto-generated_required
        public virtual void SetProperty(IContextProperty prop)
        {
            // We do not let people add properties to the default context.
            /* We allow appdomain level properties to be added to the default context
            if ((_ctxFlags & CTX_DEFAULT_CONTEXT) != 0)
            {
                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_AddContextFrozen"));
            }
            */
            
            if (prop == null || prop.Name == null)
            {
                throw new ArgumentNullException((prop==null) ? "prop" : "property name");
            }
            Contract.EndContractBlock();
    
            if ((_ctxFlags & CTX_FROZEN) != 0)
            {
                throw new InvalidOperationException(
                    Environment.GetResourceString("InvalidOperation_AddContextFrozen"));
            }
    
            lock (this)
            {
                // Check if we have a property by this name
                CheckPropertyNameClash(prop.Name, _ctxProps, _numCtxProps);
                
                // check if we need to grow the array.
                if (_ctxProps == null || _numCtxProps == _ctxProps.Length)    
                {
                    _ctxProps = GrowPropertiesArray(_ctxProps);
                }
                // now add the property
                _ctxProps[_numCtxProps++] = prop;
            }
        }
    
        [System.Security.SecurityCritical]  // auto-generated
        internal virtual void  InternalFreeze()
        {
            _ctxFlags |= CTX_FROZEN;
            // From this point on attempts to add properties will throw
            // So we don't need to take a lock.
            for (int i=0; i<_numCtxProps; i++)
            {
                _ctxProps[i].Freeze(this);
            }
            
        }
        /// <internalonly/>
        [System.Security.SecurityCritical]  // auto-generated_required
        public virtual void Freeze()
        {
            lock(this) {
                if ((_ctxFlags & CTX_FROZEN) != 0)
                {
                    throw new InvalidOperationException(
                        Environment.GetResourceString(
                            "InvalidOperation_ContextAlreadyFrozen"));
                }
                InternalFreeze();
            }
        }
    
        internal virtual void SetThreadPoolAware()
        {
            // Cannot turn off ThreadPool support for the default context
            Contract.Assert(
                (_ctxFlags & CTX_DEFAULT_CONTEXT) == 0,
                "This operation is not allowed on the default context!");
            _ctxFlags |= CTX_THREADPOOL_AWARE;
        }
    
        internal virtual bool IsThreadPoolAware 
        {
            get { return (_ctxFlags & CTX_THREADPOOL_AWARE) == CTX_THREADPOOL_AWARE;}
        }
        
        /// <internalonly/>
        public virtual IContextProperty[] ContextProperties
        {
            // we return a copy of the current set of properties
            // the user may iterate from 0 to array.length on it.
            [System.Security.SecurityCritical]  // auto-generated_required
            get 
            {
                if (_ctxProps == null)
                {
                    return null;
                }   
                lock (this)
                {         
                    IContextProperty[] retProps = new IContextProperty[_numCtxProps];
                    Array.Copy(_ctxProps, retProps, _numCtxProps);
                    return retProps;
                }
            }
        }
    
        [System.Security.SecurityCritical]  // auto-generated
        internal static void CheckPropertyNameClash(String name, IContextProperty[] props, int count)
        {
            for (int i=0; i<count; i++)
            {
                if (props[i].Name.Equals(name))
                {
                    throw new InvalidOperationException(
                        Environment.GetResourceString(
                            "InvalidOperation_DuplicatePropertyName"));
                }
            }        
        }
    
        internal static IContextProperty[] GrowPropertiesArray(IContextProperty[] props)
        {
            // grow the array of IContextProperty objects
            int newSize = (props != null ? props.Length : 0)  + GROW_BY;
            IContextProperty[] newProps = new IContextProperty[newSize];
            if (props != null)
            {
                // Copy existing properties over.
                Array.Copy(props, newProps, props.Length);
            }
            return newProps;
        }
            
        [System.Security.SecurityCritical]  // auto-generated
        internal virtual IMessageSink GetServerContextChain()
        {
            if (_serverContextChain == null) 
            {
                // a bare chain would have just this one sink.
                IMessageSink newServerContextChain = ServerContextTerminatorSink.MessageSink;
    
                // now loop over properties to add some real sinks.
                Object prop = null;
                int iSink = _numCtxProps;
                while (iSink-- > 0) 
                {
                    // see if this property wants to contribute a ServerContextSink
                    // we have to start chaining in the reverse order
                    prop = _ctxProps[iSink];
                    IContributeServerContextSink sink = prop as IContributeServerContextSink;
                    if (null != sink )
                    {
                        // yes, chain the sink ahead of the chain of sinks constructed so far.
                        newServerContextChain = sink.GetServerContextSink( newServerContextChain);
                        if (newServerContextChain == null)
                        {
                            throw new RemotingException( 
                                Environment.GetResourceString(
                                    "Remoting_Contexts_BadProperty"));
                        }
                    }
                }
                lock (this)
                {  
                    if (_serverContextChain == null)
                    {
                        _serverContextChain = newServerContextChain;
                    }
                }
            }
            return _serverContextChain;
        }
    
        [System.Security.SecurityCritical]  // auto-generated
        internal virtual IMessageSink GetClientContextChain()
        {
            Message.DebugOut("Context::GetClientContextChain: IN _ctxID =" + _ctxID + Environment.NewLine);
            if (_clientContextChain == null)
            {
                Message.DebugOut("Context::GetClientContextChain: _clientContextChain == null, creating chain" + Environment.NewLine);
                // a bare chain would have just this one sink.
                IMessageSink newClientContextChain = ClientContextTerminatorSink.MessageSink;
    
                // now loop over properties to add some real sinks.
                // Note that for the client chain we go through the properties 
                // in the reverse order as compared to the server chain. 
                // Thus if a lock was taken as the last action of an incoming
                // call, it is released as the first action of an outgoing call.                
                Object prop = null;
                int iSink = 0;
                while (iSink < _numCtxProps) 
                {
                    Message.DebugOut("Context::GetClientContextChain: checking property " + 
                                     _ctxProps[iSink].Name + Environment.NewLine);
                    // see if this property wants to contribute a ClientContextSink
                    // we have to start chaining in the reverse order
                    prop = _ctxProps[iSink];
                    IContributeClientContextSink sink = prop as IContributeClientContextSink;
                    if (null != sink)
                    {
                        Message.DebugOut("Context::GetClientContextChain: calling GetClientContextSink on " + 
                                         _ctxProps[iSink].Name + Environment.NewLine);
                        // yes, chain the sink ahead of the chain of sinks constructed so far.
                        newClientContextChain = sink.GetClientContextSink(newClientContextChain);
                        if (newClientContextChain == null)
                        {
                            throw new RemotingException( 
                                Environment.GetResourceString(
                                    "Remoting_Contexts_BadProperty"));
                        }
                    }
                    iSink++;
                }
                // now check if we raced and set appropriately
                lock (this)
                {
                    if (_clientContextChain==null)
                    {
                        _clientContextChain = newClientContextChain;
                    }
                    // else the chain we created should get GC-ed.
                }            
            }
            return _clientContextChain;        
        }
    
        [System.Security.SecurityCritical]  // auto-generated
        internal virtual IMessageSink CreateServerObjectChain(MarshalByRefObject serverObj)
        {
            // a bare chain would just be the dispatcher sink       
            IMessageSink serverObjectChain = new ServerObjectTerminatorSink(serverObj);
            
            // now loop over properties to add some real sinks.
            Object prop = null;
            int iSink = _numCtxProps;
            while (iSink-- > 0) 
            {
                // see if this property wants to contribute a ServerObjectSink
                // we have to start chaining in the reverse order
                prop = _ctxProps[iSink];
                IContributeObjectSink sink = prop as IContributeObjectSink;
                if (null != sink)
                {
                    // yes, chain the sink ahead of the chain of sinks constructed so far.
                    serverObjectChain =  sink.GetObjectSink( serverObj, serverObjectChain);
                    if (serverObjectChain == null)
                    {
                            throw new RemotingException( 
                                Environment.GetResourceString(
                                    "Remoting_Contexts_BadProperty"));
                    }
                }
            }
            return serverObjectChain;
        }
    
        [System.Security.SecurityCritical]  // auto-generated
        internal virtual IMessageSink CreateEnvoyChain(MarshalByRefObject objectOrProxy)
        {
            // a bare chain would just be the dispatcher sink
            IMessageSink envoyChain = EnvoyTerminatorSink.MessageSink;
            
            // now loop over properties to add some real sinks.
            // Note: the sinks in the envoy chain should be in mirror image
            // order relative to sinks on the server side
            Object prop = null;
            int iSink = 0;
            
            MarshalByRefObject exposedObj = objectOrProxy;
        
            while (iSink < _numCtxProps) 
            {
                // see if this property wants to contribute a ClientContextSink
                // we have to start chaining in the reverse order
                prop = _ctxProps[iSink];
                IContributeEnvoySink sink = prop as IContributeEnvoySink; 
                if (null != sink)
                {
                    // yes, chain the sink ahead of the chain of sinks constructed so far.
                    envoyChain = sink.GetEnvoySink(exposedObj, envoyChain);
                    if (envoyChain == null)
                    {
                        throw new RemotingException( 
                            Environment.GetResourceString(
                                "Remoting_Contexts_BadProperty"));
                    }
                }
                iSink++;
            }
            return envoyChain;
        }
    
 
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        //~~~~~~~~~~~~~~~~~~~ Activation Support ~~~~~~~~~~~~~~~~~~~~~~~~
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        [System.Security.SecurityCritical]  // auto-generated
        internal IMessage NotifyActivatorProperties(
            IMessage msg, bool bServerSide)
        {
            Contract.Assert( (msg is IConstructionCallMessage) 
                                || (msg is IConstructionReturnMessage),
                            "Bad activation msg type");
            Contract.Assert( !((msg is IConstructionCallMessage) && 
                                (msg is IConstructionReturnMessage)),
                            "Activation message cannot be both call & return type");
                                                        
            Contract.Assert((_ctxFlags & CTX_FROZEN) == CTX_FROZEN,
                            "ServerContext not frozen during activation!");
 
 
            // Any exception thrown by the notifications is caught and 
            // folded into a return message to return to the caller.
            IMessage errMsg = null;
 
            try
            {
                // Since the context is frozen the properties array is in 
                // effect read-only! 
                int iProp = _numCtxProps;            
                Object prop = null;
                
                while (iProp-- != 0) 
                {
                    // see if this property is interested in Activation
                    prop = _ctxProps[iProp];
                    IContextPropertyActivator activator = prop as IContextPropertyActivator; 
                    if (null != activator)
                    {
                        // yes, notify as appropriate
                        IConstructionCallMessage ccm = msg as IConstructionCallMessage;
                        if (null != ccm)
                        {
                            // IConsructionCallMessage on its way forward
                            if (!bServerSide)
                            {
                                // activation starting at client side
                                activator.CollectFromClientContext(ccm);                       
                            }
                            else
                            {
                                // activation starting at server side
                                // 
                                activator.DeliverClientContextToServerContext(ccm);
                            }
                        }
                        else
                        {
                            // IConstructionReturnMessage on its way back
                            if (bServerSide)
                            {
                                // activation returning from server side
                                activator.CollectFromServerContext((IConstructionReturnMessage)msg);
                            }
                            else
                            {
                                // activation back at client side
                                // <
                                activator.DeliverServerContextToClientContext((IConstructionReturnMessage)msg);
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                IMethodCallMessage mcm = null;
                if (msg is IConstructionCallMessage)
                {
                    mcm = (IMethodCallMessage) msg;
                }
                else
                {
                    mcm = new ErrorMessage();
                }
                errMsg = new ReturnMessage(e, mcm);
                if (msg != null)
                {
                    ((ReturnMessage)errMsg).SetLogicalCallContext(
                            (LogicalCallContext)
                            msg.Properties[Message.CallContextKey]);
                }
            }
            return errMsg;
        }
    
    /// <internalonly/>
        public override String ToString()
        {
            return    "ContextID: " + _ctxID;
        }
 
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        //~~~~~~~~~~~~~~~~~~~ Transition Support ~~~~~~~~~~~~~~~~~~~~~~~~
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
        // This is the simple cross-context call back function invoke on
        // the context to do a call-back in.
        /// <internalonly/>
        [System.Security.SecurityCritical]  // auto-generated_required
        public void DoCallBack(CrossContextDelegate deleg)
        {
            /*DBG Console.WriteLine("public DoCallBack: targetCtx: " 
            + Int32.Format(this.InternalContextID,"x")); DBG*/
                
            if (deleg == null)
            {
                throw new ArgumentNullException("deleg");
            }
            Contract.EndContractBlock();
 
            if ((_ctxFlags & CTX_FROZEN) == 0)
            {
                throw new RemotingException( 
                    Environment.GetResourceString(
                        "Remoting_Contexts_ContextNotFrozenForCallBack"));
            }
 
            Context currCtx = Thread.CurrentContext;
            if (currCtx == this)
            {
                // We are already in the target context, just execute deleg
                // NOTE: If in the future we decide to leave the context
                // and reenter it for this case we will need to change 
                // Context::RequestCallBack method in VM also!
                deleg();
            }
            else
            {                               
                // We pass 0 for target domain ID for x-context case.
                currCtx.DoCallBackGeneric(this.InternalContextID, deleg);
                GC.KeepAlive(this);
            }
        }
 
        // This is called when EE needs to do a transition and execute some
        // code. Before calling, EE determines if this is a x-domain case.              
        // targetDomainID will be 0 if it is a simple x-context call.
        //<
        [System.Security.SecurityCritical]  // auto-generated
        internal static void DoCallBackFromEE(
            IntPtr targetCtxID, IntPtr privateData, int targetDomainID)
        {
            Contract.Assert(targetCtxID != IntPtr.Zero, "Bad transition context");
                
            /*DBG Console.WriteLine("private DoCallBackFromEE: targetCtx: " 
            + Int32.Format(targetCtxID,"x") 
            + " PvtData: " + Int32.Format(privateData,"x"));DBG*/
 
            if (targetDomainID == 0)
            {
                CallBackHelper cb = new CallBackHelper(
                                            privateData,
                                            true /*fromEE*/,
                                            targetDomainID); 
                CrossContextDelegate ctxDel = new CrossContextDelegate(cb.Func);
                Thread.CurrentContext.DoCallBackGeneric(targetCtxID, ctxDel);                                                                     
            }
            else
            {
                // for x-appdomain calls, we can't pass a delegate since that 
                //   would require us to deserialize it on the other side which
                //   is not allowed for non-public methods.
                TransitionCall msgCall = new TransitionCall(targetCtxID, privateData, targetDomainID);           
            
                Message.PropagateCallContextFromThreadToMessage(msgCall);
                //DBG Console.WriteLine("CallBackGeneric starting!");
                IMessage retMsg = Thread.CurrentContext.GetClientContextChain().SyncProcessMessage(msgCall); 
                Message.PropagateCallContextFromMessageToThread(retMsg);
            
                IMethodReturnMessage msg = retMsg as IMethodReturnMessage;
                if (null != msg)
                {
                    if (msg.Exception != null)
                        throw msg.Exception;
                }
            }
        } // DoCallBackFromEE
 
                // This is called by both the call back functions above.
        [System.Security.SecurityCritical]  // auto-generated
        internal void DoCallBackGeneric(
            IntPtr targetCtxID, CrossContextDelegate deleg)
        {               
            TransitionCall msgCall = new TransitionCall(targetCtxID, deleg);           
            
            Message.PropagateCallContextFromThreadToMessage(msgCall);
            //DBG Console.WriteLine("CallBackGeneric starting!");
            IMessage retMsg = this.GetClientContextChain().SyncProcessMessage(msgCall); 
            if (null != retMsg)
            {
                Message.PropagateCallContextFromMessageToThread(retMsg);
            }
            
            IMethodReturnMessage msg = retMsg as IMethodReturnMessage;
            if (null != msg)
            {
                if (msg.Exception != null)
                    throw msg.Exception;
            }
            //DBG Console.WriteLine("CallBackGeneric finished!");
        }
        
        // This is the E-Call that we route the EE-CallBack request to
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        internal static extern void ExecuteCallBackInEE(IntPtr privateData);        
 
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        //~~~~~~~~~~~~~~~~~~~~ Context Local Store  ~~~~~~~~~~~~~~~~~~~~
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
        private LocalDataStore MyLocalStore 
        {
            get 
            { 
                if (_localDataStore == null)
                {
                    // It's OK to lock the manager here because it is going to lock
                    // itself anyway.
                    lock (_localDataStoreMgr)
                    {
                        if (_localDataStore == null)
                        {
                            // The local store has not yet been created for this thread.
                            _localDataStore = _localDataStoreMgr.CreateLocalDataStore();
                        }
                    }
                }
                return _localDataStore.Store;
            }
        }
        
        //  All of these are exact shadows of corresponding ThreadLocalStore
        //  APIs.
        /// <internalonly/>
       [System.Security.SecurityCritical]  // auto-generated_required
        public static LocalDataStoreSlot AllocateDataSlot()
        {
            return _localDataStoreMgr.AllocateDataSlot();            
        }
        /// <internalonly/>
        [System.Security.SecurityCritical]  // auto-generated_required
        public static LocalDataStoreSlot AllocateNamedDataSlot(String name)
        {
            return _localDataStoreMgr.AllocateNamedDataSlot(name);            
        }
        /// <internalonly/>
        [System.Security.SecurityCritical]  // auto-generated_required
        public static LocalDataStoreSlot GetNamedDataSlot(String name)
        {
            return _localDataStoreMgr.GetNamedDataSlot(name);            
        }
        /// <internalonly/>
        [System.Security.SecurityCritical]  // auto-generated_required
        public static void FreeNamedDataSlot(String name)
        {
            _localDataStoreMgr.FreeNamedDataSlot(name);            
        }
        
        /// <internalonly/>
        [System.Security.SecurityCritical]  // auto-generated_required
        public static void SetData(LocalDataStoreSlot slot, Object data)
        {
                        Thread.CurrentContext.MyLocalStore.SetData(slot, data);                    
        }
        
        /// <internalonly/>
        [System.Security.SecurityCritical]  // auto-generated_required
        public static Object GetData(LocalDataStoreSlot slot)
        {
            return Thread.CurrentContext.MyLocalStore.GetData(slot);              
        }
        
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        //~~~~~~~~~~~~~~~~~~~~ Context Statics ~~~~~~~~~~~~~~~~~~~~~~~~~~
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        private int ReserveSlot()
        {
            // This will be called with the context crst held so we 
            // don't take a lock here)
            if (_ctxStatics == null)
            {
                // allocate the first bucket
                _ctxStatics = new Object[STATICS_BUCKET_SIZE];
                // set next bucket field to null
                _ctxStatics[0] = null;
                _ctxStaticsFreeIndex = 1;
                _ctxStaticsCurrentBucket = 0;
            }
 
            // See if we have to allocate a new bucket
            if (_ctxStaticsFreeIndex == STATICS_BUCKET_SIZE)
            {
                Object[] newBucket = new Object[STATICS_BUCKET_SIZE];
 
                // walk the chain to locate the last bucket
                Object[] bucket = _ctxStatics;
                while (bucket[0] != null)
                {
                    bucket = (Object[]) bucket[0];
                }
                // chain in the new bucket
                bucket[0] = newBucket;
                _ctxStaticsFreeIndex = 1;
                _ctxStaticsCurrentBucket++;
            }
 
            // bucket# in highWord, index in lowWord
            return _ctxStaticsFreeIndex++|_ctxStaticsCurrentBucket<<16;
        }
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        //~~~~~~~~~~~~~~~~~~~~ End Context Statics ~~~~~~~~~~~~~~~~~~~~~~
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        //~~~~~~~~~~~~~~~~~~~~ Dynamic Sink Support  ~~~~~~~~~~~~~~~~~~~~
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        //   This allows people to register a property implementing IContributeDynamicSink
        //   with the remoting service. Based on the obj and ctx parameters, the property
        //   is asked to contribute a sink that is placed at some location in the path of 
        //   remoting calls.
        //   The property may be unregistered, which implies that the sink will be dropped
        //   for subsequent remoting calls.
        //   If multiple properties are registered, their sinks may be called in an
        //   arbitrary order which may change between calls.
        //   
        //   If obj is non null then:
        //       - if it is a proxy, all calls made on the proxy are intercepted
    //       - if it is a real object, all calls on the object are intercepted
    //       - the ctx argument must be null
    //   If ctx is non-null then:
    //       - the obj argument must be null
    //       - all calls entering and leaving the context are intercepted
    //   If both ctx and obj are null then:
    //       - all calls entering and leaving all contexts are intercepted
    //
    /// <internalonly/>
        [System.Security.SecuritySafeCritical]  // auto-generated
        [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.Infrastructure)]
        public static bool RegisterDynamicProperty(IDynamicProperty prop, ContextBoundObject obj, Context ctx)
        {
            bool fRegistered = false;
 
            if (prop == null || prop.Name == null || !(prop is IContributeDynamicSink))
            {
                throw new ArgumentNullException("prop");
            }
            if (obj != null && ctx != null)
            {
                // Exactly one of these is allowed to be non-null.
                throw new ArgumentException(Environment.GetResourceString("Argument_NonNullObjAndCtx"));
            }
            if (obj != null)
            {
                // ctx is ignored and must be null.
                fRegistered = IdentityHolder.AddDynamicProperty(obj, prop);                
            }
            else
            {
                // ctx may or may not be null
                fRegistered = Context.AddDynamicProperty(ctx, prop);
            }
 
            return fRegistered;
        }
 
    /// <internalonly/>
        [System.Security.SecuritySafeCritical]  // auto-generated
        [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.Infrastructure)]
        public static bool UnregisterDynamicProperty(String name, ContextBoundObject obj, Context ctx)
        {
            // name, obj, ctx arguments should be exactly the same as a previous
            // RegisterDynamicProperty call
            if (name == null)
            {
                throw new ArgumentNullException("name");
            }
            if (obj != null && ctx != null)
            {
                throw new ArgumentException(Environment.GetResourceString("Argument_NonNullObjAndCtx"));
            }
            Contract.EndContractBlock();
 
            bool fUnregister = false;
 
            if (obj != null)
            {
                // ctx is ignored and must be null.
                fUnregister = IdentityHolder.RemoveDynamicProperty(obj, name);
            }
            else
            {
                // ctx may or may not be null
                fUnregister = Context.RemoveDynamicProperty(ctx, name);
            }
 
            return fUnregister;
        }
 
        /*
         *  Support for dynamic sinks at context level
         *
         */        
        [System.Security.SecurityCritical]  // auto-generated
        internal static bool AddDynamicProperty(Context ctx, IDynamicProperty prop)
        {
            // Check if we have a property by this name
            if (ctx != null)
            {
                return ctx.AddPerContextDynamicProperty(prop);
            }
            else
            {
                // We have to add a sink that should fire for all contexts
                return AddGlobalDynamicProperty(prop);
            }
        }
    
        [System.Security.SecurityCritical]  // auto-generated
        private bool AddPerContextDynamicProperty(IDynamicProperty prop)
        {
            if (_dphCtx == null)
            {
                DynamicPropertyHolder dph = new DynamicPropertyHolder();
                lock (this)
                {
                    if (_dphCtx == null)
                    {
                        _dphCtx = dph;
                    }
                }
            }
            return _dphCtx.AddDynamicProperty(prop);
        }
        
        [System.Security.SecurityCritical]  // auto-generated
        private static bool AddGlobalDynamicProperty(IDynamicProperty prop)
        {
            return _dphGlobal.AddDynamicProperty(prop);
        }
        
        [System.Security.SecurityCritical]  // auto-generated
        internal static bool RemoveDynamicProperty(Context ctx, String name)
        {
            if (ctx != null)
            {
                return ctx.RemovePerContextDynamicProperty(name);
            }
            else
            {
                // We have to remove a global property
                return RemoveGlobalDynamicProperty(name);                        
            }
        }
    
        [System.Security.SecurityCritical]  // auto-generated
        private bool RemovePerContextDynamicProperty(String name)
        {
            // We have to remove a property for this context
            if (_dphCtx == null)
            {
                throw new RemotingException(
                    String.Format(
                        CultureInfo.CurrentCulture, Environment.GetResourceString("Remoting_Contexts_NoProperty"),
                        name
                        ));
            }
            return _dphCtx.RemoveDynamicProperty(name);
        }
    
        [System.Security.SecurityCritical]  // auto-generated
        private static bool RemoveGlobalDynamicProperty(String name)
        {
            return _dphGlobal.RemoveDynamicProperty(name);
        }
    
        /*
         *  Returns an array of context specific dynamic properties
         *  registered for this context. The number of such properties
         *  is designated by length of the returned array.
         */
        internal virtual IDynamicProperty[] PerContextDynamicProperties
        {
            get 
            {
                if (_dphCtx == null)
                {
                    return null;
                }   
                else
                {
                    return _dphCtx.DynamicProperties;
                }
            }
        }
    
        /*
         *  Returns an array of global dynamic properties
         *  registered (for all contexts). The number of such properties
         *  is designated by length of the returned array.
         */    
        internal static ArrayWithSize GlobalDynamicSinks
        {
            [System.Security.SecurityCritical]  // auto-generated
            get 
            {
                return _dphGlobal.DynamicSinks;
            }
        }
    
        internal virtual ArrayWithSize DynamicSinks
        {
            [System.Security.SecurityCritical]  // auto-generated
            get
            {
                if (_dphCtx == null)
                {
                    return null;
                }
                else
                {
                    return _dphCtx.DynamicSinks;
                }
            }
        }    
 
        [System.Security.SecurityCritical]  // auto-generated
        internal virtual bool NotifyDynamicSinks(
            IMessage msg,
            bool bCliSide, 
            bool bStart, 
            bool bAsync,
            bool bNotifyGlobals)
        {
            bool bHasDynamicSinks = false;
        
            if (bNotifyGlobals && (_dphGlobal.DynamicProperties != null))
            {
                ArrayWithSize globalSinks = GlobalDynamicSinks;
                if (globalSinks != null)
                {
                    DynamicPropertyHolder.NotifyDynamicSinks(
                                    msg, 
                                    globalSinks, 
                                    bCliSide,
                                    bStart,
                                    bAsync);
                    bHasDynamicSinks = true;
                }
            }
            
            ArrayWithSize perCtxSinks = DynamicSinks;
            if (perCtxSinks != null)
            {
                DynamicPropertyHolder.NotifyDynamicSinks(
                                    msg, 
                                    perCtxSinks, 
                                    bCliSide,
                                    bStart,
                                    bAsync);
                bHasDynamicSinks = true;
            }
 
            return bHasDynamicSinks;
        } // NotifyDynamicSinks
        
        //******************** END: Dynamic Sink Support ********************        
    }
 
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~~~~~~~~~~~~~~~~~~~ More Transition Support ~~~~~~~~~~~~~~~~~~~
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // This class is used as the backing object that implements a delegate
    // function to be used for internal call-backs. The delegate type must
    // be CrossContextDelegate (void Func(void) below) since we are using 
    // the DoCallBackGeneric also as the underlying mechanism for the 
    // exposed cross-context callbacks.
    [Serializable]
    internal class CallBackHelper
    {
        // Some flag definitions
        internal const int RequestedFromEE = 0x00000001;   // callBack from EE
        internal const int XDomainTransition= 0x00000100;  // going x-domain
 
        internal bool IsEERequested 
        { 
            get {return (_flags&RequestedFromEE)==RequestedFromEE;} 
            set {if (value) {_flags |= RequestedFromEE;}}                   
        }
            
        internal bool IsCrossDomain
        {
            set {if (value) {_flags |= XDomainTransition;}}                 
        }
        
        int _flags;     
        IntPtr _privateData;
 
        internal CallBackHelper(IntPtr privateData, bool bFromEE, int targetDomainID)
        {
            this.IsEERequested = bFromEE;
            this.IsCrossDomain = (targetDomainID!=0);
            _privateData = privateData;
        }
 
        [System.Security.SecurityCritical]  // auto-generated
        internal void Func()
        {
            /*DBG Console.WriteLine("DelegHelper::Func CTX:" 
            + Int32.Format(Thread.CurrentContext.InternalContextID,"x")
            +Environment.NewLine + "DMN: " + Int32.Format(Thread.GetDomainID(),"x")); DBG*/
            if (IsEERequested)
            {
                //DBG Console.WriteLine("Executing EE callback ");
                
                // EE requested this call back, call EE with its private data
                Context.ExecuteCallBackInEE(_privateData);
                
                //DBG Console.WriteLine("Execute CallBackInEE returned: " + Int32.Format(_retVal,"x"));
            }                       
            else
            {                       
                //DBG Console.WriteLine("Executing non-EE internal callback");
            }                       
        }
    }       // class CallBackHelper
}       //nameSpace Remoting