|
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
//+----------------------------------------------------------------------------
//
// Microsoft Windows
// File: Lease.cs
//
// Contents: Lease class
//
// History: 1/5/00 <EMAIL>Microsoft</EMAIL> Created
//
//+----------------------------------------------------------------------------
namespace System.Runtime.Remoting.Lifetime
{
using System;
using System.Security;
using System.Security.Permissions;
using System.Collections;
using System.Threading;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using System.Globalization;
internal class Lease : MarshalByRefObject, ILease
{
internal int id = 0;
// Lease Time
internal DateTime leaseTime;
internal TimeSpan initialLeaseTime;
// Renewal Policies
internal TimeSpan renewOnCallTime;
internal TimeSpan sponsorshipTimeout;
// Sponsors
internal Hashtable sponsorTable;
internal int sponsorCallThread;
// Links to leasemanager and managed object
internal LeaseManager leaseManager;
internal MarshalByRefObject managedObject;
// State
internal LeaseState state;
internal static volatile int nextId = 0;
internal Lease(TimeSpan initialLeaseTime,
TimeSpan renewOnCallTime,
TimeSpan sponsorshipTimeout,
MarshalByRefObject managedObject
)
{
id = nextId++;
BCLDebug.Trace("REMOTE", "Lease Constructor ",managedObject," initialLeaseTime "+initialLeaseTime+" renewOnCall "+renewOnCallTime+" sponsorshipTimeout ",sponsorshipTimeout);
// Set Policy
this.renewOnCallTime = renewOnCallTime;
this.sponsorshipTimeout = sponsorshipTimeout;
this.initialLeaseTime = initialLeaseTime;
this.managedObject = managedObject;
//Add lease to leaseManager
leaseManager = LeaseManager.GetLeaseManager();
// Initialize tables
sponsorTable = new Hashtable(10);
state = LeaseState.Initial;
}
internal void ActivateLease()
{
// Set leaseTime
leaseTime = DateTime.UtcNow.Add(initialLeaseTime);
state = LeaseState.Active;
leaseManager.ActivateLease(this);
}
// Override MarshalByRefObject InitializeLifetimeService
// Don't want a lease on a lease therefore returns null
[System.Security.SecurityCritical] // auto-generated
public override Object InitializeLifetimeService()
{
BCLDebug.Trace("REMOTE", "Lease ",id," InitializeLifetimeService, lease Marshalled");
return null;
}
// ILease Property and Methods
public TimeSpan RenewOnCallTime
{
[System.Security.SecurityCritical] // auto-generated
get { return renewOnCallTime; }
[System.Security.SecurityCritical] // auto-generated
[SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration)]
set
{
if (state == LeaseState.Initial)
{
renewOnCallTime = value;
BCLDebug.Trace("REMOTE", "Lease Set RenewOnCallProperty ",managedObject," "+renewOnCallTime);
}
else
throw new RemotingException(Environment.GetResourceString("Remoting_Lifetime_InitialStateRenewOnCall", ((Enum)state).ToString()));
}
}
public TimeSpan SponsorshipTimeout
{
[System.Security.SecurityCritical] // auto-generated
get { return sponsorshipTimeout; }
[System.Security.SecurityCritical] // auto-generated
[SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration)]
set
{
if (state == LeaseState.Initial)
{
sponsorshipTimeout = value;
BCLDebug.Trace("REMOTE", "Lease Set SponsorshipTimeout Property ",managedObject," "+sponsorshipTimeout);
}
else
throw new RemotingException(Environment.GetResourceString("Remoting_Lifetime_InitialStateSponsorshipTimeout", ((Enum)state).ToString()));
}
}
public TimeSpan InitialLeaseTime
{
[System.Security.SecurityCritical] // auto-generated
get { return initialLeaseTime; }
[System.Security.SecurityCritical] // auto-generated
[SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration)]
set
{
if (state == LeaseState.Initial)
{
initialLeaseTime = value;
if (TimeSpan.Zero.CompareTo(value) >= 0)
state = LeaseState.Null;
BCLDebug.Trace("REMOTE", "Lease Set InitialLeaseTime Property ",managedObject," "+InitialLeaseTime+", current state "+((Enum)state).ToString());
}
else
throw new RemotingException(Environment.GetResourceString("Remoting_Lifetime_InitialStateInitialLeaseTime", ((Enum)state).ToString()));
}
}
public TimeSpan CurrentLeaseTime
{
[System.Security.SecurityCritical] // auto-generated
get { return leaseTime.Subtract(DateTime.UtcNow); }
}
public LeaseState CurrentState
{
[System.Security.SecurityCritical] // auto-generated
get { return state;}
}
[System.Security.SecurityCritical] // auto-generated
[SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration)]
public void Register(ISponsor obj)
{
Register(obj, TimeSpan.Zero);
}
[System.Security.SecurityCritical] // auto-generated
[SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration)]
public void Register(ISponsor obj, TimeSpan renewalTime)
{
lock(this)
{
BCLDebug.Trace("REMOTE", "Lease "+id+" Register Sponsor renewalTime ",renewalTime," state ",((Enum)state).ToString());
if (state == LeaseState.Expired || sponsorshipTimeout == TimeSpan.Zero)
return;
Object sponsorId = GetSponsorId(obj);
lock(sponsorTable)
{
if (renewalTime > TimeSpan.Zero)
AddTime(renewalTime);
if (!sponsorTable.ContainsKey(sponsorId))
{
// Place in tables
sponsorTable[sponsorId] = new SponsorStateInfo(renewalTime, SponsorState.Initial);
}
}
}
}
[System.Security.SecurityCritical] // auto-generated
[SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration)]
public void Unregister(ISponsor sponsor)
{
lock(this)
{
BCLDebug.Trace("REMOTE", "Lease",id," Unregister state ",((Enum)state).ToString());
if (state == LeaseState.Expired)
return;
Object sponsorId = GetSponsorId(sponsor);
lock(sponsorTable)
{
if (sponsorId != null)
{
leaseManager.DeleteSponsor(sponsorId);
SponsorStateInfo sponsorStateInfo = (SponsorStateInfo)sponsorTable[sponsorId];
sponsorTable.Remove(sponsorId);
}
}
}
}
// Get the local representative of the sponsor to prevent a remote access when placing
// in a hash table.
[System.Security.SecurityCritical] // auto-generated
private Object GetSponsorId(ISponsor obj)
{
Object sponsorId = null;
if (obj != null)
{
if (RemotingServices.IsTransparentProxy(obj))
sponsorId = RemotingServices.GetRealProxy(obj);
else
sponsorId = obj;
}
return sponsorId;
}
// Convert from the local representative of the sponsor to either the MarshalByRefObject or local object
[System.Security.SecurityCritical] // auto-generated
private ISponsor GetSponsorFromId(Object sponsorId)
{
Object sponsor = null;
RealProxy rp = sponsorId as RealProxy;
if (null != rp)
sponsor = rp.GetTransparentProxy();
else
sponsor = sponsorId;
return (ISponsor)sponsor;
}
[System.Security.SecurityCritical] // auto-generated
[SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration)]
public TimeSpan Renew(TimeSpan renewalTime)
{
return RenewInternal(renewalTime);
}
// We will call this internally within the server domain
internal TimeSpan RenewInternal(TimeSpan renewalTime)
{
lock(this)
{
BCLDebug.Trace("REMOTE","Lease ",id," Renew ",renewalTime," state ",((Enum)state).ToString());
if (state == LeaseState.Expired)
return TimeSpan.Zero;
AddTime(renewalTime);
return leaseTime.Subtract(DateTime.UtcNow);
}
}
// Used for a lease which has been created, but will not be used
internal void Remove()
{
BCLDebug.Trace("REMOTE","Lease ",id," Remove state ",((Enum)state).ToString());
if (state == LeaseState.Expired)
return;
state = LeaseState.Expired;
leaseManager.DeleteLease(this);
}
[System.Security.SecurityCritical] // auto-generated
internal void Cancel()
{
lock(this)
{
BCLDebug.Trace("REMOTE","Lease ",id," Cancel Managed Object ",managedObject," state ",((Enum)state).ToString());
if (state == LeaseState.Expired)
return;
Remove();
// Disconnect the object ...
// We use the internal version of Disconnect passing "false"
// for the bResetURI flag. This allows the object to keep its
// old URI in case its lease gets reactivated later.
RemotingServices.Disconnect(managedObject, false);
// Disconnect the lease for the object.
RemotingServices.Disconnect(this);
}
}
#if _DEBUG
~Lease()
{
BCLDebug.Trace("REMOTE","Lease ",id," Finalize");
}
#endif
internal void RenewOnCall()
{
lock(this)
{
//BCLDebug.Trace("REMOTE","Lease ",id," RenewOnCall state ",((Enum)state).ToString());
if (state == LeaseState.Initial || state == LeaseState.Expired)
return;
AddTime(renewOnCallTime);
}
}
[System.Security.SecurityCritical] // auto-generated
internal void LeaseExpired(DateTime now)
{
lock(this)
{
BCLDebug.Trace("REMOTE","Lease ",id," LeaseExpired state ",((Enum)state).ToString());
if (state == LeaseState.Expired)
return;
// There is a small window between the time the leaseManager
// thread examines all the leases and tests for expiry and
// when an indivisual lease is locked for expiry. The object
// could get marshal-ed in this time which would reset its lease
// Therefore we check again to see if we should indeed proceed
// with the expire code (using the same value of 'now' as used
// by the leaseManager thread)
if (leaseTime.CompareTo(now) < 0)
ProcessNextSponsor();
}
}
internal delegate TimeSpan AsyncRenewal(ILease lease);
[System.Security.SecurityCritical] // auto-generated
internal void SponsorCall(ISponsor sponsor)
{
BCLDebug.Trace("REMOTE","Lease ",id," SponsorCall state ",((Enum)state).ToString());
bool exceptionOccurred = false;
if (state == LeaseState.Expired)
return;
lock(sponsorTable)
{
try
{
Object sponsorId = GetSponsorId(sponsor);
sponsorCallThread = Thread.CurrentThread.GetHashCode();
AsyncRenewal ar = new AsyncRenewal(sponsor.Renewal);
SponsorStateInfo sponsorStateInfo = (SponsorStateInfo)sponsorTable[sponsorId];
sponsorStateInfo.sponsorState = SponsorState.Waiting;
// The first parameter should be the lease we are trying to renew.
IAsyncResult iar = ar.BeginInvoke(this, new AsyncCallback(this.SponsorCallback), null);
if ((sponsorStateInfo.sponsorState == SponsorState.Waiting) && (state != LeaseState.Expired))
{
// Even if we get here, the operation could still complete before
// we call the the line below. This seems to be a ----.
// Sponsor could have completed before statement is reached, so only execute
// if the sponsor state is still waiting
leaseManager.RegisterSponsorCall(this, sponsorId, sponsorshipTimeout);
}
sponsorCallThread = 0;
}catch(Exception)
{
// Sponsor not avaiable
exceptionOccurred = true;
sponsorCallThread = 0;
}
}
if (exceptionOccurred)
{
BCLDebug.Trace("REMOTE","Lease ",id," SponsorCall Sponsor Exception ");
Unregister(sponsor);
ProcessNextSponsor();
}
}
[System.Security.SecurityCritical] // auto-generated
internal void SponsorTimeout(Object sponsorId)
{
lock (this)
{
if (!sponsorTable.ContainsKey(sponsorId))
return;
lock(sponsorTable)
{
SponsorStateInfo sponsorStateInfo = (SponsorStateInfo)sponsorTable[sponsorId];
BCLDebug.Trace("REMOTE","Lease ",id," SponsorTimeout sponsorState ",((Enum)sponsorStateInfo.sponsorState).ToString());
if (sponsorStateInfo.sponsorState == SponsorState.Waiting)
{
Unregister(GetSponsorFromId(sponsorId));
ProcessNextSponsor();
}
}
}
}
[System.Security.SecurityCritical] // auto-generated
private void ProcessNextSponsor()
{
BCLDebug.Trace("REMOTE","Lease ",id," ProcessNextSponsor");
Object largestSponsor = null;
TimeSpan largestRenewalTime = TimeSpan.Zero;
lock(sponsorTable)
{
IDictionaryEnumerator e = sponsorTable.GetEnumerator();
// Find sponsor with largest previous renewal value
while(e.MoveNext())
{
Object sponsorId = e.Key;
SponsorStateInfo sponsorStateInfo = (SponsorStateInfo)e.Value;
if ((sponsorStateInfo.sponsorState == SponsorState.Initial) && (largestRenewalTime == TimeSpan.Zero))
{
largestRenewalTime = sponsorStateInfo.renewalTime;
largestSponsor = sponsorId;
}
else if (sponsorStateInfo.renewalTime > largestRenewalTime)
{
largestRenewalTime = sponsorStateInfo.renewalTime;
largestSponsor = sponsorId;
}
}
}
if (largestSponsor != null)
SponsorCall(GetSponsorFromId(largestSponsor));
else
{
// No more sponsors to try, Cancel
BCLDebug.Trace("REMOTE","Lease ",id," ProcessNextSponsor no more sponsors");
Cancel();
}
}
// This gets called when we explicitly transfer the call back from the
// called function to a threadpool thread.
[System.Security.SecurityCritical] // auto-generated
internal void SponsorCallback(Object obj)
{
SponsorCallback((IAsyncResult)obj);
}
// On another thread
[System.Security.SecurityCritical] // auto-generated
internal void SponsorCallback(IAsyncResult iar)
{
BCLDebug.Trace("REMOTE","Lease ",id," SponsorCallback IAsyncResult ",iar," state ",((Enum)state).ToString());
if (state == LeaseState.Expired)
{
return;
}
int thisThread = Thread.CurrentThread.GetHashCode();
if (thisThread == sponsorCallThread)
{
// Looks like something went wrong and the thread that
// did the AsyncRenewal::BeginInvoke is executing the callback
// We will queue the work to the thread pool (otherwise there
// is a possibility of stack overflow if all sponsors are down)
WaitCallback threadFunc = new WaitCallback(this.SponsorCallback);
ThreadPool.QueueUserWorkItem(threadFunc, iar);
return;
}
AsyncResult asyncResult = (AsyncResult)iar;
AsyncRenewal ar = (AsyncRenewal)asyncResult.AsyncDelegate;
ISponsor sponsor = (ISponsor)ar.Target;
SponsorStateInfo sponsorStateInfo = null;
if (iar.IsCompleted)
{
// Sponsor came back with renewal
BCLDebug.Trace("REMOTE","Lease ",id," SponsorCallback sponsor completed");
bool exceptionOccurred = false;
TimeSpan renewalTime = TimeSpan.Zero;
try
{
renewalTime = (TimeSpan)ar.EndInvoke(iar);
}catch(Exception)
{
// Sponsor not avaiable
exceptionOccurred = true;
}
if (exceptionOccurred)
{
BCLDebug.Trace("REMOTE","Lease ",id," SponsorCallback Sponsor Exception ");
Unregister(sponsor);
ProcessNextSponsor();
}
else
{
Object sponsorId = GetSponsorId(sponsor);
lock(sponsorTable)
{
if (sponsorTable.ContainsKey(sponsorId))
{
sponsorStateInfo = (SponsorStateInfo)sponsorTable[sponsorId];
sponsorStateInfo.sponsorState = SponsorState.Completed;
sponsorStateInfo.renewalTime = renewalTime;
}
else
{
// Sponsor was deleted, possibly from a sponsor time out
}
}
if (sponsorStateInfo == null)
{
// Sponsor was deleted
ProcessNextSponsor();
}
else if (sponsorStateInfo.renewalTime == TimeSpan.Zero)
{
BCLDebug.Trace("REMOTE","Lease ",id," SponsorCallback sponsor did not renew ");
Unregister(sponsor);
ProcessNextSponsor();
}
else
RenewInternal(sponsorStateInfo.renewalTime);
}
}
else
{
// Sponsor timed out
// Note time outs should be handled by the LeaseManager
BCLDebug.Trace("REMOTE","Lease ",id," SponsorCallback sponsor did not complete, timed out");
Unregister(sponsor);
ProcessNextSponsor();
}
}
private void AddTime(TimeSpan renewalSpan)
{
if (state == LeaseState.Expired)
return;
DateTime now = DateTime.UtcNow;
DateTime oldLeaseTime = leaseTime;
DateTime renewTime = now.Add(renewalSpan);
if (leaseTime.CompareTo(renewTime) < 0)
{
leaseManager.ChangedLeaseTime(this, renewTime);
leaseTime = renewTime;
state = LeaseState.Active;
}
//BCLDebug.Trace("REMOTE","Lease ",id," AddTime renewalSpan ",renewalSpan," current Time ",now," old leaseTime ",oldLeaseTime," new leaseTime ",leaseTime," state ",((Enum)state).ToString());
}
[Serializable]
internal enum SponsorState
{
Initial = 0,
Waiting = 1,
Completed = 2
}
internal sealed class SponsorStateInfo
{
internal TimeSpan renewalTime;
internal SponsorState sponsorState;
internal SponsorStateInfo(TimeSpan renewalTime, SponsorState sponsorState)
{
this.renewalTime = renewalTime;
this.sponsorState = sponsorState;
}
}
}
internal class LeaseSink : IMessageSink
{
Lease lease = null;
IMessageSink nextSink = null;
public LeaseSink(Lease lease, IMessageSink nextSink)
{
this.lease = lease;
this.nextSink = nextSink;
}
//IMessageSink methods
[System.Security.SecurityCritical] // auto-generated
public IMessage SyncProcessMessage(IMessage msg)
{
//BCLDebug.Trace("REMOTE","Lease ",id," SyncProcessMessage");
lease.RenewOnCall();
return nextSink.SyncProcessMessage(msg);
}
[System.Security.SecurityCritical] // auto-generated
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
{
//BCLDebug.Trace("REMOTE","Lease ",id," AsyncProcessMessage");
lease.RenewOnCall();
return nextSink.AsyncProcessMessage(msg, replySink);
}
public IMessageSink NextSink
{
[System.Security.SecurityCritical] // auto-generated
get
{
//BCLDebug.Trace("REMOTE","Lease ",id," NextSink");
return nextSink;
}
}
}
}
|