| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688 |
- //------------------------------------------------------------
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //------------------------------------------------------------
- namespace System.ServiceModel.PeerResolvers
- {
- using System;
- using System.Collections.Generic;
- using System.Runtime;
- using System.ServiceModel;
- using System.ServiceModel.Channels;
- using System.Threading;
- [ObsoleteAttribute ("PeerChannel feature is obsolete and will be removed in the future.", false)]
- [ServiceBehavior(UseSynchronizationContext = false, InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
- public class CustomPeerResolverService : IPeerResolverContract
- {
- internal enum RegistrationState
- {
- OK, Deleted
- }
- internal class RegistrationEntry
- {
- Guid clientId;
- Guid registrationId;
- string meshId;
- DateTime expires;
- PeerNodeAddress address;
- RegistrationState state;
- public RegistrationEntry(Guid clientId, Guid registrationId, string meshId, DateTime expires, PeerNodeAddress address)
- {
- this.ClientId = clientId;
- this.RegistrationId = registrationId;
- this.MeshId = meshId;
- this.Expires = expires;
- this.Address = address;
- this.State = RegistrationState.OK;
- }
- public Guid ClientId
- {
- get { return clientId; }
- set { clientId = value; }
- }
- public Guid RegistrationId
- {
- get { return registrationId; }
- set { registrationId = value; }
- }
- public string MeshId
- {
- get { return meshId; }
- set { meshId = value; }
- }
- public DateTime Expires
- {
- get { return expires; }
- set { expires = value; }
- }
- public PeerNodeAddress Address
- {
- get { return address; }
- set { address = value; }
- }
- public RegistrationState State
- {
- get { return state; }
- set { state = value; }
- }
- }
- internal class LiteLock
- {
- bool forWrite;
- bool upgraded;
- ReaderWriterLock locker;
- TimeSpan timeout = TimeSpan.FromMinutes(1);
- LockCookie lc;
- LiteLock(ReaderWriterLock locker, bool forWrite)
- {
- this.locker = locker;
- this.forWrite = forWrite;
- }
- public static void Acquire(out LiteLock liteLock, ReaderWriterLock locker)
- {
- Acquire(out liteLock, locker, false);
- }
- public static void Acquire(out LiteLock liteLock, ReaderWriterLock locker, bool forWrite)
- {
- LiteLock theLock = new LiteLock(locker, forWrite);
- try { }
- finally
- {
- if (forWrite)
- {
- locker.AcquireWriterLock(theLock.timeout);
- }
- else
- {
- locker.AcquireReaderLock(theLock.timeout);
- }
- liteLock = theLock;
- }
- }
- public static void Release(LiteLock liteLock)
- {
- if (liteLock == null)
- {
- return;
- }
- if (liteLock.forWrite)
- {
- liteLock.locker.ReleaseWriterLock();
- }
- else
- {
- Fx.Assert(!liteLock.upgraded, "Can't release while upgraded!");
- liteLock.locker.ReleaseReaderLock();
- }
- }
- public void UpgradeToWriterLock()
- {
- Fx.Assert(!forWrite, "Invalid call to Upgrade!!");
- Fx.Assert(!upgraded, "Already upgraded!");
- try { }
- finally
- {
- lc = locker.UpgradeToWriterLock(timeout);
- upgraded = true;
- }
- }
- public void DowngradeFromWriterLock()
- {
- Fx.Assert(!forWrite, "Invalid call to Downgrade!!");
- if (upgraded)
- {
- locker.DowngradeFromWriterLock(ref lc);
- upgraded = false;
- }
- }
- }
- internal class MeshEntry
- {
- Dictionary<Guid, RegistrationEntry> entryTable;
- Dictionary<string, RegistrationEntry> service2EntryTable;
- List<RegistrationEntry> entryList;
- ReaderWriterLock gate;
- internal MeshEntry()
- {
- EntryTable = new Dictionary<Guid, RegistrationEntry>();
- Service2EntryTable = new Dictionary<string, RegistrationEntry>();
- EntryList = new List<RegistrationEntry>();
- Gate = new ReaderWriterLock();
- }
- public Dictionary<Guid, RegistrationEntry> EntryTable
- {
- get { return entryTable; }
- set { entryTable = value; }
- }
- public Dictionary<string, RegistrationEntry> Service2EntryTable
- {
- get { return service2EntryTable; }
- set { service2EntryTable = value; }
- }
- public List<RegistrationEntry> EntryList
- {
- get { return entryList; }
- set { entryList = value; }
- }
- public ReaderWriterLock Gate
- {
- get { return gate; }
- set { gate = value; }
- }
- }
- Dictionary<string, MeshEntry> meshId2Entry = new Dictionary<string, MeshEntry>();
- ReaderWriterLock gate;
- TimeSpan timeout = TimeSpan.FromMinutes(1);
- TimeSpan cleanupInterval = TimeSpan.FromMinutes(1);
- TimeSpan refreshInterval = TimeSpan.FromMinutes(10);
- bool controlShape;
- bool isCleaning;
- IOThreadTimer timer;
- object thisLock = new object();
- bool opened;
- TimeSpan LockWait = TimeSpan.FromSeconds(5);
- public CustomPeerResolverService()
- {
- isCleaning = false;
- gate = new ReaderWriterLock();
- }
- public TimeSpan CleanupInterval
- {
- get
- {
- return cleanupInterval;
- }
- set
- {
- if (value < TimeSpan.Zero)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value,
- SR.GetString(SR.SFxTimeoutOutOfRange0)));
- }
- if (TimeoutHelper.IsTooLarge(value))
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value,
- SR.GetString(SR.SFxTimeoutOutOfRangeTooBig)));
- }
- lock (ThisLock)
- {
- ThrowIfOpened("Set CleanupInterval");
- this.cleanupInterval = value;
- }
- }
- }
- public TimeSpan RefreshInterval
- {
- get
- {
- return refreshInterval;
- }
- set
- {
- if (value < TimeSpan.Zero)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value,
- SR.GetString(SR.SFxTimeoutOutOfRange0)));
- }
- if (TimeoutHelper.IsTooLarge(value))
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value,
- SR.GetString(SR.SFxTimeoutOutOfRangeTooBig)));
- }
- lock (ThisLock)
- {
- ThrowIfOpened("Set RefreshInterval");
- this.refreshInterval = value;
- }
- }
- }
- public bool ControlShape
- {
- get
- {
- return this.controlShape;
- }
- set
- {
- lock (ThisLock)
- {
- ThrowIfOpened("Set ControlShape");
- this.controlShape = value;
- }
- }
- }
- MeshEntry GetMeshEntry(string meshId) { return GetMeshEntry(meshId, true); }
- MeshEntry GetMeshEntry(string meshId, bool createIfNotExists)
- {
- MeshEntry meshEntry = null;
- LiteLock ll = null;
- try
- {
- LiteLock.Acquire(out ll, gate);
- if (!this.meshId2Entry.TryGetValue(meshId, out meshEntry) && createIfNotExists)
- {
- meshEntry = new MeshEntry();
- try
- {
- ll.UpgradeToWriterLock();
- meshId2Entry.Add(meshId, meshEntry);
- }
- finally
- {
- ll.DowngradeFromWriterLock();
- }
- }
- }
- finally
- {
- LiteLock.Release(ll);
- }
- Fx.Assert(meshEntry != null || !createIfNotExists, "GetMeshEntry failed to get an entry!");
- return meshEntry;
- }
- public virtual RegisterResponseInfo Register(Guid clientId, string meshId, PeerNodeAddress address)
- {
- Guid registrationId = Guid.NewGuid();
- DateTime expiry = DateTime.UtcNow + RefreshInterval;
- RegistrationEntry entry = null;
- MeshEntry meshEntry = null;
- lock (ThisLock)
- {
- entry = new RegistrationEntry(clientId, registrationId, meshId, expiry, address);
- meshEntry = GetMeshEntry(meshId);
- if (meshEntry.Service2EntryTable.ContainsKey(address.ServicePath))
- PeerExceptionHelper.ThrowInvalidOperation_DuplicatePeerRegistration(address.ServicePath);
- LiteLock ll = null;
- try
- {
- // meshEntry.gate can be held by this thread for write if this is coming from update
- // else MUST not be held at all.
- if (!meshEntry.Gate.IsWriterLockHeld)
- {
- LiteLock.Acquire(out ll, meshEntry.Gate, true);
- }
- meshEntry.EntryTable.Add(registrationId, entry);
- meshEntry.EntryList.Add(entry);
- meshEntry.Service2EntryTable.Add(address.ServicePath, entry);
- }
- finally
- {
- if (ll != null)
- LiteLock.Release(ll);
- }
- }
- return new RegisterResponseInfo(registrationId, RefreshInterval);
- }
- public virtual RegisterResponseInfo Register(RegisterInfo registerInfo)
- {
- if (registerInfo == null)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("registerInfo", SR.GetString(SR.PeerNullRegistrationInfo));
- }
- ThrowIfClosed("Register");
- if (!registerInfo.HasBody() || String.IsNullOrEmpty(registerInfo.MeshId))
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("registerInfo", SR.GetString(SR.PeerInvalidMessageBody, registerInfo));
- }
- return Register(registerInfo.ClientId, registerInfo.MeshId, registerInfo.NodeAddress);
- }
- public virtual RegisterResponseInfo Update(UpdateInfo updateInfo)
- {
- if (updateInfo == null)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("updateInfo", SR.GetString(SR.PeerNullRegistrationInfo));
- }
- ThrowIfClosed("Update");
- if (!updateInfo.HasBody() || String.IsNullOrEmpty(updateInfo.MeshId) || updateInfo.NodeAddress == null)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("updateInfo", SR.GetString(SR.PeerInvalidMessageBody, updateInfo));
- }
- Guid registrationId = updateInfo.RegistrationId;
- RegistrationEntry entry;
- MeshEntry meshEntry = GetMeshEntry(updateInfo.MeshId);
- LiteLock ll = null;
- //handle cases when Update ----s with Register.
- if (updateInfo.RegistrationId == Guid.Empty || meshEntry == null)
- return Register(updateInfo.ClientId, updateInfo.MeshId, updateInfo.NodeAddress);
- //
- // preserve locking order between ThisLock and the LiteLock.
- lock (ThisLock)
- {
- try
- {
- LiteLock.Acquire(out ll, meshEntry.Gate);
- if (!meshEntry.EntryTable.TryGetValue(updateInfo.RegistrationId, out entry))
- {
- try
- {
- // upgrade to writer lock
- ll.UpgradeToWriterLock();
- return Register(updateInfo.ClientId, updateInfo.MeshId, updateInfo.NodeAddress);
- }
- finally
- {
- ll.DowngradeFromWriterLock();
- }
- }
- lock (entry)
- {
- entry.Address = updateInfo.NodeAddress;
- entry.Expires = DateTime.UtcNow + this.RefreshInterval;
- }
- }
- finally
- {
- LiteLock.Release(ll);
- }
- }
- return new RegisterResponseInfo(registrationId, RefreshInterval);
- }
- public virtual ResolveResponseInfo Resolve(ResolveInfo resolveInfo)
- {
- if (resolveInfo == null)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("resolveInfo", SR.GetString(SR.PeerNullResolveInfo));
- }
- ThrowIfClosed("Resolve");
- if (!resolveInfo.HasBody())
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("resolveInfo", SR.GetString(SR.PeerInvalidMessageBody, resolveInfo));
- }
- int currentCount = 0;
- int index = 0;
- int maxEntries = resolveInfo.MaxAddresses;
- ResolveResponseInfo response = new ResolveResponseInfo();
- List<PeerNodeAddress> results = new List<PeerNodeAddress>();
- List<RegistrationEntry> entries = null;
- PeerNodeAddress address;
- RegistrationEntry entry;
- MeshEntry meshEntry = GetMeshEntry(resolveInfo.MeshId, false);
- if (meshEntry != null)
- {
- LiteLock ll = null;
- try
- {
- LiteLock.Acquire(out ll, meshEntry.Gate);
- entries = meshEntry.EntryList;
- if (entries.Count <= maxEntries)
- {
- foreach (RegistrationEntry e in entries)
- {
- results.Add(e.Address);
- }
- }
- else
- {
- Random random = new Random();
- while (currentCount < maxEntries)
- {
- index = random.Next(entries.Count);
- entry = entries[index];
- Fx.Assert(entry.State == RegistrationState.OK, "A deleted registration is still around!");
- address = entry.Address;
- if (!results.Contains(address))
- results.Add(address);
- currentCount++;
- }
- }
- }
- finally
- {
- LiteLock.Release(ll);
- }
- }
- response.Addresses = results.ToArray();
- return response;
- }
- public virtual void Unregister(UnregisterInfo unregisterInfo)
- {
- if (unregisterInfo == null)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("unregisterinfo", SR.GetString(SR.PeerNullRegistrationInfo));
- }
- ThrowIfClosed("Unregister");
- if (!unregisterInfo.HasBody() || String.IsNullOrEmpty(unregisterInfo.MeshId) || unregisterInfo.RegistrationId == Guid.Empty)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("unregisterInfo", SR.GetString(SR.PeerInvalidMessageBody, unregisterInfo));
- }
- RegistrationEntry registration = null;
- MeshEntry meshEntry = GetMeshEntry(unregisterInfo.MeshId, false);
- //there could be a ---- that two different threads could be working on the same entry
- //we wont optimize for that case.
- LiteLock ll = null;
- try
- {
- LiteLock.Acquire(out ll, meshEntry.Gate, true);
- if (!meshEntry.EntryTable.TryGetValue(unregisterInfo.RegistrationId, out registration))
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("unregisterInfo", SR.GetString(SR.PeerInvalidMessageBody, unregisterInfo));
- meshEntry.EntryTable.Remove(unregisterInfo.RegistrationId);
- meshEntry.EntryList.Remove(registration);
- meshEntry.Service2EntryTable.Remove(registration.Address.ServicePath);
- registration.State = RegistrationState.Deleted;
- }
- finally
- {
- LiteLock.Release(ll);
- }
- }
- public virtual RefreshResponseInfo Refresh(RefreshInfo refreshInfo)
- {
- if (refreshInfo == null)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("refreshInfo", SR.GetString(SR.PeerNullRefreshInfo));
- }
- ThrowIfClosed("Refresh");
- if (!refreshInfo.HasBody() || String.IsNullOrEmpty(refreshInfo.MeshId) || refreshInfo.RegistrationId == Guid.Empty)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("refreshInfo", SR.GetString(SR.PeerInvalidMessageBody, refreshInfo));
- }
- RefreshResult result = RefreshResult.RegistrationNotFound;
- RegistrationEntry entry = null;
- MeshEntry meshEntry = GetMeshEntry(refreshInfo.MeshId, false);
- LiteLock ll = null;
- if (meshEntry != null)
- {
- try
- {
- LiteLock.Acquire(out ll, meshEntry.Gate);
- if (!meshEntry.EntryTable.TryGetValue(refreshInfo.RegistrationId, out entry))
- return new RefreshResponseInfo(RefreshInterval, result);
- lock (entry)
- {
- if (entry.State == RegistrationState.OK)
- {
- entry.Expires = DateTime.UtcNow + RefreshInterval;
- result = RefreshResult.Success;
- }
- }
- }
- finally
- {
- LiteLock.Release(ll);
- }
- }
- return new RefreshResponseInfo(RefreshInterval, result);
- }
- public virtual ServiceSettingsResponseInfo GetServiceSettings()
- {
- ThrowIfClosed("GetServiceSettings");
- ServiceSettingsResponseInfo info = new ServiceSettingsResponseInfo(this.ControlShape);
- return info;
- }
- public virtual void Open()
- {
- ThrowIfOpened("Open");
- if (this.refreshInterval <= TimeSpan.Zero)
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("RefreshInterval", SR.GetString(SR.RefreshIntervalMustBeGreaterThanZero, this.refreshInterval));
- if (this.CleanupInterval <= TimeSpan.Zero)
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("CleanupInterval", SR.GetString(SR.CleanupIntervalMustBeGreaterThanZero, this.cleanupInterval));
- //check that we are good to open
- timer = new IOThreadTimer(new Action<object>(CleanupActivity), null, false);
- timer.Set(CleanupInterval);
- opened = true;
- }
- public virtual void Close()
- {
- ThrowIfClosed("Close");
- timer.Cancel();
- opened = false;
- }
- internal virtual void CleanupActivity(object state)
- {
- if (!opened)
- return;
- if (!isCleaning)
- {
- lock (ThisLock)
- {
- if (!isCleaning)
- {
- isCleaning = true;
- try
- {
- MeshEntry meshEntry = null;
- //acquire a write lock. from the reader/writer lock can we postpone until no contention?
- ICollection<string> keys = null;
- LiteLock ll = null;
- try
- {
- LiteLock.Acquire(out ll, gate);
- keys = meshId2Entry.Keys;
- }
- finally
- {
- LiteLock.Release(ll);
- }
- foreach (string meshId in keys)
- {
- meshEntry = GetMeshEntry(meshId);
- CleanupMeshEntry(meshEntry);
- }
- }
- finally
- {
- isCleaning = false;
- if (opened)
- timer.Set(this.CleanupInterval);
- }
- }
- }
- }
- }
- //always call this from a readlock
- void CleanupMeshEntry(MeshEntry meshEntry)
- {
- List<Guid> remove = new List<Guid>();
- if (!opened)
- return;
- LiteLock ll = null;
- try
- {
- LiteLock.Acquire(out ll, meshEntry.Gate, true);
- foreach (KeyValuePair<Guid, RegistrationEntry> item in meshEntry.EntryTable)
- {
- if ((item.Value.Expires <= DateTime.UtcNow) || (item.Value.State == RegistrationState.Deleted))
- {
- remove.Add(item.Key);
- meshEntry.EntryList.Remove(item.Value);
- meshEntry.Service2EntryTable.Remove(item.Value.Address.ServicePath);
- }
- }
- foreach (Guid id in remove)
- {
- meshEntry.EntryTable.Remove(id);
- }
- }
- finally
- {
- LiteLock.Release(ll);
- }
- }
- object ThisLock
- {
- get
- {
- return this.thisLock;
- }
- }
- void ThrowIfOpened(string operation)
- {
- if (opened)
- PeerExceptionHelper.ThrowInvalidOperation_NotValidWhenOpen(operation);
- }
- void ThrowIfClosed(string operation)
- {
- if (!opened)
- PeerExceptionHelper.ThrowInvalidOperation_NotValidWhenClosed(operation);
- }
- }
- }
|