3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // Synchronization Property for URT Contexts. Uses the ThreadPool API.
8 // An instance of this property in a context enforces a synchronization
9 // domain for the context (and all contexts that share the same instance).
10 // This means that at any instant, at most 1 thread could be executing
11 // in all contexts that share an instance of this property.
13 // This is done by contributing sinks that intercept and serialize in-coming
14 // calls for the respective contexts.
16 // If the property is marked for re-entrancy, then call-outs are
17 // intercepted too. The call-out interception allows other waiting threads
18 // to enter the synchronization domain for maximal throughput.
20 namespace System
.Runtime
.Remoting
.Contexts
{
21 using System
.Threading
;
22 using System
.Runtime
.Remoting
;
23 using System
.Runtime
.Remoting
.Messaging
;
24 using System
.Runtime
.Remoting
.Activation
;
25 using System
.Security
.Permissions
;
27 using System
.Diagnostics
.Contracts
;
28 using Queue
= System
.Collections
.Queue
;
29 using ArrayList
= System
.Collections
.ArrayList
;
30 [System
.Security
.SecurityCritical
] // auto-generated_required
32 [AttributeUsage(AttributeTargets
.Class
)]
33 [SecurityPermissionAttribute(SecurityAction
.InheritanceDemand
, Flags
=SecurityPermissionFlag
.Infrastructure
)]
34 [System
.Runtime
.InteropServices
.ComVisible(true)]
35 public class SynchronizationAttribute
36 : ContextAttribute
, IContributeServerContextSink
,
37 IContributeClientContextSink
39 // The class should not be instantiated in a context that has Synchronization
40 public const int NOT_SUPPORTED
= 0x00000001;
42 // The class does not care if the context has Synchronization or not
43 public const int SUPPORTED
= 0x00000002;
45 // The class should be instantiated in a context that has Synchronization
46 public const int REQUIRED
= 0x00000004;
48 // The class should be instantiated in a context with a new instance of
49 // Synchronization property each time
50 public const int REQUIRES_NEW
= 0x00000008;
52 private const String PROPERTY_NAME
= "Synchronization";
54 private static readonly int _timeOut
= -1;
55 // event that releases a thread-pool worker thread
57 internal AutoResetEvent _asyncWorkEvent
;
59 private RegisteredWaitHandle _waitHandle
;
61 // queue of work items.
63 internal Queue _workItemQueue
;
64 // flag for the domain lock (access always synchronized on the _workItemQueue)
66 internal bool _locked
;
67 // flag to indicate if the lock should be released during call-outs
68 internal bool _bReEntrant
;
69 // flag for use as an attribute on types
73 private SynchronizationAttribute _cliCtxAttr
;
74 // Logical call id (used only in non-reentrant case for deadlock avoidance)
76 private String _syncLcid
;
78 private ArrayList _asyncLcidList
;
81 public virtual bool Locked {get { return _locked;}
set { _locked=value; }
}
82 public virtual bool IsReEntrant { get { return _bReEntrant;}
}
84 internal String SyncCallOutLCID
90 "Should not use this for the reentrant case");
99 "Should not use this for the reentrant case");
103 || (_syncLcid
!=null && value==null)
104 || _syncLcid
.Equals(value),
105 "context can be associated with one logical call at a time");
111 internal ArrayList AsyncCallOutLCIDList
113 get { return _asyncLcidList; }
116 internal bool IsKnownLCID(IMessage reqMsg
)
119 ((LogicalCallContext
)reqMsg
.Properties
[Message
.CallContextKey
])
120 .RemotingData
.LogicalCallID
;
121 return ( msgLCID
.Equals(_syncLcid
)
122 || _asyncLcidList
.Contains(msgLCID
));
128 * Constructor for the synchronized dispatch property
130 public SynchronizationAttribute()
132 : this(REQUIRED
, false) {
137 * If reEntrant is true, we allow other calls to come in
138 * if the currently running call leaves the domain for a call-out.
140 public SynchronizationAttribute(bool reEntrant
)
142 : this(REQUIRED
, reEntrant
) {
145 public SynchronizationAttribute(int flag
)
147 : this(flag
, false) {
150 public SynchronizationAttribute(int flag
, bool reEntrant
)
152 // Invoke ContextProperty ctor!
153 : base(PROPERTY_NAME
) {
155 _bReEntrant
= reEntrant
;
166 throw new ArgumentException(Environment
.GetResourceString("Argument_InvalidFlag"), "flag");
170 // Dispose off the WaitHandle registered in Initialization
171 internal void Dispose()
173 //Unregister the RegisteredWaitHandle
174 if (_waitHandle
!= null)
175 _waitHandle
.Unregister(null);
178 // Override ContextAttribute's implementation of IContextAttribute::IsContextOK
179 [System
.Security
.SecurityCritical
]
180 [System
.Runtime
.InteropServices
.ComVisible(true)]
181 public override bool IsContextOK(Context ctx
, IConstructionCallMessage msg
)
184 throw new ArgumentNullException("ctx");
186 throw new ArgumentNullException("msg");
187 Contract
.EndContractBlock();
192 if (_flavor
== REQUIRES_NEW
)
195 // Each activation request instantiates a new attribute class.
196 // We are relying on that for the REQUIRES_NEW case!
197 Contract
.Assert(ctx
.GetProperty(PROPERTY_NAME
) != this,
198 "ctx.GetProperty(PROPERTY_NAME) != this");
202 SynchronizationAttribute syncProp
= (SynchronizationAttribute
) ctx
.GetProperty(PROPERTY_NAME
);
203 if ( ( (_flavor
== NOT_SUPPORTED
)&&(syncProp
!= null) )
204 || ( (_flavor
== REQUIRED
)&&(syncProp
== null) )
210 if (_flavor
== REQUIRED
)
212 // pick up the property from the current context
213 _cliCtxAttr
= syncProp
;
219 // Override ContextAttribute's impl. of IContextAttribute::GetPropForNewCtx
220 [System
.Security
.SecurityCritical
]
221 [System
.Runtime
.InteropServices
.ComVisible(true)]
222 public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg
)
224 if ( (_flavor
==NOT_SUPPORTED
) || (_flavor
==SUPPORTED
) || (null == ctorMsg
) )
229 if (_cliCtxAttr
!= null)
231 Contract
.Assert(_flavor
== REQUIRED
,"Use cli-ctx property only for the REQUIRED flavor");
232 ctorMsg
.ContextProperties
.Add((IContextProperty
)_cliCtxAttr
);
237 ctorMsg
.ContextProperties
.Add((IContextProperty
)this);
241 // We need this to make the use of the property as an attribute
242 // light-weight. This allows us to delay initialize everything we
243 // need to fully function as a ContextProperty.
244 internal virtual void InitIfNecessary()
248 if (_asyncWorkEvent
== null)
250 // initialize thread pool event to non-signaled state.
251 _asyncWorkEvent
= new AutoResetEvent(false);
253 _workItemQueue
= new Queue();
254 _asyncLcidList
= new ArrayList();
256 WaitOrTimerCallback callBackDelegate
=
257 new WaitOrTimerCallback(this.DispatcherCallBack
);
259 // Register a callback to be executed by the thread-pool
260 // each time the event is signaled.
261 _waitHandle
= ThreadPool
.RegisterWaitForSingleObject(
266 false); // bExecuteOnlyOnce
272 * Call back function -- executed for each work item that
273 * was enqueued. This is invoked by a thread-pool thread for
274 * async work items and the caller thread for sync items.
276 private void DispatcherCallBack(Object stateIgnored
, bool ignored
)
278 // This function should be called by only one thread at a time. We will
279 // ensure this by releasing exactly one waiting thread to go work on
282 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] --- In DispatherCallBack ");
284 Contract
.Assert(_locked
==true,"_locked==true");
286 // get the work item out of the queue.
287 lock (_workItemQueue
)
289 work
= (WorkItem
) _workItemQueue
.Dequeue();
290 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] --- Dequeued Work for: " + work._thread.GetHashCode());
292 Contract
.Assert(work
!=null,"work!=null");
293 Contract
.Assert(work
.IsSignaled() && !(work
.IsDummy()),"work.IsSignaled() && !(work.IsDummy())");
294 // execute the work item (WorkItem.Execute will switch to the proper context)
295 ExecuteWorkItem(work
);
296 HandleWorkCompletion();
297 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] --- CallBack finished for: " + work._thread.GetHashCode());
301 * This is used by the call-out (client context) sinks to notify
302 * the domain manager that the thread is leaving
304 internal virtual void HandleThreadExit()
306 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] ~~~~ Thread EXIT ~~~~");
307 // For now treat this as if the work was completed!
308 Contract
.Assert(_locked
==true,"_locked==true");
309 HandleWorkCompletion();
313 * This is used by a returning call-out thread to request
314 * that it be queued for re-entry into the domain.
316 internal virtual void HandleThreadReEntry()
318 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] ~~~~ Thread REQUEST REENTRY ~~~~");
319 // Treat this as if a new work item needs to be done
322 WorkItem work
= new WorkItem(null, null, null);
324 HandleWorkRequest(work
);
328 * This gets called at the end of work.Execute and from
329 * HandleThreadExit() in the re-entrant scenario.
330 * This is the point where we decide what to do next!
332 internal virtual void HandleWorkCompletion()
334 // We should still have the lock held for the workItem that just completed
335 Contract
.Assert(_locked
==true,"_locked==true");
336 // Now we check the queue to see if we need to release any one?
337 WorkItem nextWork
= null;
338 bool bNotify
= false;
339 lock (_workItemQueue
)
341 if (_workItemQueue
.Count
>= 1)
343 nextWork
= (WorkItem
) _workItemQueue
.Peek();
345 nextWork
.SetSignaled();
349 // We set locked to false only in the case there is no
350 // next work to be done.
351 // NOTE: this is the only place _locked in ever set to false!
352 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] Domain UNLOCKED!");
356 // See if we found a non-signaled work item at the head.
359 // In both sync and async cases we just hand off the _locked state to
360 // the next thread which will execute.
361 if (nextWork
.IsAsync())
363 // Async-WorkItem: signal ThreadPool event to release one thread
364 _asyncWorkEvent
.Set();
365 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] ### Signal " + nextWork._thread.GetHashCode() + (nextWork.IsDummy()?" DUMMY ":" REAL "));
369 // Sync-WorkItem: notify the waiting sync-thread.
372 Monitor
.Pulse(nextWork
);
373 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] ~~~ Notify " + nextWork._thread.GetHashCode() + (nextWork.IsDummy()?" DUMMY ":" REAL ") );
380 * This is called by any new incoming thread or from
381 * HandleThreadReEntry() when a call-out thread wants to
382 * re-enter the domain.
383 * In the latter case, the WorkItem is a dummy item, it
384 * just serves the purpose of something to block on till
385 * the thread is given a green signal to re-enter.
387 internal virtual void HandleWorkRequest(WorkItem work
)
394 // Check for nested call backs
395 if (!IsNestedCall(work
._reqMsg
))
397 // See what type of work it is
400 // Async work is always queued.
402 // Enqueue the workItem
403 lock (_workItemQueue
)
405 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] ### Async Item EnQueue " + work._thread.GetHashCode());
407 _workItemQueue
.Enqueue(work
);
408 // If this is the only work item in the queue we will
409 // have to trigger the thread-pool event ourselves
410 if ( (!_locked
) && (_workItemQueue
.Count
== 1) )
412 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] ### Async Signal Self: " + work._thread.GetHashCode());
414 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] ### Domain Locked!");
416 _asyncWorkEvent
.Set();
422 // Sync work is queued only if there are other items
423 // already in the queue.
426 // Enqueue if we need to
429 if ((!_locked
) && (_workItemQueue
.Count
== 0))
432 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] ### Domain Locked!");
437 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] ~~~ ENQUEUE Sync!" + (work.IsDummy()?" DUMMY ":" REAL ") + work._thread);
440 _workItemQueue
.Enqueue(work
);
446 // If we queued a work item we must wait for some
447 // other thread to peek at it and Notify us.
449 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] ~~~ WORK::WAIT" + work._thread);
451 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] ~~~ FINISH Work::WAIT" + work._thread);
452 Contract
.Assert(_locked
==true,"_locked==true");
453 // Our turn to complete the work!
454 // Execute the callBack only if this is real work
457 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] ~~~ Invoke DispatcherCallBack " + work._thread);
458 // We invoke the callback here that does exactly
459 // what we need to do ... dequeue work, execute, checkForMore
460 DispatcherCallBack(null, true);
464 // DummyWork is just use to block/unblock a returning call.
465 // Throw away our dummy WorkItem.
468 _workItemQueue
.Dequeue();
470 // We don't check for more work here since we are already
471 // in the midst of an executing WorkItem (at the end of which
472 // the check will be performed)
477 // We did not queue the work item.
480 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] ~~~ Execute direct" + work._thread);
482 Contract
.Assert(_locked
==true,"_locked==true");
484 ExecuteWorkItem(work
);
485 // Check for more work
486 HandleWorkCompletion();
494 // We allow the nested calls to execute directly
496 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] ~~~ Execute Nested Call direct" + work._thread);
498 Contract
.Assert(_locked
==true,"_locked==true");
501 // We are still inside the top level call ...
502 // so after work.Execute finishes we don't check for more work
503 // or unlock the domain as we do elsewhere.
507 internal void ExecuteWorkItem(WorkItem work
)
512 internal bool IsNestedCall(IMessage reqMsg
)
514 // This returns TRUE only if it is a non-reEntrant context
516 // (the LCID of the reqMsg matches that of
517 // the top level sync call lcid associated with the context.
519 // it matches one of the async call out lcids)
521 bool bNested
= false;
524 String lcid
= SyncCallOutLCID
;
527 // This means we are inside a top level call
528 LogicalCallContext callCtx
=
529 (LogicalCallContext
)reqMsg
.Properties
[Message
.CallContextKey
];
531 if ( callCtx
!=null &&
532 lcid
.Equals(callCtx
.RemotingData
.LogicalCallID
))
534 // This is a nested call (we made a call out during
535 // the top level call and eventually that has resulted
536 // in an incoming call with the same lcid)
540 if (!bNested
&& AsyncCallOutLCIDList
.Count
>0)
542 // This means we are inside a top level call
543 LogicalCallContext callCtx
=
544 (LogicalCallContext
)reqMsg
.Properties
[Message
.CallContextKey
];
545 if (AsyncCallOutLCIDList
.Contains(callCtx
.RemotingData
.LogicalCallID
))
556 * Implements IContributeServerContextSink::GetServerContextSink
557 * Create a SynchronizedDispatch sink and return it.
559 [System
.Security
.SecurityCritical
]
560 public virtual IMessageSink
GetServerContextSink(IMessageSink nextSink
)
564 SynchronizedServerContextSink propertySink
=
565 new SynchronizedServerContextSink(
569 return (IMessageSink
) propertySink
;
573 * Implements IContributeClientContextSink::GetClientContextSink
574 * Create a CallOut sink and return it.
576 [System
.Security
.SecurityCritical
]
577 public virtual IMessageSink
GetClientContextSink(IMessageSink nextSink
)
581 SynchronizedClientContextSink propertySink
=
582 new SynchronizedClientContextSink(
586 return (IMessageSink
) propertySink
;
591 /*************************************** SERVER SINK ********************************/
593 * Implements the sink contributed by the Synch-Dispatch
594 * Property. The sink holds a back pointer to the property.
595 * The sink intercepts incoming calls to objects resident in
596 * the Context and co-ordinates with the property to enforce
599 internal class SynchronizedServerContextSink
600 : InternalSink
, IMessageSink
602 internal IMessageSink _nextSink
;
603 [System
.Security
.SecurityCritical
] // auto-generated
604 internal SynchronizationAttribute _property
;
606 [System
.Security
.SecurityCritical
] // auto-generated
607 internal SynchronizedServerContextSink(SynchronizationAttribute prop
, IMessageSink nextSink
)
610 _nextSink
= nextSink
;
613 [System
.Security
.SecuritySafeCritical
] // auto-generated
614 ~
SynchronizedServerContextSink()
620 * Implements IMessageSink::SyncProcessMessage
622 [System
.Security
.SecurityCritical
] // auto-generated
623 public virtual IMessage
SyncProcessMessage(IMessage reqMsg
)
625 // 1. Create a work item
626 WorkItem work
= new WorkItem(reqMsg
,
628 null /* replySink */);
630 // 2. Notify the property to handle the WorkItem
631 // The work item may get put in a Queue or may execute directly
632 // if the domain is free.
633 _property
.HandleWorkRequest(work
);
635 // 3. Pick up retMsg from the WorkItem and return
636 return work
.ReplyMessage
;
640 * Implements IMessageSink::AsyncProcessMessage
642 [System
.Security
.SecurityCritical
] // auto-generated
643 public virtual IMessageCtrl
AsyncProcessMessage(IMessage reqMsg
, IMessageSink replySink
)
645 // 1. Create a work item
646 WorkItem work
= new WorkItem(reqMsg
,
650 // 2. We always queue the work item in async case
651 _property
.HandleWorkRequest(work
);
652 // 3. Return an IMsgCtrl
657 * Implements IMessageSink::GetNextSink
659 public IMessageSink NextSink
661 [System
.Security
.SecurityCritical
] // auto-generated
669 //*************************************** WORK ITEM ********************************//
671 * A work item holds the info about a call to Sync or
672 * Async-ProcessMessage.
674 internal class WorkItem
676 private const int FLG_WAITING
= 0x0001;
677 private const int FLG_SIGNALED
= 0x0002;
678 private const int FLG_ASYNC
= 0x0004;
679 private const int FLG_DUMMY
= 0x0008;
682 internal IMessage _reqMsg
;
683 internal IMessageSink _nextSink
;
684 // ReplySink will be null for an sync work item.
685 internal IMessageSink _replySink
;
686 // ReplyMsg is set once the sync call is completed
687 internal IMessage _replyMsg
;
689 // Context in which the work should execute.
690 internal Context _ctx
;
692 [System
.Security
.SecurityCritical
] // auto-generated
693 internal LogicalCallContext _callCtx
;
694 internal static InternalCrossContextDelegate _xctxDel
= new InternalCrossContextDelegate(ExecuteCallback
);
697 //internal int _thread;
699 [System
.Security
.SecuritySafeCritical
] // auto-generated
704 [System
.Security
.SecurityCritical
] // auto-generated
705 internal WorkItem(IMessage reqMsg
, IMessageSink nextSink
, IMessageSink replySink
)
709 _nextSink
= nextSink
;
710 _replySink
= replySink
;
711 _ctx
= Thread
.CurrentContext
;
712 _callCtx
= Thread
.CurrentThread
.GetMutableExecutionContext().LogicalCallContext
;
714 //_thread = Thread.CurrentThread.GetHashCode();
717 // To mark a work item being enqueued
718 internal virtual void SetWaiting()
720 Contract
.Assert(!IsWaiting(),"!IsWaiting()");
721 _flags
|= FLG_WAITING
;
724 internal virtual bool IsWaiting()
726 return (_flags
&FLG_WAITING
) == FLG_WAITING
;
729 // To mark a work item that has been given the green light!
730 internal virtual void SetSignaled()
732 Contract
.Assert(!IsSignaled(),"!IsSignaled()");
733 _flags
|= FLG_SIGNALED
;
736 internal virtual bool IsSignaled()
738 return (_flags
& FLG_SIGNALED
) == FLG_SIGNALED
;
741 internal virtual void SetAsync()
746 internal virtual bool IsAsync()
748 return (_flags
& FLG_ASYNC
) == FLG_ASYNC
;
751 internal virtual void SetDummy()
756 internal virtual bool IsDummy()
758 return (_flags
& FLG_DUMMY
) == FLG_DUMMY
;
762 [System
.Security
.SecurityCritical
] // auto-generated
763 internal static Object
ExecuteCallback(Object
[] args
)
765 WorkItem This
= (WorkItem
) args
[0];
769 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] AsyncWork.Execute");
770 This
._nextSink
.AsyncProcessMessage(This
._reqMsg
, This
._replySink
);
772 else if (This
._nextSink
!= null)
774 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] SyncWork.Execute");
775 This
._replyMsg
= This
._nextSink
.SyncProcessMessage(This
._reqMsg
);
781 * Execute is called to complete a work item (sync or async).
782 * Execute assumes that the context is set correctly and the lock
783 * is taken (i.e. it makes no policy decisions)
785 * It is called from the following 3 points:
786 * 1. thread pool thread executing the callback for an async item
787 * 2. calling thread executing the callback for a queued sync item
788 * 3. calling thread directly calling Execute for a non-queued sync item
790 [System
.Security
.SecurityCritical
] // auto-generated
791 internal virtual void Execute()
793 // Execute should be called with the domain policy enforced
794 // i.e. a Synchronization domain should be locked etc ...
795 Contract
.Assert(IsSignaled(),"IsSignaled()");
797 Thread
.CurrentThread
.InternalCrossContextCallback(_ctx
, _xctxDel
, new Object
[] { this }
);
799 internal virtual IMessage ReplyMessage { get {return _replyMsg;}}
802 //*************************************** CLIENT SINK ********************************//
805 * Implements the client context sink contributed by the
806 * Property. The sink holds a back pointer to the property.
807 * The sink intercepts outgoing calls from objects the Context
808 * and co-ordinates with the property to enforce the domain policy.
810 internal class SynchronizedClientContextSink
811 : InternalSink
, IMessageSink
813 internal IMessageSink _nextSink
;
814 [System
.Security
.SecurityCritical
] // auto-generated
815 internal SynchronizationAttribute _property
;
817 [System
.Security
.SecurityCritical
] // auto-generated
818 internal SynchronizedClientContextSink(SynchronizationAttribute prop
, IMessageSink nextSink
)
821 _nextSink
= nextSink
;
824 [System
.Security
.SecuritySafeCritical
] // auto-generated
825 ~
SynchronizedClientContextSink()
831 * Implements IMessageSink::SyncProcessMessage for the call-out sink
833 [System
.Security
.SecurityCritical
] // auto-generated
834 public virtual IMessage
SyncProcessMessage(IMessage reqMsg
)
836 Contract
.Assert(_property
.Locked
== true,"_property.Locked == true");
838 if (_property
.IsReEntrant
)
840 // In this case we are required to let anybody waiting for
841 // the domain to enter and execute
842 // Notify the property that we are leaving
843 _property
.HandleThreadExit();
845 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] R: Sync call-out");
846 replyMsg
= _nextSink
.SyncProcessMessage(reqMsg
);
848 // We will just block till we are given permission to re-enter
849 // Notify the property that we wish to re-enter the domain.
850 // This will block the thread here if someone is in the domain.
851 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] R: Sync call-out returned, waiting for lock");
852 _property
.HandleThreadReEntry();
853 Contract
.Assert(_property
.Locked
== true,"_property.Locked == true");
857 // In the non-reentrant case we are just a pass-through sink
858 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] NR: Sync call-out (pass through)");
859 // We should mark the domain with our LCID so that call-backs are allowed to enter..
860 LogicalCallContext cctx
=
861 (LogicalCallContext
) reqMsg
.Properties
[Message
.CallContextKey
];
863 String lcid
= cctx
.RemotingData
.LogicalCallID
;
867 // We used to assign call-ids in RemotingProxy.cs at the
868 // start of each Invoke. As an optimization we now do it
869 // here in a delayed fashion... since currently only
870 // Synchronization needs it
871 // Note that for Sync-calls we would just inherit an LCID
872 // if the call has one, if not we create one. However for
873 // async calls we always generate a new LCID.
874 lcid
= Identity
.GetNewLogicalCallID();
875 cctx
.RemotingData
.LogicalCallID
= lcid
;
879 _property
.SyncCallOutLCID
== null,
880 "Synchronization domain is already in a callOut state");
883 bool bTopLevel
=false;
884 if (_property
.SyncCallOutLCID
==null)
886 _property
.SyncCallOutLCID
= lcid
;
890 Contract
.Assert(lcid
.Equals(_property
.SyncCallOutLCID
), "Bad synchronization domain state!");
892 replyMsg
= _nextSink
.SyncProcessMessage(reqMsg
);
894 // if a top level call out returned we clear the callId in the domain
897 _property
.SyncCallOutLCID
= null;
899 // The sync callOut is done, we do not need the lcid
900 // that was associated with the call any more.
901 // (clear it only if we added one to the reqMsg)
904 // Note that we make changes to the callCtx in
905 // the reply message ... since this is the one that
906 // will get installed back on the thread that called
908 LogicalCallContext cctxRet
=
909 (LogicalCallContext
) replyMsg
.Properties
[Message
.CallContextKey
];
912 "CallContext should be non-null");
913 cctxRet
.RemotingData
.LogicalCallID
= null;
917 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] NR: Sync call-out returned");
923 * Implements IMessageSink::AsyncProcessMessage for the call-out sink
925 [System
.Security
.SecurityCritical
] // auto-generated
926 public virtual IMessageCtrl
AsyncProcessMessage(IMessage reqMsg
, IMessageSink replySink
)
928 IMessageCtrl msgCtrl
= null;
930 Contract
.Assert(_property
.Locked
== true,"_property.Locked == true");
932 if (!_property
.IsReEntrant
)
934 // In this case new calls are not allowed to enter the domain
935 // We need to track potentially more than one async-call-outs
936 // and allow the completion notifications to come in for those
938 LogicalCallContext cctx
=
939 (LogicalCallContext
) reqMsg
.Properties
[Message
.CallContextKey
];
940 // We used to generate a new lcid automatically in RemotingProxy
941 // Invoke at the start of each Async call.
942 // However now we do it here as an optimization (since only
943 // Synchronization needs it)
944 // RemotingProxy invoke code does Clone() the callContext at
945 // the start of each Async call so we don't have to worry
946 // about stomping someone else's lcid here.
949 String lcid
= Identity
.GetNewLogicalCallID();
950 cctx
.RemotingData
.LogicalCallID
= lcid
;
954 _property
.SyncCallOutLCID
== null,
955 "Cannot handle async call outs when already in a top-level sync call out");
956 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] NR: Async CallOut: adding to lcidList: " + lcid);
957 _property
.AsyncCallOutLCIDList
.Add(lcid
);
959 // We will call AsyncProcessMessage directly on this thread
960 // since the thread should not block much. However, we will
961 // have to intercept the callback on the replySink chain for
962 // which we wrap the caller provided replySink into our sink.
963 AsyncReplySink mySink
= new AsyncReplySink(replySink
, _property
);
965 // NOTE: we will need to yield the Synchronization Domain at
966 // some time or another to get our own callBack to complete.
968 // Note that for the Async call-outs we have to provide an interception
969 // sink whether we are re-entrant or not since we want
970 // the replySink.SyncProcessMessage call to be wait for the lock just like
971 // any other call-in.
972 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] Async call-out");
974 msgCtrl
= _nextSink
.AsyncProcessMessage(reqMsg
, (IMessageSink
)mySink
);
975 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] Async call-out AsyncPM returned, reply to come separately");
981 * Implements IMessageSink::GetNextSink
983 public IMessageSink NextSink
985 [System
.Security
.SecurityCritical
] // auto-generated
994 * This class just implements the CallBack sink we provide to
995 * intercept the callback of an Async out-call. The CallBack sink
996 * ensures that arbitrary threads do not enter our Synchronization
997 * Domain without asking us if it is Ok!
999 internal class AsyncReplySink
: IMessageSink
1001 internal IMessageSink _nextSink
;
1002 [System
.Security
.SecurityCritical
] // auto-generated
1003 internal SynchronizationAttribute _property
;
1004 [System
.Security
.SecurityCritical
] // auto-generated
1005 internal AsyncReplySink(IMessageSink nextSink
, SynchronizationAttribute prop
)
1007 _nextSink
= nextSink
;
1011 [System
.Security
.SecurityCritical
] // auto-generated
1012 public virtual IMessage
SyncProcessMessage(IMessage reqMsg
)
1015 // We handle this as a regular new Sync workItem
1016 // 1. Create a work item
1017 WorkItem work
= new WorkItem(reqMsg
,
1019 null /* replySink */);
1021 // 2. Notify the property to handle the WorkItem
1022 // The work item may get put in a Queue or may execute right away.
1023 _property
.HandleWorkRequest(work
);
1025 if (!_property
.IsReEntrant
)
1027 // Remove the async lcid we had added to the call out list.
1028 //DBGConsole.WriteLine(Thread.CurrentThread.GetHashCode()+"] NR: InterceptionSink::SyncPM Removing async call-out lcid: " + ((LogicalCallContext)reqMsg.Properties[Message.CallContextKey]).RemotingData.LogicalCallID);
1029 _property
.AsyncCallOutLCIDList
.Remove(
1030 ((LogicalCallContext
)reqMsg
.Properties
[Message
.CallContextKey
]).RemotingData
.LogicalCallID
);
1033 // 3. Pick up retMsg from the WorkItem and return
1034 return work
.ReplyMessage
;
1037 [System
.Security
.SecurityCritical
] // auto-generated
1038 public virtual IMessageCtrl
AsyncProcessMessage(IMessage reqMsg
, IMessageSink replySink
)
1040 throw new NotSupportedException();
1044 * Implements IMessageSink::GetNextSink
1046 public IMessageSink NextSink
1048 [System
.Security
.SecurityCritical
] // auto-generated