|
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.Runtime.DurableInstancing
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Runtime;
using System.Xml.Linq;
using System.Threading;
[Fx.Tag.XamlVisible(false)]
public sealed class InstanceView
{
static readonly ReadOnlyDictionaryInternal<XName, InstanceValue> emptyProperties = new ReadOnlyDictionaryInternal<XName, InstanceValue>(new Dictionary<XName, InstanceValue>(0));
static readonly ReadOnlyDictionaryInternal<Guid, InstanceKeyView> emptyKeys = new ReadOnlyDictionaryInternal<Guid, InstanceKeyView>(new Dictionary<Guid, InstanceKeyView>(0));
IDictionary<XName, InstanceValue> data;
IDictionary<XName, InstanceValue> metadata;
IDictionary<XName, InstanceValue> ownerMetadata;
IDictionary<Guid, InstanceKeyView> keys;
ReadOnlyCollection<InstanceStoreQueryResult> queryResults;
Dictionary<XName, InstanceValue> accumulatedMetadataWrites;
Dictionary<XName, InstanceValue> accumulatedOwnerMetadataWrites;
Collection<InstanceStoreQueryResult> queryResultsBackingCollection;
long instanceVersion;
internal InstanceView(InstanceOwner owner)
: this()
{
InstanceOwner = owner;
}
internal InstanceView(InstanceOwner owner, Guid instanceId)
: this()
{
Fx.Assert(instanceId != Guid.Empty, "Null instanceId passed to InstanceView ctor.");
InstanceOwner = owner;
InstanceId = instanceId;
IsBoundToInstance = true;
}
InstanceView()
{
this.instanceVersion = -1;
InstanceDataConsistency = InstanceValueConsistency.InDoubt | InstanceValueConsistency.Partial;
InstanceMetadataConsistency = InstanceValueConsistency.InDoubt | InstanceValueConsistency.Partial;
InstanceOwnerMetadataConsistency = InstanceValueConsistency.InDoubt | InstanceValueConsistency.Partial;
InstanceKeysConsistency = InstanceValueConsistency.InDoubt | InstanceValueConsistency.Partial;
}
InstanceView(InstanceView source)
{
this.instanceVersion = source.instanceVersion;
InstanceOwner = source.InstanceOwner;
InstanceId = source.InstanceId;
IsBoundToInstance = source.IsBoundToInstance;
InstanceState = source.InstanceState;
InstanceDataConsistency = source.InstanceDataConsistency;
InstanceMetadataConsistency = source.InstanceMetadataConsistency;
InstanceOwnerMetadataConsistency = source.InstanceOwnerMetadataConsistency;
InstanceKeysConsistency = source.InstanceKeysConsistency;
InstanceData = source.InstanceData;
InstanceMetadata = source.InstanceMetadata;
InstanceOwnerMetadata = source.InstanceOwnerMetadata;
InstanceStoreQueryResults = source.InstanceStoreQueryResults;
Dictionary<Guid, InstanceKeyView> keys = null;
if (source.InstanceKeys.Count > 0)
{
keys = new Dictionary<Guid, InstanceKeyView>(source.InstanceKeys.Count);
foreach (KeyValuePair<Guid, InstanceKeyView> key in source.InstanceKeys)
{
keys.Add(key.Key, key.Value.Clone());
}
}
InstanceKeys = keys == null ? null : new ReadOnlyDictionaryInternal<Guid, InstanceKeyView>(keys);
}
public Guid InstanceId { get; private set; }
public bool IsBoundToInstance { get; private set; }
public InstanceOwner InstanceOwner { get; private set; }
public bool IsBoundToInstanceOwner
{
get
{
return InstanceOwner != null;
}
}
public bool IsBoundToLock
{
get
{
return this.instanceVersion >= 0;
}
}
public InstanceState InstanceState { get; internal set; }
// All dictionaries are always read-only.
public InstanceValueConsistency InstanceDataConsistency { get; internal set; }
public IDictionary<XName, InstanceValue> InstanceData
{
get
{
return this.data ?? InstanceView.emptyProperties;
}
internal set
{
Fx.AssertAndThrow(!IsViewFrozen, "Setting Data on frozen View.");
this.data = value;
}
}
public InstanceValueConsistency InstanceMetadataConsistency { get; internal set; }
public IDictionary<XName, InstanceValue> InstanceMetadata
{
get
{
IDictionary<XName, InstanceValue> pendingWrites = this.accumulatedMetadataWrites;
this.accumulatedMetadataWrites = null;
this.metadata = pendingWrites.ReadOnlyMergeInto(this.metadata ?? InstanceView.emptyProperties, true);
return this.metadata;
}
internal set
{
Fx.AssertAndThrow(!IsViewFrozen, "Setting Metadata on frozen View.");
this.accumulatedMetadataWrites = null;
this.metadata = value;
}
}
internal Dictionary<XName, InstanceValue> AccumulatedMetadataWrites
{
get
{
if (this.accumulatedMetadataWrites == null)
{
this.accumulatedMetadataWrites = new Dictionary<XName, InstanceValue>();
}
return this.accumulatedMetadataWrites;
}
}
public InstanceValueConsistency InstanceOwnerMetadataConsistency { get; internal set; }
public IDictionary<XName, InstanceValue> InstanceOwnerMetadata
{
get
{
IDictionary<XName, InstanceValue> pendingWrites = this.accumulatedOwnerMetadataWrites;
this.accumulatedOwnerMetadataWrites = null;
this.ownerMetadata = pendingWrites.ReadOnlyMergeInto(this.ownerMetadata ?? InstanceView.emptyProperties, true);
return this.ownerMetadata;
}
internal set
{
Fx.AssertAndThrow(!IsViewFrozen, "Setting OwnerMetadata on frozen View.");
this.accumulatedOwnerMetadataWrites = null;
this.ownerMetadata = value;
}
}
internal Dictionary<XName, InstanceValue> AccumulatedOwnerMetadataWrites
{
get
{
if (this.accumulatedOwnerMetadataWrites == null)
{
this.accumulatedOwnerMetadataWrites = new Dictionary<XName, InstanceValue>();
}
return this.accumulatedOwnerMetadataWrites;
}
}
public InstanceValueConsistency InstanceKeysConsistency { get; internal set; }
public IDictionary<Guid, InstanceKeyView> InstanceKeys
{
get
{
return this.keys ?? InstanceView.emptyKeys;
}
internal set
{
Fx.AssertAndThrow(!IsViewFrozen, "Setting Keys on frozen View.");
this.keys = value;
}
}
// Arch prefers ReadOnlyCollection here to ICollection.
[SuppressMessage(FxCop.Category.Usage, FxCop.Rule.CollectionPropertiesShouldBeReadOnly,
Justification = "property is of ReadOnlyCollection type")]
public ReadOnlyCollection<InstanceStoreQueryResult> InstanceStoreQueryResults
{
get
{
if (this.queryResults == null)
{
this.queryResults = new ReadOnlyCollection<InstanceStoreQueryResult>(QueryResultsBacking);
}
return this.queryResults;
}
internal set
{
Fx.AssertAndThrow(!IsViewFrozen, "Setting InstanceStoreQueryResults on frozen View.");
this.queryResults = null;
if (value == null)
{
this.queryResultsBackingCollection = null;
}
else
{
this.queryResultsBackingCollection = new Collection<InstanceStoreQueryResult>();
foreach (InstanceStoreQueryResult queryResult in value)
{
this.queryResultsBackingCollection.Add(queryResult);
}
}
}
}
internal Collection<InstanceStoreQueryResult> QueryResultsBacking
{
get
{
if (this.queryResultsBackingCollection == null)
{
this.queryResultsBackingCollection = new Collection<InstanceStoreQueryResult>();
}
return this.queryResultsBackingCollection;
}
}
internal void BindOwner(InstanceOwner owner)
{
Fx.AssertAndThrow(!IsViewFrozen, "BindOwner called on read-only InstanceView.");
Fx.Assert(owner != null, "Null owner passed to BindOwner.");
if (IsBoundToInstanceOwner)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.ContextAlreadyBoundToOwner));
}
InstanceOwner = owner;
}
internal void BindInstance(Guid instanceId)
{
Fx.AssertAndThrow(!IsViewFrozen, "BindInstance called on read-only InstanceView.");
Fx.Assert(instanceId != Guid.Empty, "Null instanceId passed to BindInstance.");
if (IsBoundToInstance)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.ContextAlreadyBoundToInstance));
}
InstanceId = instanceId;
IsBoundToInstance = true;
}
internal void BindLock(long instanceVersion)
{
Fx.AssertAndThrow(!IsViewFrozen, "BindLock called on read-only InstanceView.");
Fx.Assert(instanceVersion >= 0, "Negative instanceVersion passed to BindLock.");
if (!IsBoundToInstanceOwner)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.ContextMustBeBoundToOwner));
}
if (!IsBoundToInstance)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.ContextMustBeBoundToInstance));
}
if (Interlocked.CompareExchange(ref this.instanceVersion, instanceVersion, -1) != -1)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.ContextAlreadyBoundToLock));
}
}
// This method is called when IPC.BindReclaimedLock is called. It reserves the lock, so that future calls to this or BindLock can be prevented.
// We set the version to -(instanceVersion + 2) so that 0 maps to -2 (since -1 is special).
internal void StartBindLock(long instanceVersion)
{
Fx.AssertAndThrow(!IsViewFrozen, "StartBindLock called on read-only InstanceView.");
Fx.Assert(instanceVersion >= 0, "Negative instanceVersion passed to StartBindLock.");
if (!IsBoundToInstanceOwner)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.ContextMustBeBoundToOwner));
}
if (!IsBoundToInstance)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.ContextMustBeBoundToInstance));
}
if (Interlocked.CompareExchange(ref this.instanceVersion, checked(-instanceVersion - 2), -1) != -1)
{
throw Fx.Exception.AsError(new InvalidOperationException(SRCore.ContextAlreadyBoundToLock));
}
}
// This completes the bind started in StartBindLock.
internal void FinishBindLock(long instanceVersion)
{
Fx.AssertAndThrow(!IsViewFrozen, "FinishBindLock called on read-only InstanceView.");
Fx.Assert(IsBoundToInstanceOwner, "Must be bound to owner, checked in StartBindLock.");
Fx.Assert(IsBoundToInstance, "Must be bound to instance, checked in StartBindLock.");
long result = Interlocked.CompareExchange(ref this.instanceVersion, instanceVersion, -instanceVersion - 2);
Fx.AssertAndThrow(result == -instanceVersion - 2, "FinishBindLock called with mismatched instance version.");
}
internal void MakeReadOnly()
{
IsViewFrozen = true;
}
internal InstanceView Clone()
{
return new InstanceView(this);
}
bool IsViewFrozen { get; set; }
}
}
|