The Labs \ Source Viewer \ SSCLI \ System.Runtime.Remoting.Lifetime \ LeaseSink

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. //+----------------------------------------------------------------------------
  16. //
  17. // Microsoft Windows
  18. // File: Lease.cs
  19. //
  20. // Contents: Lease class
  21. //
  22. // History: 1/5/00 Created
  23. //
  24. //+----------------------------------------------------------------------------
  25. namespace System.Runtime.Remoting.Lifetime
  26. {
  27.     using System;
  28.     using System.Security;
  29.     using System.Security.Permissions;
  30.     using System.Collections;
  31.     using System.Threading;
  32.     using System.Runtime.Remoting.Messaging;
  33.     using System.Runtime.Remoting.Proxies;
  34.     using System.Globalization;
  35.    
  36.     internal class Lease : MarshalByRefObject, ILease
  37.     {
  38.         internal int id = 0;
  39.        
  40.         // Lease Time
  41.         internal DateTime leaseTime;
  42.         internal TimeSpan initialLeaseTime;
  43.        
  44.         // Renewal Policies
  45.         internal TimeSpan renewOnCallTime;
  46.         internal TimeSpan sponsorshipTimeout;
  47.         internal bool isInfinite = false;
  48.        
  49.         // Sponsors
  50.         internal Hashtable sponsorTable;
  51.         internal int sponsorCallThread;
  52.        
  53.         // Links to leasemanager and managed object
  54.         internal LeaseManager leaseManager;
  55.         internal MarshalByRefObject managedObject;
  56.        
  57.         // State
  58.         internal LeaseState state;
  59.        
  60.         static internal int nextId = 0;
  61.        
  62.        
  63.         internal Lease(TimeSpan initialLeaseTime, TimeSpan renewOnCallTime, TimeSpan sponsorshipTimeout, MarshalByRefObject managedObject)
  64.         {
  65.             id = nextId++;
  66.             BCLDebug.Trace("REMOTE", "Lease Constructor ", managedObject, " initialLeaseTime " + initialLeaseTime + " renewOnCall " + renewOnCallTime + " sponsorshipTimeout ", sponsorshipTimeout);
  67.            
  68.             // Set Policy
  69.             this.renewOnCallTime = renewOnCallTime;
  70.             this.sponsorshipTimeout = sponsorshipTimeout;
  71.             this.initialLeaseTime = initialLeaseTime;
  72.             this.managedObject = managedObject;
  73.            
  74.             //Add lease to leaseManager
  75.             leaseManager = LeaseManager.GetLeaseManager();
  76.            
  77.             // Initialize tables
  78.             sponsorTable = new Hashtable(10);
  79.             state = LeaseState.Initial;
  80.         }
  81.        
  82.         internal void ActivateLease()
  83.         {
  84.             // Set leaseTime
  85.             leaseTime = DateTime.UtcNow.Add(initialLeaseTime);
  86.             state = LeaseState.Active;
  87.             leaseManager.ActivateLease(this);
  88.         }
  89.        
  90.         // Override MarshalByRefObject InitializeLifetimeService
  91.         // Don't want a lease on a lease therefore returns null
  92.         public override object InitializeLifetimeService()
  93.         {
  94.             BCLDebug.Trace("REMOTE", "Lease ", id, " InitializeLifetimeService, lease Marshalled");
  95.             return null;
  96.         }
  97.        
  98.         // ILease Property and Methods
  99.        
  100.         public TimeSpan RenewOnCallTime {
  101.             get { return renewOnCallTime; }
  102.             [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.RemotingConfiguration)]
  103.             set {
  104.                 if (state == LeaseState.Initial) {
  105.                     renewOnCallTime = value;
  106.                     BCLDebug.Trace("REMOTE", "Lease Set RenewOnCallProperty ", managedObject, " " + renewOnCallTime);
  107.                 }
  108.                 else
  109.                     throw new RemotingException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Remoting_Lifetime_InitialStateRenewOnCall"), ((Enum)state).ToString()));
  110.             }
  111.         }
  112.        
  113.         public TimeSpan SponsorshipTimeout {
  114.             get { return sponsorshipTimeout; }
  115.             [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.RemotingConfiguration)]
  116.             set {
  117.                 if (state == LeaseState.Initial) {
  118.                     sponsorshipTimeout = value;
  119.                     BCLDebug.Trace("REMOTE", "Lease Set SponsorshipTimeout Property ", managedObject, " " + sponsorshipTimeout);
  120.                 }
  121.                 else
  122.                     throw new RemotingException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Remoting_Lifetime_InitialStateSponsorshipTimeout"), ((Enum)state).ToString()));
  123.             }
  124.         }
  125.        
  126.         public TimeSpan InitialLeaseTime {
  127.             get { return initialLeaseTime; }
  128.            
  129.             [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.RemotingConfiguration)]
  130.             set {
  131.                 if (state == LeaseState.Initial) {
  132.                     initialLeaseTime = value;
  133.                     if (TimeSpan.Zero.CompareTo(value) >= 0)
  134.                         state = LeaseState.Null;
  135.                     BCLDebug.Trace("REMOTE", "Lease Set InitialLeaseTime Property ", managedObject, " " + InitialLeaseTime + ", current state " + ((Enum)state).ToString());
  136.                 }
  137.                 else
  138.                     throw new RemotingException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Remoting_Lifetime_InitialStateInitialLeaseTime"), ((Enum)state).ToString()));
  139.             }
  140.         }
  141.        
  142.         public TimeSpan CurrentLeaseTime {
  143.             get { return leaseTime.Subtract(DateTime.UtcNow); }
  144.         }
  145.        
  146.         public LeaseState CurrentState {
  147.             get { return state; }
  148.         }
  149.        
  150.        
  151.         [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.RemotingConfiguration)]
  152.         public void Register(ISponsor obj)
  153.         {
  154.             Register(obj, TimeSpan.Zero);
  155.         }
  156.        
  157.         [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.RemotingConfiguration)]
  158.         public void Register(ISponsor obj, TimeSpan renewalTime)
  159.         {
  160.             lock (this) {
  161.                 BCLDebug.Trace("REMOTE", "Lease " + id + " Register Sponsor renewalTime ", renewalTime, " state ", ((Enum)state).ToString());
  162.                 if (state == LeaseState.Expired || sponsorshipTimeout == TimeSpan.Zero)
  163.                     return;
  164.                
  165.                 object sponsorId = GetSponsorId(obj);
  166.                 lock (sponsorTable) {
  167.                     if (renewalTime > TimeSpan.Zero)
  168.                         AddTime(renewalTime);
  169.                     if (!sponsorTable.ContainsKey(sponsorId)) {
  170.                         // Place in tables
  171.                         sponsorTable[sponsorId] = new SponsorStateInfo(renewalTime, SponsorState.Initial);
  172.                     }
  173.                 }
  174.             }
  175.         }
  176.        
  177.         [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.RemotingConfiguration)]
  178.         public void Unregister(ISponsor sponsor)
  179.         {
  180.             lock (this) {
  181.                 BCLDebug.Trace("REMOTE", "Lease", id, " Unregister state ", ((Enum)state).ToString());
  182.                 if (state == LeaseState.Expired)
  183.                     return;
  184.                
  185.                 object sponsorId = GetSponsorId(sponsor);
  186.                 lock (sponsorTable) {
  187.                     if (sponsorId != null) {
  188.                         leaseManager.DeleteSponsor(sponsorId);
  189.                         SponsorStateInfo sponsorStateInfo = (SponsorStateInfo)sponsorTable[sponsorId];
  190.                         sponsorTable.Remove(sponsorId);
  191.                     }
  192.                 }
  193.             }
  194.         }
  195.        
  196.         // Get the local representative of the sponsor to prevent a remote access when placing
  197.         // in a hash table.
  198.         private object GetSponsorId(ISponsor obj)
  199.         {
  200.             object sponsorId = null;
  201.             if (obj != null) {
  202.                 if (RemotingServices.IsTransparentProxy(obj))
  203.                     sponsorId = RemotingServices.GetRealProxy(obj);
  204.                 else
  205.                     sponsorId = obj;
  206.             }
  207.             return sponsorId;
  208.         }
  209.        
  210.         // Convert from the local representative of the sponsor to either the MarshalByRefObject or local object
  211.         private ISponsor GetSponsorFromId(object sponsorId)
  212.         {
  213.             object sponsor = null;
  214.             RealProxy rp = sponsorId as RealProxy;
  215.             if (null != rp)
  216.                 sponsor = rp.GetTransparentProxy();
  217.             else
  218.                 sponsor = sponsorId;
  219.             return (ISponsor)sponsor;
  220.         }
  221.        
  222.         [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.RemotingConfiguration)]
  223.         public TimeSpan Renew(TimeSpan renewalTime)
  224.         {
  225.             return RenewInternal(renewalTime);
  226.         }
  227.        
  228.         // We will call this internally within the server domain
  229.         internal TimeSpan RenewInternal(TimeSpan renewalTime)
  230.         {
  231.             lock (this) {
  232.                 BCLDebug.Trace("REMOTE", "Lease ", id, " Renew ", renewalTime, " state ", ((Enum)state).ToString());
  233.                 if (state == LeaseState.Expired)
  234.                     return TimeSpan.Zero;
  235.                 AddTime(renewalTime);
  236.                 return leaseTime.Subtract(DateTime.UtcNow);
  237.             }
  238.         }
  239.        
  240.         // Used for a lease which has been created, but will not be used
  241.         internal void Remove()
  242.         {
  243.             BCLDebug.Trace("REMOTE", "Lease ", id, " Remove state ", ((Enum)state).ToString());
  244.             if (state == LeaseState.Expired)
  245.                 return;
  246.             state = LeaseState.Expired;
  247.             leaseManager.DeleteLease(this);
  248.         }
  249.        
  250.         internal void Cancel()
  251.         {
  252.             lock (this) {
  253.                 BCLDebug.Trace("REMOTE", "Lease ", id, " Cancel Managed Object ", managedObject, " state ", ((Enum)state).ToString());
  254.                
  255.                 if (state == LeaseState.Expired)
  256.                     return;
  257.                
  258.                 Remove();
  259.                 // Disconnect the object ...
  260.                 // We use the internal version of Disconnect passing "false"
  261.                 // for the bResetURI flag. This allows the object to keep its
  262.                 // old URI in case its lease gets reactivated later.
  263.                 RemotingServices.Disconnect(managedObject, false);
  264.                
  265.                 // Disconnect the lease for the object.
  266.                 RemotingServices.Disconnect(this);
  267.             }
  268.         }
  269.        
  270.        
  271.        
  272.         #if _DEBUG
  273.         ~Lease()
  274.         {
  275.             BCLDebug.Trace("REMOTE", "Lease ", id, " Finalize");
  276.         }
  277.         #endif
  278.        
  279.         internal void RenewOnCall()
  280.         {
  281.             lock (this) {
  282.                 //BCLDebug.Trace("REMOTE","Lease ",id," RenewOnCall state ",((Enum)state).ToString());
  283.                 if (state == LeaseState.Initial || state == LeaseState.Expired)
  284.                     return;
  285.                 AddTime(renewOnCallTime);
  286.             }
  287.         }
  288.        
  289.         internal void LeaseExpired(DateTime now)
  290.         {
  291.             lock (this) {
  292.                 BCLDebug.Trace("REMOTE", "Lease ", id, " LeaseExpired state ", ((Enum)state).ToString());
  293.                 if (state == LeaseState.Expired)
  294.                     return;
  295.                
  296.                 // There is a small window between the time the leaseManager
  297.                 // thread examines all the leases and tests for expiry and
  298.                 // when an indivisual lease is locked for expiry. The object
  299.                 // could get marshal-ed in this time which would reset its lease
  300.                 // Therefore we check again to see if we should indeed proceed
  301.                 // with the expire code (using the same value of 'now' as used
  302.                 // by the leaseManager thread)
  303.                 if (leaseTime.CompareTo(now) < 0)
  304.                     ProcessNextSponsor();
  305.             }
  306.         }
  307.        
  308.         internal delegate TimeSpan AsyncRenewal(ILease lease);
  309.        
  310.         internal void SponsorCall(ISponsor sponsor)
  311.         {
  312.             BCLDebug.Trace("REMOTE", "Lease ", id, " SponsorCall state ", ((Enum)state).ToString());
  313.             bool exceptionOccurred = false;
  314.             if (state == LeaseState.Expired)
  315.                 return;
  316.            
  317.             lock (sponsorTable) {
  318.                 try {
  319.                     object sponsorId = GetSponsorId(sponsor);
  320.                     sponsorCallThread = Thread.CurrentThread.GetHashCode();
  321.                     AsyncRenewal ar = new AsyncRenewal(sponsor.Renewal);
  322.                     SponsorStateInfo sponsorStateInfo = (SponsorStateInfo)sponsorTable[sponsorId];
  323.                     sponsorStateInfo.sponsorState = SponsorState.Waiting;
  324.                    
  325.                     // The first parameter should be the lease we are trying to renew.
  326.                     IAsyncResult iar = ar.BeginInvoke(this, new AsyncCallback(this.SponsorCallback), null);
  327.                     if ((sponsorStateInfo.sponsorState == SponsorState.Waiting) && (state != LeaseState.Expired)) {
  328.                         // Even if we get here, the operation could still complete before
  329.                         // we call the the line below. This seems to be a race.
  330.                        
  331.                         // Sponsor could have completed before statement is reached, so only execute
  332.                         // if the sponsor state is still waiting
  333.                         leaseManager.RegisterSponsorCall(this, sponsorId, sponsorshipTimeout);
  334.                     }
  335.                     sponsorCallThread = 0;
  336.                 }
  337.                 catch (Exception) {
  338.                     // Sponsor not avaiable
  339.                     exceptionOccurred = true;
  340.                    
  341.                     sponsorCallThread = 0;
  342.                 }
  343.             }
  344.            
  345.             if (exceptionOccurred) {
  346.                 BCLDebug.Trace("REMOTE", "Lease ", id, " SponsorCall Sponsor Exception ");
  347.                 Unregister(sponsor);
  348.                 ProcessNextSponsor();
  349.             }
  350.         }
  351.        
  352.         internal void SponsorTimeout(object sponsorId)
  353.         {
  354.             lock (this) {
  355.                 if (!sponsorTable.ContainsKey(sponsorId))
  356.                     return;
  357.                 lock (sponsorTable) {
  358.                     SponsorStateInfo sponsorStateInfo = (SponsorStateInfo)sponsorTable[sponsorId];
  359.                     BCLDebug.Trace("REMOTE", "Lease ", id, " SponsorTimeout sponsorState ", ((Enum)sponsorStateInfo.sponsorState).ToString());
  360.                     if (sponsorStateInfo.sponsorState == SponsorState.Waiting) {
  361.                         Unregister(GetSponsorFromId(sponsorId));
  362.                         ProcessNextSponsor();
  363.                     }
  364.                 }
  365.             }
  366.         }
  367.        
  368.         private void ProcessNextSponsor()
  369.         {
  370.             BCLDebug.Trace("REMOTE", "Lease ", id, " ProcessNextSponsor");
  371.            
  372.             object largestSponsor = null;
  373.             TimeSpan largestRenewalTime = TimeSpan.Zero;
  374.            
  375.            
  376.             lock (sponsorTable) {
  377.                 IDictionaryEnumerator e = sponsorTable.GetEnumerator();
  378.                 // Find sponsor with largest previous renewal value
  379.                 while (e.MoveNext()) {
  380.                     object sponsorId = e.Key;
  381.                     SponsorStateInfo sponsorStateInfo = (SponsorStateInfo)e.Value;
  382.                     if ((sponsorStateInfo.sponsorState == SponsorState.Initial) && (largestRenewalTime == TimeSpan.Zero)) {
  383.                         largestRenewalTime = sponsorStateInfo.renewalTime;
  384.                         largestSponsor = sponsorId;
  385.                     }
  386.                     else if (sponsorStateInfo.renewalTime > largestRenewalTime) {
  387.                         largestRenewalTime = sponsorStateInfo.renewalTime;
  388.                         largestSponsor = sponsorId;
  389.                     }
  390.                 }
  391.             }
  392.            
  393.             if (largestSponsor != null)
  394.                 SponsorCall(GetSponsorFromId(largestSponsor));
  395.             else {
  396.                 // No more sponsors to try, Cancel
  397.                 BCLDebug.Trace("REMOTE", "Lease ", id, " ProcessNextSponsor no more sponsors");
  398.                 Cancel();
  399.             }
  400.         }
  401.        
  402.        
  403.         // This gets called when we explicitly transfer the call back from the
  404.         // called function to a threadpool thread.
  405.         internal void SponsorCallback(object obj)
  406.         {
  407.             SponsorCallback((IAsyncResult)obj);
  408.         }
  409.        
  410.         // On another thread
  411.         internal void SponsorCallback(IAsyncResult iar)
  412.         {
  413.             BCLDebug.Trace("REMOTE", "Lease ", id, " SponsorCallback IAsyncResult ", iar, " state ", ((Enum)state).ToString());
  414.             if (state == LeaseState.Expired) {
  415.                 return;
  416.             }
  417.            
  418.             int thisThread = Thread.CurrentThread.GetHashCode();
  419.             if (thisThread == sponsorCallThread) {
  420.                 WaitCallback threadFunc = new WaitCallback(this.SponsorCallback);
  421.                 ThreadPool.QueueUserWorkItem(threadFunc, iar);
  422.                 return;
  423.             }
  424.            
  425.             AsyncResult asyncResult = (AsyncResult)iar;
  426.             AsyncRenewal ar = (AsyncRenewal)asyncResult.AsyncDelegate;
  427.             ISponsor sponsor = (ISponsor)ar.Target;
  428.             SponsorStateInfo sponsorStateInfo = null;
  429.             if (iar.IsCompleted) {
  430.                 // Sponsor came back with renewal
  431.                 BCLDebug.Trace("REMOTE", "Lease ", id, " SponsorCallback sponsor completed");
  432.                 bool exceptionOccurred = false;
  433.                 TimeSpan renewalTime = TimeSpan.Zero;
  434.                 try {
  435.                     renewalTime = (TimeSpan)ar.EndInvoke(iar);
  436.                 }
  437.                 catch (Exception) {
  438.                     // Sponsor not avaiable
  439.                     exceptionOccurred = true;
  440.                 }
  441.                 if (exceptionOccurred) {
  442.                     BCLDebug.Trace("REMOTE", "Lease ", id, " SponsorCallback Sponsor Exception ");
  443.                     Unregister(sponsor);
  444.                     ProcessNextSponsor();
  445.                 }
  446.                 else {
  447.                     object sponsorId = GetSponsorId(sponsor);
  448.                     lock (sponsorTable) {
  449.                         if (sponsorTable.ContainsKey(sponsorId)) {
  450.                             sponsorStateInfo = (SponsorStateInfo)sponsorTable[sponsorId];
  451.                             sponsorStateInfo.sponsorState = SponsorState.Completed;
  452.                             sponsorStateInfo.renewalTime = renewalTime;
  453.                         }
  454.                         else {
  455.                             // Sponsor was deleted, possibly from a sponsor time out
  456.                         }
  457.                     }
  458.                    
  459.                     if (sponsorStateInfo == null) {
  460.                         // Sponsor was deleted
  461.                         ProcessNextSponsor();
  462.                     }
  463.                     else if (sponsorStateInfo.renewalTime == TimeSpan.Zero) {
  464.                         BCLDebug.Trace("REMOTE", "Lease ", id, " SponsorCallback sponsor did not renew ");
  465.                         Unregister(sponsor);
  466.                         ProcessNextSponsor();
  467.                     }
  468.                     else
  469.                         Renew(sponsorStateInfo.renewalTime);
  470.                 }
  471.             }
  472.             else {
  473.                 // Sponsor timed out
  474.                 // Note time outs should be handled by the LeaseManager
  475.                 BCLDebug.Trace("REMOTE", "Lease ", id, " SponsorCallback sponsor did not complete, timed out");
  476.                 Unregister(sponsor);
  477.                 ProcessNextSponsor();
  478.             }
  479.         }
  480.        
  481.        
  482.        
  483.         private void AddTime(TimeSpan renewalSpan)
  484.         {
  485.             if (state == LeaseState.Expired)
  486.                 return;
  487.            
  488.             DateTime now = DateTime.UtcNow;
  489.             DateTime oldLeaseTime = leaseTime;
  490.             DateTime renewTime = now.Add(renewalSpan);
  491.             if (leaseTime.CompareTo(renewTime) < 0) {
  492.                 leaseManager.ChangedLeaseTime(this, renewTime);
  493.                 leaseTime = renewTime;
  494.                 state = LeaseState.Active;
  495.             }
  496.             //BCLDebug.Trace("REMOTE","Lease ",id," AddTime renewalSpan ",renewalSpan," current Time ",now," old leaseTime ",oldLeaseTime," new leaseTime ",leaseTime," state ",((Enum)state).ToString());
  497.         }
  498.        
  499.        
  500.        
  501.         [Serializable()]
  502.         internal enum SponsorState
  503.         {
  504.             Initial = 0,
  505.             Waiting = 1,
  506.             Completed = 2
  507.         }
  508.        
  509.         internal sealed class SponsorStateInfo
  510.         {
  511.             internal TimeSpan renewalTime;
  512.             internal SponsorState sponsorState;
  513.            
  514.             internal SponsorStateInfo(TimeSpan renewalTime, SponsorState sponsorState)
  515.             {
  516.                 this.renewalTime = renewalTime;
  517.                 this.sponsorState = sponsorState;
  518.             }
  519.         }
  520.     }
  521.    
  522.     internal class LeaseSink : IMessageSink
  523.     {
  524.         Lease lease = null;
  525.         IMessageSink nextSink = null;
  526.        
  527.         public LeaseSink(Lease lease, IMessageSink nextSink)
  528.         {
  529.             this.lease = lease;
  530.             this.nextSink = nextSink;
  531.         }
  532.        
  533.         //IMessageSink methods
  534.         public IMessage SyncProcessMessage(IMessage msg)
  535.         {
  536.             //BCLDebug.Trace("REMOTE","Lease ",id," SyncProcessMessage");
  537.             lease.RenewOnCall();
  538.             return nextSink.SyncProcessMessage(msg);
  539.         }
  540.        
  541.         public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
  542.         {
  543.             //BCLDebug.Trace("REMOTE","Lease ",id," AsyncProcessMessage");
  544.             lease.RenewOnCall();
  545.             return nextSink.AsyncProcessMessage(msg, replySink);
  546.         }
  547.        
  548.         public IMessageSink NextSink {
  549. //BCLDebug.Trace("REMOTE","Lease ",id," NextSink");
  550.             get { return nextSink; }
  551.         }
  552.     }
  553. }

Developer Fusion