3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 //+----------------------------------------------------------------------------
11 // Contents: Lease class
13 // History: 1/5/00 <EMAIL>[....]</EMAIL> Created
15 //+----------------------------------------------------------------------------
17 namespace System.Runtime.Remoting.Lifetime
20 using System.Security;
21 using System.Security.Permissions;
22 using System.Collections;
23 using System.Threading;
24 using System.Runtime.Remoting.Messaging;
25 using System.Runtime.Remoting.Proxies;
26 using System.Globalization;
28 internal class Lease : MarshalByRefObject, ILease
33 internal DateTime leaseTime;
34 internal TimeSpan initialLeaseTime;
37 internal TimeSpan renewOnCallTime;
38 internal TimeSpan sponsorshipTimeout;
41 internal Hashtable sponsorTable;
42 internal int sponsorCallThread;
44 // Links to leasemanager and managed object
45 internal LeaseManager leaseManager;
46 internal MarshalByRefObject managedObject;
49 internal LeaseState state;
51 internal static volatile int nextId = 0;
54 internal Lease(TimeSpan initialLeaseTime,
55 TimeSpan renewOnCallTime,
56 TimeSpan sponsorshipTimeout,
57 MarshalByRefObject managedObject
61 BCLDebug.Trace("REMOTE", "Lease Constructor ",managedObject," initialLeaseTime "+initialLeaseTime+" renewOnCall "+renewOnCallTime+" sponsorshipTimeout ",sponsorshipTimeout);
64 this.renewOnCallTime = renewOnCallTime;
65 this.sponsorshipTimeout = sponsorshipTimeout;
66 this.initialLeaseTime = initialLeaseTime;
67 this.managedObject = managedObject;
69 //Add lease to leaseManager
70 leaseManager = LeaseManager.GetLeaseManager();
73 sponsorTable = new Hashtable(10);
74 state = LeaseState.Initial;
77 internal void ActivateLease()
80 leaseTime = DateTime.UtcNow.Add(initialLeaseTime);
81 state = LeaseState.Active;
82 leaseManager.ActivateLease(this);
85 // Override MarshalByRefObject InitializeLifetimeService
86 // Don't want a lease on a lease therefore returns null
87 [System.Security.SecurityCritical] // auto-generated
88 public override Object InitializeLifetimeService()
90 BCLDebug.Trace("REMOTE", "Lease ",id," InitializeLifetimeService, lease Marshalled");
94 // ILease Property and Methods
96 public TimeSpan RenewOnCallTime
98 [System.Security.SecurityCritical] // auto-generated
99 get { return renewOnCallTime; }
100 [System.Security.SecurityCritical] // auto-generated
101 [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration)]
104 if (state == LeaseState.Initial)
106 renewOnCallTime = value;
107 BCLDebug.Trace("REMOTE", "Lease Set RenewOnCallProperty ",managedObject," "+renewOnCallTime);
110 throw new RemotingException(Environment.GetResourceString("Remoting_Lifetime_InitialStateRenewOnCall", ((Enum)state).ToString()));
114 public TimeSpan SponsorshipTimeout
116 [System.Security.SecurityCritical] // auto-generated
117 get { return sponsorshipTimeout; }
118 [System.Security.SecurityCritical] // auto-generated
119 [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration)]
122 if (state == LeaseState.Initial)
124 sponsorshipTimeout = value;
125 BCLDebug.Trace("REMOTE", "Lease Set SponsorshipTimeout Property ",managedObject," "+sponsorshipTimeout);
128 throw new RemotingException(Environment.GetResourceString("Remoting_Lifetime_InitialStateSponsorshipTimeout", ((Enum)state).ToString()));
132 public TimeSpan InitialLeaseTime
134 [System.Security.SecurityCritical] // auto-generated
135 get { return initialLeaseTime; }
137 [System.Security.SecurityCritical] // auto-generated
138 [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration)]
141 if (state == LeaseState.Initial)
143 initialLeaseTime = value;
144 if (TimeSpan.Zero.CompareTo(value) >= 0)
145 state = LeaseState.Null;
146 BCLDebug.Trace("REMOTE", "Lease Set InitialLeaseTime Property ",managedObject," "+InitialLeaseTime+", current state "+((Enum)state).ToString());
149 throw new RemotingException(Environment.GetResourceString("Remoting_Lifetime_InitialStateInitialLeaseTime", ((Enum)state).ToString()));
153 public TimeSpan CurrentLeaseTime
155 [System.Security.SecurityCritical] // auto-generated
156 get { return leaseTime.Subtract(DateTime.UtcNow); }
159 public LeaseState CurrentState
161 [System.Security.SecurityCritical] // auto-generated
166 [System.Security.SecurityCritical] // auto-generated
167 [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration)]
168 public void Register(ISponsor obj)
170 Register(obj, TimeSpan.Zero);
173 [System.Security.SecurityCritical] // auto-generated
174 [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration)]
175 public void Register(ISponsor obj, TimeSpan renewalTime)
179 BCLDebug.Trace("REMOTE", "Lease "+id+" Register Sponsor renewalTime ",renewalTime," state ",((Enum)state).ToString());
180 if (state == LeaseState.Expired || sponsorshipTimeout == TimeSpan.Zero)
183 Object sponsorId = GetSponsorId(obj);
186 if (renewalTime > TimeSpan.Zero)
187 AddTime(renewalTime);
188 if (!sponsorTable.ContainsKey(sponsorId))
191 sponsorTable[sponsorId] = new SponsorStateInfo(renewalTime, SponsorState.Initial);
197 [System.Security.SecurityCritical] // auto-generated
198 [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration)]
199 public void Unregister(ISponsor sponsor)
203 BCLDebug.Trace("REMOTE", "Lease",id," Unregister state ",((Enum)state).ToString());
204 if (state == LeaseState.Expired)
207 Object sponsorId = GetSponsorId(sponsor);
210 if (sponsorId != null)
212 leaseManager.DeleteSponsor(sponsorId);
213 SponsorStateInfo sponsorStateInfo = (SponsorStateInfo)sponsorTable[sponsorId];
214 sponsorTable.Remove(sponsorId);
220 // Get the local representative of the sponsor to prevent a remote access when placing
222 [System.Security.SecurityCritical] // auto-generated
223 private Object GetSponsorId(ISponsor obj)
225 Object sponsorId = null;
228 if (RemotingServices.IsTransparentProxy(obj))
229 sponsorId = RemotingServices.GetRealProxy(obj);
236 // Convert from the local representative of the sponsor to either the MarshalByRefObject or local object
237 [System.Security.SecurityCritical] // auto-generated
238 private ISponsor GetSponsorFromId(Object sponsorId)
240 Object sponsor = null;
241 RealProxy rp = sponsorId as RealProxy;
243 sponsor = rp.GetTransparentProxy();
246 return (ISponsor)sponsor;
249 [System.Security.SecurityCritical] // auto-generated
250 [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration)]
251 public TimeSpan Renew(TimeSpan renewalTime)
253 return RenewInternal(renewalTime);
256 // We will call this internally within the server domain
257 internal TimeSpan RenewInternal(TimeSpan renewalTime)
261 BCLDebug.Trace("REMOTE","Lease ",id," Renew ",renewalTime," state ",((Enum)state).ToString());
262 if (state == LeaseState.Expired)
263 return TimeSpan.Zero;
264 AddTime(renewalTime);
265 return leaseTime.Subtract(DateTime.UtcNow);
269 // Used for a lease which has been created, but will not be used
270 internal void Remove()
272 BCLDebug.Trace("REMOTE","Lease ",id," Remove state ",((Enum)state).ToString());
273 if (state == LeaseState.Expired)
275 state = LeaseState.Expired;
276 leaseManager.DeleteLease(this);
279 [System.Security.SecurityCritical] // auto-generated
280 internal void Cancel()
284 BCLDebug.Trace("REMOTE","Lease ",id," Cancel Managed Object ",managedObject," state ",((Enum)state).ToString());
286 if (state == LeaseState.Expired)
290 // Disconnect the object ...
291 // We use the internal version of Disconnect passing "false"
292 // for the bResetURI flag. This allows the object to keep its
293 // old URI in case its lease gets reactivated later.
294 RemotingServices.Disconnect(managedObject, false);
296 // Disconnect the lease for the object.
297 RemotingServices.Disconnect(this);
306 BCLDebug.Trace("REMOTE","Lease ",id," Finalize");
310 internal void RenewOnCall()
314 //BCLDebug.Trace("REMOTE","Lease ",id," RenewOnCall state ",((Enum)state).ToString());
315 if (state == LeaseState.Initial || state == LeaseState.Expired)
317 AddTime(renewOnCallTime);
321 [System.Security.SecurityCritical] // auto-generated
322 internal void LeaseExpired(DateTime now)
326 BCLDebug.Trace("REMOTE","Lease ",id," LeaseExpired state ",((Enum)state).ToString());
327 if (state == LeaseState.Expired)
330 // There is a small window between the time the leaseManager
331 // thread examines all the leases and tests for expiry and
332 // when an indivisual lease is locked for expiry. The object
333 // could get marshal-ed in this time which would reset its lease
334 // Therefore we check again to see if we should indeed proceed
335 // with the expire code (using the same value of 'now' as used
336 // by the leaseManager thread)
337 if (leaseTime.CompareTo(now) < 0)
338 ProcessNextSponsor();
342 internal delegate TimeSpan AsyncRenewal(ILease lease);
344 [System.Security.SecurityCritical] // auto-generated
345 internal void SponsorCall(ISponsor sponsor)
347 BCLDebug.Trace("REMOTE","Lease ",id," SponsorCall state ",((Enum)state).ToString());
348 bool exceptionOccurred = false;
349 if (state == LeaseState.Expired)
356 Object sponsorId = GetSponsorId(sponsor);
357 sponsorCallThread = Thread.CurrentThread.GetHashCode();
358 AsyncRenewal ar = new AsyncRenewal(sponsor.Renewal);
359 SponsorStateInfo sponsorStateInfo = (SponsorStateInfo)sponsorTable[sponsorId];
360 sponsorStateInfo.sponsorState = SponsorState.Waiting;
362 // The first parameter should be the lease we are trying to renew.
363 IAsyncResult iar = ar.BeginInvoke(this, new AsyncCallback(this.SponsorCallback), null);
364 if ((sponsorStateInfo.sponsorState == SponsorState.Waiting) && (state != LeaseState.Expired))
366 // Even if we get here, the operation could still complete before
367 // we call the the line below. This seems to be a ----.
369 // Sponsor could have completed before statement is reached, so only execute
370 // if the sponsor state is still waiting
371 leaseManager.RegisterSponsorCall(this, sponsorId, sponsorshipTimeout);
373 sponsorCallThread = 0;
376 // Sponsor not avaiable
377 exceptionOccurred = true;
379 sponsorCallThread = 0;
383 if (exceptionOccurred)
385 BCLDebug.Trace("REMOTE","Lease ",id," SponsorCall Sponsor Exception ");
387 ProcessNextSponsor();
391 [System.Security.SecurityCritical] // auto-generated
392 internal void SponsorTimeout(Object sponsorId)
396 if (!sponsorTable.ContainsKey(sponsorId))
400 SponsorStateInfo sponsorStateInfo = (SponsorStateInfo)sponsorTable[sponsorId];
401 BCLDebug.Trace("REMOTE","Lease ",id," SponsorTimeout sponsorState ",((Enum)sponsorStateInfo.sponsorState).ToString());
402 if (sponsorStateInfo.sponsorState == SponsorState.Waiting)
404 Unregister(GetSponsorFromId(sponsorId));
405 ProcessNextSponsor();
411 [System.Security.SecurityCritical] // auto-generated
412 private void ProcessNextSponsor()
414 BCLDebug.Trace("REMOTE","Lease ",id," ProcessNextSponsor");
416 Object largestSponsor = null;
417 TimeSpan largestRenewalTime = TimeSpan.Zero;
422 IDictionaryEnumerator e = sponsorTable.GetEnumerator();
423 // Find sponsor with largest previous renewal value
426 Object sponsorId = e.Key;
427 SponsorStateInfo sponsorStateInfo = (SponsorStateInfo)e.Value;
428 if ((sponsorStateInfo.sponsorState == SponsorState.Initial) && (largestRenewalTime == TimeSpan.Zero))
430 largestRenewalTime = sponsorStateInfo.renewalTime;
431 largestSponsor = sponsorId;
433 else if (sponsorStateInfo.renewalTime > largestRenewalTime)
435 largestRenewalTime = sponsorStateInfo.renewalTime;
436 largestSponsor = sponsorId;
441 if (largestSponsor != null)
442 SponsorCall(GetSponsorFromId(largestSponsor));
445 // No more sponsors to try, Cancel
446 BCLDebug.Trace("REMOTE","Lease ",id," ProcessNextSponsor no more sponsors");
452 // This gets called when we explicitly transfer the call back from the
453 // called function to a threadpool thread.
454 [System.Security.SecurityCritical] // auto-generated
455 internal void SponsorCallback(Object obj)
457 SponsorCallback((IAsyncResult)obj);
461 [System.Security.SecurityCritical] // auto-generated
462 internal void SponsorCallback(IAsyncResult iar)
464 BCLDebug.Trace("REMOTE","Lease ",id," SponsorCallback IAsyncResult ",iar," state ",((Enum)state).ToString());
465 if (state == LeaseState.Expired)
470 int thisThread = Thread.CurrentThread.GetHashCode();
471 if (thisThread == sponsorCallThread)
473 // Looks like something went wrong and the thread that
474 // did the AsyncRenewal::BeginInvoke is executing the callback
475 // We will queue the work to the thread pool (otherwise there
476 // is a possibility of stack overflow if all sponsors are down)
477 WaitCallback threadFunc = new WaitCallback(this.SponsorCallback);
478 ThreadPool.QueueUserWorkItem(threadFunc, iar);
482 AsyncResult asyncResult = (AsyncResult)iar;
483 AsyncRenewal ar = (AsyncRenewal)asyncResult.AsyncDelegate;
484 ISponsor sponsor = (ISponsor)ar.Target;
485 SponsorStateInfo sponsorStateInfo = null;
488 // Sponsor came back with renewal
489 BCLDebug.Trace("REMOTE","Lease ",id," SponsorCallback sponsor completed");
490 bool exceptionOccurred = false;
491 TimeSpan renewalTime = TimeSpan.Zero;
494 renewalTime = (TimeSpan)ar.EndInvoke(iar);
497 // Sponsor not avaiable
498 exceptionOccurred = true;
500 if (exceptionOccurred)
502 BCLDebug.Trace("REMOTE","Lease ",id," SponsorCallback Sponsor Exception ");
504 ProcessNextSponsor();
508 Object sponsorId = GetSponsorId(sponsor);
511 if (sponsorTable.ContainsKey(sponsorId))
513 sponsorStateInfo = (SponsorStateInfo)sponsorTable[sponsorId];
514 sponsorStateInfo.sponsorState = SponsorState.Completed;
515 sponsorStateInfo.renewalTime = renewalTime;
519 // Sponsor was deleted, possibly from a sponsor time out
523 if (sponsorStateInfo == null)
525 // Sponsor was deleted
526 ProcessNextSponsor();
528 else if (sponsorStateInfo.renewalTime == TimeSpan.Zero)
530 BCLDebug.Trace("REMOTE","Lease ",id," SponsorCallback sponsor did not renew ");
532 ProcessNextSponsor();
535 RenewInternal(sponsorStateInfo.renewalTime);
541 // Note time outs should be handled by the LeaseManager
542 BCLDebug.Trace("REMOTE","Lease ",id," SponsorCallback sponsor did not complete, timed out");
544 ProcessNextSponsor();
550 private void AddTime(TimeSpan renewalSpan)
552 if (state == LeaseState.Expired)
555 DateTime now = DateTime.UtcNow;
556 DateTime oldLeaseTime = leaseTime;
557 DateTime renewTime = now.Add(renewalSpan);
558 if (leaseTime.CompareTo(renewTime) < 0)
560 leaseManager.ChangedLeaseTime(this, renewTime);
561 leaseTime = renewTime;
562 state = LeaseState.Active;
564 //BCLDebug.Trace("REMOTE","Lease ",id," AddTime renewalSpan ",renewalSpan," current Time ",now," old leaseTime ",oldLeaseTime," new leaseTime ",leaseTime," state ",((Enum)state).ToString());
570 internal enum SponsorState
577 internal sealed class SponsorStateInfo
579 internal TimeSpan renewalTime;
580 internal SponsorState sponsorState;
582 internal SponsorStateInfo(TimeSpan renewalTime, SponsorState sponsorState)
584 this.renewalTime = renewalTime;
585 this.sponsorState = sponsorState;
590 internal class LeaseSink : IMessageSink
593 IMessageSink nextSink = null;
595 public LeaseSink(Lease lease, IMessageSink nextSink)
598 this.nextSink = nextSink;
601 //IMessageSink methods
602 [System.Security.SecurityCritical] // auto-generated
603 public IMessage SyncProcessMessage(IMessage msg)
605 //BCLDebug.Trace("REMOTE","Lease ",id," SyncProcessMessage");
607 return nextSink.SyncProcessMessage(msg);
610 [System.Security.SecurityCritical] // auto-generated
611 public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
613 //BCLDebug.Trace("REMOTE","Lease ",id," AsyncProcessMessage");
615 return nextSink.AsyncProcessMessage(msg, replySink);
618 public IMessageSink NextSink
620 [System.Security.SecurityCritical] // auto-generated
623 //BCLDebug.Trace("REMOTE","Lease ",id," NextSink");