You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@ -0,0 +1,52 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
using System.ServiceModel.Channels;
|
||||
using System.Globalization;
|
||||
using System.Xml;
|
||||
|
||||
class DurableDispatcherAddressingFault : MessageFault
|
||||
{
|
||||
const string missingContextHeaderFaultName = "MissingContext";
|
||||
|
||||
FaultCode faultCode;
|
||||
FaultReason faultReason;
|
||||
|
||||
public DurableDispatcherAddressingFault()
|
||||
{
|
||||
faultCode = FaultCode.CreateSenderFaultCode(missingContextHeaderFaultName, ContextMessageHeader.ContextHeaderNamespace);
|
||||
faultReason = new FaultReason(new FaultReasonText(SR2.GetString(SR2.CurrentOperationCannotCreateInstance), CultureInfo.CurrentCulture));
|
||||
}
|
||||
|
||||
public override FaultCode Code
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.faultCode;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool HasDetail
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override FaultReason Reason
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.faultReason;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnWriteDetailContents(XmlDictionaryWriter writer)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
//----------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
using System.ServiceModel.Channels;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
abstract class DurableErrorHandler : IErrorHandler
|
||||
{
|
||||
bool debug;
|
||||
|
||||
public DurableErrorHandler(bool debug)
|
||||
{
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
public static void CleanUpInstanceContextAtOperationCompletion()
|
||||
{
|
||||
if (OperationContext.Current == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.NoOperationContext));
|
||||
}
|
||||
|
||||
DurableMessageDispatchInspector.SuppressContextOnReply(OperationContext.Current);
|
||||
OperationContext.Current.InstanceContext.IncomingChannels.Clear();
|
||||
}
|
||||
|
||||
public bool HandleError(Exception error)
|
||||
{
|
||||
return IsUserCodeException(error);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Globalization", "CA1304")]
|
||||
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
|
||||
{
|
||||
if (fault == null && IsUserCodeException(error))
|
||||
{
|
||||
FaultCode code = new FaultCode(FaultCodeConstants.Codes.InternalServiceFault, FaultCodeConstants.Namespaces.NetDispatch);
|
||||
code = FaultCode.CreateReceiverFaultCode(code);
|
||||
|
||||
string action = FaultCodeConstants.Actions.NetDispatcher;
|
||||
MessageFault messageFault;
|
||||
|
||||
if (this.debug)
|
||||
{
|
||||
Exception toTrace = GetExceptionToTrace(error);
|
||||
messageFault = MessageFault.CreateFault(code, new FaultReason(toTrace.Message), new ExceptionDetail(toTrace));
|
||||
}
|
||||
else
|
||||
{
|
||||
string reason = SR.GetString(SR.SFxInternalServerError);
|
||||
messageFault = MessageFault.CreateFault(code, new FaultReason(reason));
|
||||
}
|
||||
fault = Message.CreateMessage(version, messageFault, action);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Exception GetExceptionToTrace(Exception error)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
protected abstract bool IsUserCodeException(Exception error);
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
using System.Runtime;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Persistence;
|
||||
|
||||
abstract class DurableInstance : CommunicationObject, IExtension<InstanceContext>
|
||||
{
|
||||
DurableInstanceContextProvider instanceContextProvider;
|
||||
Guid instanceId;
|
||||
|
||||
protected DurableInstance(DurableInstanceContextProvider instanceContextProvider, Guid instanceId)
|
||||
{
|
||||
if (instanceContextProvider == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("instanceContextProvider");
|
||||
}
|
||||
|
||||
this.instanceId = instanceId;
|
||||
this.instanceContextProvider = instanceContextProvider;
|
||||
}
|
||||
|
||||
public Guid InstanceId
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.instanceId;
|
||||
}
|
||||
}
|
||||
|
||||
protected override TimeSpan DefaultCloseTimeout
|
||||
{
|
||||
get { return PersistenceProvider.DefaultOpenClosePersistenceTimout; }
|
||||
}
|
||||
|
||||
protected override TimeSpan DefaultOpenTimeout
|
||||
{
|
||||
get { return PersistenceProvider.DefaultOpenClosePersistenceTimout; }
|
||||
}
|
||||
|
||||
public void DecrementActivityCount()
|
||||
{
|
||||
instanceContextProvider.DecrementActivityCount(this.instanceId);
|
||||
}
|
||||
|
||||
void IExtension<InstanceContext>.Attach(InstanceContext owner)
|
||||
{
|
||||
}
|
||||
|
||||
void IExtension<InstanceContext>.Detach(InstanceContext owner)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnAbort()
|
||||
{
|
||||
}
|
||||
|
||||
protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
|
||||
{
|
||||
return new CompletedAsyncResult(callback, state);
|
||||
}
|
||||
|
||||
protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
|
||||
{
|
||||
return new CompletedAsyncResult(callback, state);
|
||||
}
|
||||
|
||||
protected override void OnClose(TimeSpan timeout)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnEndClose(IAsyncResult result)
|
||||
{
|
||||
CompletedAsyncResult.End(result);
|
||||
}
|
||||
|
||||
protected override void OnEndOpen(IAsyncResult result)
|
||||
{
|
||||
CompletedAsyncResult.End(result);
|
||||
}
|
||||
|
||||
protected override void OnOpen(TimeSpan timeout)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,464 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime;
|
||||
using System.Runtime.Diagnostics;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Description;
|
||||
using System.ServiceModel.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
|
||||
abstract class DurableInstanceContextProvider : IInstanceContextProvider
|
||||
{
|
||||
ContextCache contextCache;
|
||||
|
||||
bool isPerCall;
|
||||
ServiceHostBase serviceHostBase;
|
||||
|
||||
protected DurableInstanceContextProvider(ServiceHostBase serviceHostBase, bool isPerCall)
|
||||
{
|
||||
if (serviceHostBase == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serviceHostBase");
|
||||
}
|
||||
|
||||
this.serviceHostBase = serviceHostBase;
|
||||
|
||||
if (serviceHostBase.Description.Behaviors.Find<ServiceThrottlingBehavior>() == null)
|
||||
{
|
||||
serviceHostBase.ServiceThrottle.MaxConcurrentInstances = (new ServiceThrottlingBehavior()).MaxConcurrentInstances;
|
||||
}
|
||||
this.contextCache = new ContextCache();
|
||||
this.isPerCall = isPerCall;
|
||||
}
|
||||
|
||||
protected ContextCache Cache
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.contextCache;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual InstanceContext GetExistingInstanceContext(Message message, IContextChannel channel)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
|
||||
}
|
||||
if (channel == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("channel");
|
||||
}
|
||||
|
||||
Guid instanceId = GetInstanceIdFromMessage(message);
|
||||
InstanceContext result = null;
|
||||
|
||||
if (instanceId != Guid.Empty) //Not an activation request.
|
||||
{
|
||||
if (contextCache.TryGetInstanceContext(instanceId, out result))
|
||||
{
|
||||
lock (result.ThisLock)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(channel.SessionId) && !result.IncomingChannels.Contains(channel))
|
||||
{
|
||||
result.IncomingChannels.Add(channel);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int GetReferenceCount(Guid instanceId)
|
||||
{
|
||||
return this.Cache.GetReferenceCount(instanceId);
|
||||
}
|
||||
|
||||
public virtual void InitializeInstanceContext(InstanceContext instanceContext, Message message, IContextChannel channel)
|
||||
{
|
||||
if (instanceContext == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("instanceContext");
|
||||
}
|
||||
if (message == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
|
||||
}
|
||||
if (channel == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("channel");
|
||||
}
|
||||
|
||||
Guid instanceId = GetInstanceIdFromMessage(message);
|
||||
DurableInstance durableInstance;
|
||||
if (instanceId == Guid.Empty) //Activation Request.
|
||||
{
|
||||
instanceId = Guid.NewGuid();
|
||||
durableInstance = this.OnCreateNewInstance(instanceId);
|
||||
message.Properties[DurableMessageDispatchInspector.NewDurableInstanceIdPropertyName] = instanceId;
|
||||
}
|
||||
else
|
||||
{
|
||||
durableInstance = this.OnGetExistingInstance(instanceId);
|
||||
}
|
||||
|
||||
Fx.Assert(durableInstance != null, "Durable instance should never be null at this point.");
|
||||
durableInstance.Open();
|
||||
|
||||
instanceContext.Extensions.Add(durableInstance);
|
||||
|
||||
if (!string.IsNullOrEmpty(channel.SessionId))
|
||||
{
|
||||
instanceContext.IncomingChannels.Add(channel);
|
||||
}
|
||||
|
||||
contextCache.AddInstanceContext(instanceId, instanceContext);
|
||||
|
||||
if (DiagnosticUtility.ShouldTraceInformation)
|
||||
{
|
||||
string traceText = SR.GetString(SR.TraceCodeDICPInstanceContextCached, instanceId);
|
||||
TraceUtility.TraceEvent(TraceEventType.Information,
|
||||
TraceCode.DICPInstanceContextCached, SR.GetString(SR.TraceCodeDICPInstanceContextCached),
|
||||
new StringTraceRecord("InstanceDetail", traceText),
|
||||
this, null);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsIdle(InstanceContext instanceContext)
|
||||
{
|
||||
bool removed = false;
|
||||
|
||||
if (instanceContext == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("instanceContext");
|
||||
}
|
||||
|
||||
DurableInstance durableInstance = instanceContext.Extensions.Find<DurableInstance>();
|
||||
|
||||
if (durableInstance == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||||
new InvalidOperationException(
|
||||
SR2.GetString(
|
||||
SR2.RequiredInstanceContextExtensionNotFound,
|
||||
typeof(DurableInstance).Name)));
|
||||
}
|
||||
|
||||
lock (instanceContext.ThisLock)
|
||||
{
|
||||
if (instanceContext.IncomingChannels.Count == 0)
|
||||
{
|
||||
removed = contextCache.RemoveIfNotBusy(durableInstance.InstanceId, instanceContext);
|
||||
}
|
||||
}
|
||||
|
||||
if (removed && DiagnosticUtility.ShouldTraceInformation)
|
||||
{
|
||||
string traceText = SR.GetString(SR.TraceCodeDICPInstanceContextRemovedFromCache, durableInstance.InstanceId);
|
||||
TraceUtility.TraceEvent(TraceEventType.Information,
|
||||
TraceCode.DICPInstanceContextRemovedFromCache, SR.GetString(SR.TraceCodeDICPInstanceContextRemovedFromCache),
|
||||
new StringTraceRecord("InstanceDetail", traceText),
|
||||
this, null);
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
public virtual void NotifyIdle(InstanceContextIdleCallback callback, InstanceContext instanceContext)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//Called by MessageInspector.BeforeReply
|
||||
internal void DecrementActivityCount(Guid instanceId)
|
||||
{
|
||||
contextCache.ReleaseReference(instanceId);
|
||||
}
|
||||
|
||||
internal void UnbindAbortedInstance(InstanceContext instanceContext, Guid instanceId)
|
||||
{
|
||||
if (instanceContext == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("instanceContext");
|
||||
}
|
||||
|
||||
//We made our best effor to clean up the instancecontext out of our cache.
|
||||
//If another request already in middle of processing the request on InstanceContext
|
||||
//It will Fail with CommunicationException.
|
||||
this.contextCache.Remove(instanceId, instanceContext);
|
||||
}
|
||||
|
||||
protected virtual Guid GetInstanceIdFromMessage(Message message)
|
||||
{
|
||||
if (!this.isPerCall)
|
||||
{
|
||||
ContextMessageProperty contextProperties = null;
|
||||
string instanceId = null;
|
||||
|
||||
if (ContextMessageProperty.TryGet(message, out contextProperties))
|
||||
{
|
||||
if (contextProperties.Context.TryGetValue(WellKnownContextProperties.InstanceId, out instanceId))
|
||||
{
|
||||
return Fx.CreateGuid(instanceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Guid.Empty;
|
||||
}
|
||||
|
||||
protected abstract DurableInstance OnCreateNewInstance(Guid instanceId);
|
||||
protected abstract DurableInstance OnGetExistingInstance(Guid instanceId);
|
||||
|
||||
//This class takes self contained lock, never calls out with lock taken.
|
||||
protected class ContextCache
|
||||
{
|
||||
Dictionary<Guid, ContextItem> contextCache;
|
||||
object lockObject = new object();
|
||||
|
||||
public ContextCache()
|
||||
{
|
||||
contextCache = new Dictionary<Guid, ContextItem>();
|
||||
}
|
||||
|
||||
public void AddInstanceContext(Guid instanceId, InstanceContext instanceContext)
|
||||
{
|
||||
ContextItem contextItem;
|
||||
int? referenceCount = null;
|
||||
|
||||
lock (lockObject)
|
||||
{
|
||||
if (!contextCache.TryGetValue(instanceId, out contextItem))
|
||||
{
|
||||
//This will be the case for activation request.
|
||||
contextItem = new ContextItem(instanceId);
|
||||
referenceCount = contextItem.AddReference();
|
||||
contextCache.Add(instanceId, contextItem);
|
||||
}
|
||||
}
|
||||
contextItem.InstanceContext = instanceContext;
|
||||
|
||||
if (DiagnosticUtility.ShouldTraceInformation && referenceCount.HasValue)
|
||||
{
|
||||
string traceText = SR2.GetString(SR2.DurableInstanceRefCountToInstanceContext, instanceId, referenceCount.Value);
|
||||
TraceUtility.TraceEvent(TraceEventType.Information,
|
||||
TraceCode.InstanceContextBoundToDurableInstance, SR.GetString(SR.TraceCodeInstanceContextBoundToDurableInstance),
|
||||
new StringTraceRecord("InstanceDetail", traceText),
|
||||
this, null);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(Guid instanceId, InstanceContext instanceContext)
|
||||
{
|
||||
ContextItem contextItem = null;
|
||||
|
||||
lock (this.lockObject)
|
||||
{
|
||||
if (contextCache.TryGetValue(instanceId, out contextItem))
|
||||
{
|
||||
return object.ReferenceEquals(contextItem.InstanceContext, instanceContext);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetReferenceCount(Guid instanceId)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
lock (lockObject)
|
||||
{
|
||||
ContextItem contextItem;
|
||||
if (contextCache.TryGetValue(instanceId, out contextItem))
|
||||
{
|
||||
result = contextItem.ReferenceCount;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void ReleaseReference(Guid instanceId)
|
||||
{
|
||||
int referenceCount = -1;
|
||||
ContextItem contextItem;
|
||||
|
||||
lock (lockObject)
|
||||
{
|
||||
if (contextCache.TryGetValue(instanceId, out contextItem))
|
||||
{
|
||||
referenceCount = contextItem.ReleaseReference();
|
||||
}
|
||||
else
|
||||
{
|
||||
Fx.Assert(false, "Cannot Release Reference of non exisiting items");
|
||||
}
|
||||
}
|
||||
|
||||
if (DiagnosticUtility.ShouldTraceInformation)
|
||||
{
|
||||
string traceText = SR2.GetString(SR2.DurableInstanceRefCountToInstanceContext, instanceId, referenceCount);
|
||||
TraceUtility.TraceEvent(TraceEventType.Information,
|
||||
TraceCode.InstanceContextDetachedFromDurableInstance, SR.GetString(SR.TraceCodeInstanceContextDetachedFromDurableInstance),
|
||||
new StringTraceRecord("InstanceDetail", traceText),
|
||||
this, null);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(Guid instanceId, InstanceContext instanceContext)
|
||||
{
|
||||
lock (this.lockObject)
|
||||
{
|
||||
ContextItem contextItem = null;
|
||||
if (this.contextCache.TryGetValue(instanceId, out contextItem))
|
||||
{
|
||||
if (object.ReferenceEquals(instanceContext, contextItem.InstanceContext))
|
||||
{
|
||||
return this.contextCache.Remove(instanceId);
|
||||
}
|
||||
}
|
||||
//InstanceContext is not in memory.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool RemoveIfNotBusy(Guid instanceId, InstanceContext instanceContext)
|
||||
{
|
||||
lock (lockObject)
|
||||
{
|
||||
ContextItem contextItem = null;
|
||||
if (contextCache.TryGetValue(instanceId, out contextItem))
|
||||
{
|
||||
if (object.ReferenceEquals(contextItem.InstanceContext, instanceContext))
|
||||
{
|
||||
return (!contextItem.HasOutstandingReference)
|
||||
&& (contextCache.Remove(instanceId));
|
||||
}
|
||||
}
|
||||
//InstanceContext is not in memory.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//Helper method to call from GetExistingInstanceContext
|
||||
//returns true : If InstanceContext is found in cache & guaranteed to stay in cache until ReleaseReference is called.
|
||||
//returns false : If InstanceContext is not found in cache;
|
||||
// reference & slot is created for the ID;
|
||||
// InitializeInstanceContext to call AddInstanceContext.
|
||||
public bool TryGetInstanceContext(Guid instanceId, out InstanceContext instanceContext)
|
||||
{
|
||||
ContextItem contextItem;
|
||||
instanceContext = null;
|
||||
int referenceCount = -1;
|
||||
|
||||
try
|
||||
{
|
||||
lock (lockObject)
|
||||
{
|
||||
if (!contextCache.TryGetValue(instanceId, out contextItem))
|
||||
{
|
||||
contextItem = new ContextItem(instanceId);
|
||||
referenceCount = contextItem.AddReference();
|
||||
contextCache.Add(instanceId, contextItem);
|
||||
return false;
|
||||
}
|
||||
referenceCount = contextItem.AddReference();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (DiagnosticUtility.ShouldTraceInformation)
|
||||
{
|
||||
string traceText = SR2.GetString(SR2.DurableInstanceRefCountToInstanceContext, instanceId, referenceCount);
|
||||
TraceUtility.TraceEvent(TraceEventType.Information,
|
||||
TraceCode.InstanceContextBoundToDurableInstance, SR.GetString(SR.TraceCodeInstanceContextBoundToDurableInstance),
|
||||
new StringTraceRecord("InstanceDetail", traceText),
|
||||
this, null);
|
||||
}
|
||||
}
|
||||
instanceContext = contextItem.InstanceContext;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
class ContextItem
|
||||
{
|
||||
InstanceContext context;
|
||||
Guid instanceId;
|
||||
object lockObject;
|
||||
int referenceCount;
|
||||
|
||||
public ContextItem(Guid instanceId)
|
||||
{
|
||||
lockObject = new object();
|
||||
referenceCount = 0;
|
||||
this.instanceId = instanceId;
|
||||
}
|
||||
|
||||
public bool HasOutstandingReference
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.referenceCount > 0;
|
||||
}
|
||||
}
|
||||
|
||||
public InstanceContext InstanceContext
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.context == null)
|
||||
{
|
||||
lock (this.lockObject)
|
||||
{
|
||||
if (this.context == null)
|
||||
{
|
||||
Monitor.Wait(this.lockObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
Fx.Assert(this.context != null, "Context cannot be null at this point");
|
||||
return this.context;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
|
||||
}
|
||||
this.context = value;
|
||||
lock (this.lockObject)
|
||||
{
|
||||
Monitor.PulseAll(this.lockObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int ReferenceCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.referenceCount;
|
||||
}
|
||||
}
|
||||
|
||||
public int AddReference()
|
||||
{
|
||||
//Called from higher locks taken
|
||||
return ++this.referenceCount;
|
||||
}
|
||||
|
||||
public int ReleaseReference()
|
||||
{
|
||||
Fx.Assert(referenceCount > 0, "Reference count gone to negative");
|
||||
//Called from higher locks taken
|
||||
return --this.referenceCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
using System.ServiceModel.Channels;
|
||||
|
||||
class DurableInstanceProvider : IInstanceProvider
|
||||
{
|
||||
DurableInstanceContextProvider durableInstanceContextProvider;
|
||||
|
||||
public DurableInstanceProvider(DurableInstanceContextProvider instanceContextProvider)
|
||||
{
|
||||
this.durableInstanceContextProvider = instanceContextProvider;
|
||||
}
|
||||
|
||||
public object GetInstance(InstanceContext instanceContext)
|
||||
{
|
||||
return ((IInstanceProvider) this).GetInstance(instanceContext, null);
|
||||
}
|
||||
|
||||
public object GetInstance(InstanceContext instanceContext, Message message)
|
||||
{
|
||||
if (instanceContext == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("instanceContext");
|
||||
}
|
||||
|
||||
return instanceContext.Extensions.Find<DurableInstance>();
|
||||
}
|
||||
|
||||
public virtual void ReleaseInstance(InstanceContext instanceContext, object instance)
|
||||
{
|
||||
if (instanceContext == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("instanceContext");
|
||||
}
|
||||
|
||||
if (instance == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("instance");
|
||||
}
|
||||
|
||||
DurableInstance durableInstance = (DurableInstance) instance;
|
||||
|
||||
if (instanceContext.State == CommunicationState.Faulted || instanceContext.Aborted)
|
||||
{
|
||||
durableInstance.Abort();
|
||||
this.durableInstanceContextProvider.UnbindAbortedInstance(instanceContext, durableInstance.InstanceId);
|
||||
}
|
||||
else if (instanceContext.State == CommunicationState.Closed)
|
||||
{
|
||||
durableInstance.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
using System.ServiceModel.Channels;
|
||||
|
||||
class DurableMessageDispatchInspector : IDispatchMessageInspector
|
||||
{
|
||||
public const string NewDurableInstanceIdPropertyName = "newDurableInstanceIdProperty";
|
||||
const string suppressContextOnReply = "suppressContextOnReply";
|
||||
SessionMode sessionMode;
|
||||
|
||||
public DurableMessageDispatchInspector(SessionMode sessionMode)
|
||||
{
|
||||
this.sessionMode = sessionMode;
|
||||
}
|
||||
|
||||
public static void SuppressContextOnReply(OperationContext operationContext)
|
||||
{
|
||||
if (operationContext == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("operationContext");
|
||||
}
|
||||
operationContext.OutgoingMessageProperties[suppressContextOnReply] = true;
|
||||
}
|
||||
|
||||
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
|
||||
{
|
||||
if (instanceContext == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("instanceContext");
|
||||
}
|
||||
|
||||
if (request == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("request");
|
||||
}
|
||||
|
||||
if (sessionMode != SessionMode.NotAllowed)
|
||||
{
|
||||
object result = null;
|
||||
|
||||
if (request.Properties.TryGetValue(NewDurableInstanceIdPropertyName, out result))
|
||||
{
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void BeforeSendReply(ref Message reply, object correlationState)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (reply != null)
|
||||
{
|
||||
ContextMessageProperty context = null;
|
||||
|
||||
if (sessionMode == SessionMode.NotAllowed || reply.Properties.ContainsKey(suppressContextOnReply))
|
||||
{
|
||||
if (ContextMessageProperty.TryGet(reply, out context))
|
||||
{
|
||||
context.Context.Clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string newInstanceId = correlationState as string;
|
||||
|
||||
if (newInstanceId != null)
|
||||
{
|
||||
|
||||
if (!ContextMessageProperty.TryGet(reply, out context))
|
||||
{
|
||||
context = new ContextMessageProperty();
|
||||
context.Context[WellKnownContextProperties.InstanceId] = newInstanceId;
|
||||
context.AddOrReplaceInMessage(reply);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Context[WellKnownContextProperties.InstanceId] = newInstanceId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
DurableInstance durableInstance = OperationContext.Current.InstanceContext.Extensions.Find<DurableInstance>();
|
||||
|
||||
if (durableInstance == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||||
new InvalidOperationException(
|
||||
SR2.GetString(
|
||||
SR2.RequiredInstanceContextExtensionNotFound,
|
||||
typeof(DurableInstance).Name)));
|
||||
}
|
||||
//Decrement InstanceActivity Count
|
||||
durableInstance.DecrementActivityCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
using System;
|
||||
using System.ServiceModel.Description;
|
||||
|
||||
[Obsolete("The WF3 types are deprecated. Instead, please use the new WF4 types from System.Activities.*")]
|
||||
public static class DurableOperationContext
|
||||
{
|
||||
public static Guid InstanceId
|
||||
{
|
||||
get
|
||||
{
|
||||
ServiceDurableInstance durableInstance = GetInstanceContextExtension();
|
||||
|
||||
return durableInstance.InstanceId;
|
||||
}
|
||||
}
|
||||
|
||||
public static void AbortInstance()
|
||||
{
|
||||
ServiceDurableInstance durableInstance = GetInstanceContextExtension();
|
||||
|
||||
durableInstance.AbortInstance();
|
||||
}
|
||||
|
||||
public static void CompleteInstance()
|
||||
{
|
||||
ServiceDurableInstance durableInstance = GetInstanceContextExtension();
|
||||
|
||||
durableInstance.MarkForCompletion();
|
||||
}
|
||||
|
||||
internal static void BeginOperation()
|
||||
{
|
||||
OperationContext operationContext = OperationContext.Current;
|
||||
|
||||
if (operationContext != null)
|
||||
{
|
||||
operationContext.Extensions.Add(new DurableOperationContext.IsInOperation());
|
||||
}
|
||||
}
|
||||
|
||||
internal static void EndOperation()
|
||||
{
|
||||
OperationContext operationContext = OperationContext.Current;
|
||||
|
||||
if (operationContext != null)
|
||||
{
|
||||
DurableOperationContext.IsInOperation isInOperation = operationContext.Extensions.Find<DurableOperationContext.IsInOperation>();
|
||||
|
||||
if (isInOperation != null)
|
||||
{
|
||||
operationContext.Extensions.Remove(isInOperation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ServiceDurableInstance GetInstanceContextExtension()
|
||||
{
|
||||
OperationContext operationContext = OperationContext.Current;
|
||||
|
||||
if (operationContext == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||||
new InvalidOperationException(
|
||||
SR2.GetString(
|
||||
SR2.OnlyCallableFromServiceOperation,
|
||||
typeof(DurableOperationContext).Name)));
|
||||
}
|
||||
|
||||
IsInOperation isInOperation = operationContext.Extensions.Find<IsInOperation>();
|
||||
|
||||
if (isInOperation == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||||
new InvalidOperationException(
|
||||
SR2.GetString(
|
||||
SR2.OnlyCallableWhileInOperation,
|
||||
typeof(DurableOperationContext).Name)));
|
||||
}
|
||||
|
||||
InstanceContext currentInstanceContext = operationContext.InstanceContext;
|
||||
|
||||
if (currentInstanceContext == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||||
new InvalidOperationException(
|
||||
SR2.GetString(
|
||||
SR2.OnlyCallableFromServiceOperation,
|
||||
typeof(DurableOperationContext).Name)));
|
||||
}
|
||||
|
||||
ServiceDurableInstance durableInstance =
|
||||
currentInstanceContext.Extensions.Find<ServiceDurableInstance>();
|
||||
|
||||
if (durableInstance == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||||
new InvalidOperationException(
|
||||
SR2.GetString(
|
||||
SR2.OnlyCallableFromDurableService,
|
||||
typeof(DurableOperationContext).Name,
|
||||
typeof(DurableServiceAttribute).Name)));
|
||||
}
|
||||
|
||||
return durableInstance;
|
||||
}
|
||||
|
||||
class IsInOperation : IExtension<OperationContext>
|
||||
{
|
||||
public void Attach(OperationContext owner)
|
||||
{
|
||||
}
|
||||
|
||||
public void Detach(OperationContext owner)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
using System.ServiceModel.Description;
|
||||
using System.Workflow.Runtime;
|
||||
using System.Runtime;
|
||||
|
||||
class DurableRuntimeValidator
|
||||
{
|
||||
ConcurrencyMode concurrencyMode;
|
||||
UnknownExceptionAction exceptionAction;
|
||||
bool saveStateInOperationTransaction;
|
||||
bool validated;
|
||||
|
||||
public DurableRuntimeValidator(bool saveStateInOperationTransaction, UnknownExceptionAction exceptionAction)
|
||||
{
|
||||
this.saveStateInOperationTransaction = saveStateInOperationTransaction;
|
||||
this.exceptionAction = exceptionAction;
|
||||
this.validated = false;
|
||||
}
|
||||
|
||||
public ConcurrencyMode ConcurrencyMode
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!this.validated)
|
||||
{
|
||||
ValidateRuntime();
|
||||
}
|
||||
|
||||
return concurrencyMode;
|
||||
}
|
||||
}
|
||||
|
||||
public void ValidateRuntime()
|
||||
{
|
||||
if (!this.validated)
|
||||
{
|
||||
Fx.Assert(
|
||||
OperationContext.Current != null &&
|
||||
OperationContext.Current.EndpointDispatcher != null &&
|
||||
OperationContext.Current.EndpointDispatcher.DispatchRuntime != null,
|
||||
"There shouldn't have been a null value in " +
|
||||
"OperationContext.Current.EndpointDispatcher.DispatchRuntime.");
|
||||
|
||||
this.concurrencyMode = OperationContext.Current.EndpointDispatcher.DispatchRuntime.ConcurrencyMode;
|
||||
|
||||
if (this.concurrencyMode == ConcurrencyMode.Multiple)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||||
new InvalidOperationException(
|
||||
SR2.GetString(SR2.ConcurrencyMultipleNotSupported)));
|
||||
}
|
||||
|
||||
if (this.saveStateInOperationTransaction && this.concurrencyMode != ConcurrencyMode.Single)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||||
new InvalidOperationException(
|
||||
SR2.GetString(SR2.SaveStateInTransactionRequiresSingle)));
|
||||
}
|
||||
|
||||
if (this.concurrencyMode == ConcurrencyMode.Reentrant
|
||||
&& this.exceptionAction == UnknownExceptionAction.AbortInstance)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||||
new InvalidOperationException(
|
||||
SR2.GetString(SR2.ConcurrencyReentrantAndAbortNotSupported)));
|
||||
}
|
||||
|
||||
this.validated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,92 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Persistence;
|
||||
using System.Threading;
|
||||
using System.ServiceModel.Description;
|
||||
using System.ServiceModel.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.Diagnostics;
|
||||
|
||||
class ServiceDurableInstanceContextProvider : DurableInstanceContextProvider
|
||||
{
|
||||
TimeSpan operationTimeout;
|
||||
PersistenceProviderFactory providerFactory;
|
||||
DurableRuntimeValidator runtimeValidator;
|
||||
bool saveStateInOperationTransaction;
|
||||
Type serviceType;
|
||||
UnknownExceptionAction unknownExceptionAction;
|
||||
|
||||
public ServiceDurableInstanceContextProvider(
|
||||
ServiceHostBase serviceHostBase,
|
||||
bool isPercall,
|
||||
Type serviceType,
|
||||
PersistenceProviderFactory providerFactory,
|
||||
bool saveStateInOperationTransaction,
|
||||
UnknownExceptionAction unknownExceptionAction,
|
||||
DurableRuntimeValidator runtimeValidator,
|
||||
TimeSpan operationTimeout)
|
||||
: base(serviceHostBase, isPercall)
|
||||
{
|
||||
if (serviceType == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serviceType");
|
||||
}
|
||||
|
||||
if (providerFactory == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("providerFactory");
|
||||
}
|
||||
|
||||
if (runtimeValidator == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("runtimeValidator");
|
||||
}
|
||||
|
||||
this.serviceType = serviceType;
|
||||
this.providerFactory = providerFactory;
|
||||
this.saveStateInOperationTransaction = saveStateInOperationTransaction;
|
||||
this.unknownExceptionAction = unknownExceptionAction;
|
||||
this.runtimeValidator = runtimeValidator;
|
||||
this.operationTimeout = operationTimeout;
|
||||
}
|
||||
|
||||
protected override DurableInstance OnCreateNewInstance(Guid instanceId)
|
||||
{
|
||||
if (DiagnosticUtility.ShouldTraceInformation)
|
||||
{
|
||||
string traceText = SR2.GetString(SR2.InstanceContextProviderCreatedNewInstance, "Service", instanceId);
|
||||
TraceUtility.TraceEvent(TraceEventType.Information,
|
||||
TraceCode.ActivatingMessageReceived, SR.GetString(SR.TraceCodeActivatingMessageReceived),
|
||||
new StringTraceRecord("NewInstanceDetail", traceText),
|
||||
this, null);
|
||||
}
|
||||
|
||||
return new ServiceDurableInstance(
|
||||
this.providerFactory.CreateProvider(instanceId),
|
||||
this,
|
||||
this.saveStateInOperationTransaction,
|
||||
this.unknownExceptionAction,
|
||||
this.runtimeValidator,
|
||||
this.operationTimeout,
|
||||
this.serviceType);
|
||||
}
|
||||
|
||||
protected override DurableInstance OnGetExistingInstance(Guid instanceId)
|
||||
{
|
||||
return new ServiceDurableInstance(
|
||||
this.providerFactory.CreateProvider(instanceId),
|
||||
this,
|
||||
this.saveStateInOperationTransaction,
|
||||
this.unknownExceptionAction,
|
||||
this.runtimeValidator,
|
||||
this.operationTimeout);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
using System;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.Collections;
|
||||
|
||||
class ServiceErrorHandler : DurableErrorHandler
|
||||
{
|
||||
const string dataKey = "System.ServiceModel.Dispatcher.ServiceErrorHandler.MarkExeption";
|
||||
|
||||
public ServiceErrorHandler(bool debug)
|
||||
: base(debug)
|
||||
{
|
||||
}
|
||||
|
||||
public static void MarkException(Exception toMark)
|
||||
{
|
||||
// From MSDN: The OutOfMemoryException, StackOverflowException and ThreadAbortException
|
||||
// classes always return a null reference for the value of the Data property.
|
||||
// These are fatal exceptions and therefore we don't care that we can't mark them.
|
||||
IDictionary data = toMark.Data;
|
||||
if (data != null && !data.IsReadOnly && !data.IsFixedSize)
|
||||
{
|
||||
data.Add(dataKey, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool IsUserCodeException(Exception error)
|
||||
{
|
||||
IDictionary data = error.Data;
|
||||
|
||||
if (data != null && data.Contains(dataKey))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,334 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime;
|
||||
using System.ServiceModel.Description;
|
||||
using System.ServiceModel.Diagnostics;
|
||||
|
||||
class ServiceOperationInvoker : IOperationInvoker
|
||||
{
|
||||
bool canCreateInstance;
|
||||
bool completesInstance;
|
||||
bool contractCausesSave;
|
||||
IOperationInvoker innerInvoker;
|
||||
|
||||
public ServiceOperationInvoker(IOperationInvoker innerInvoker, bool completesInstance, bool canCreateInstance, bool contractCausesSave)
|
||||
{
|
||||
if (innerInvoker == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("innerInvoker");
|
||||
}
|
||||
|
||||
this.innerInvoker = innerInvoker;
|
||||
this.completesInstance = completesInstance;
|
||||
this.canCreateInstance = canCreateInstance;
|
||||
this.contractCausesSave = contractCausesSave;
|
||||
}
|
||||
|
||||
public bool IsSynchronous
|
||||
{
|
||||
get { return this.innerInvoker.IsSynchronous; }
|
||||
}
|
||||
|
||||
public object[] AllocateInputs()
|
||||
{
|
||||
return this.innerInvoker.AllocateInputs();
|
||||
}
|
||||
|
||||
public object Invoke(object instance, object[] inputs, out object[] outputs)
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("instance");
|
||||
}
|
||||
|
||||
ServiceDurableInstance durableInstance = instance as ServiceDurableInstance;
|
||||
|
||||
if (durableInstance == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||||
new InvalidOperationException(
|
||||
SR2.GetString(SR2.InvokeCalledWithWrongType, typeof(DurableServiceAttribute).Name)));
|
||||
}
|
||||
|
||||
object serviceInstance = durableInstance.StartOperation(this.canCreateInstance);
|
||||
Exception operationException = null;
|
||||
|
||||
bool failFast = false;
|
||||
try
|
||||
{
|
||||
return this.innerInvoker.Invoke(serviceInstance, inputs, out outputs);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (Fx.IsFatal(e))
|
||||
{
|
||||
failFast = true;
|
||||
throw;
|
||||
}
|
||||
|
||||
operationException = e;
|
||||
ServiceErrorHandler.MarkException(e);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!failFast)
|
||||
{
|
||||
durableInstance.FinishOperation(this.completesInstance, this.contractCausesSave, operationException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("instance");
|
||||
}
|
||||
|
||||
ServiceDurableInstance durableInstance = instance as ServiceDurableInstance;
|
||||
|
||||
if (durableInstance == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||||
new InvalidOperationException(
|
||||
SR2.GetString(SR2.InvokeCalledWithWrongType, typeof(DurableServiceAttribute).Name)));
|
||||
}
|
||||
|
||||
return new InvokeAsyncResult(durableInstance, inputs, this, this.canCreateInstance, callback, state);
|
||||
}
|
||||
|
||||
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
|
||||
{
|
||||
return InvokeAsyncResult.End(out outputs, result);
|
||||
}
|
||||
|
||||
public class InvokeAsyncResult : AsyncResult
|
||||
{
|
||||
static AsyncCallback finishCallback = Fx.ThunkCallback(new AsyncCallback(FinishComplete));
|
||||
static AsyncCallback invokeCallback = Fx.ThunkCallback(new AsyncCallback(InvokeComplete));
|
||||
static AsyncCallback startCallback = Fx.ThunkCallback(new AsyncCallback(StartComplete));
|
||||
Exception completionException;
|
||||
ServiceDurableInstance durableInstance;
|
||||
object[] inputs;
|
||||
|
||||
ServiceOperationInvoker invoker;
|
||||
OperationContext operationContext;
|
||||
object[] outputs;
|
||||
object returnValue;
|
||||
object serviceInstance;
|
||||
|
||||
public InvokeAsyncResult(ServiceDurableInstance instance, object[] inputs, ServiceOperationInvoker invoker, bool canCreateInstance, AsyncCallback callback, object state)
|
||||
: base(callback, state)
|
||||
{
|
||||
this.invoker = invoker;
|
||||
this.inputs = inputs;
|
||||
this.durableInstance = instance;
|
||||
this.operationContext = OperationContext.Current;
|
||||
|
||||
IAsyncResult result = this.durableInstance.BeginStartOperation(canCreateInstance, startCallback, this);
|
||||
|
||||
if (result.CompletedSynchronously)
|
||||
{
|
||||
this.serviceInstance = this.durableInstance.EndStartOperation(result);
|
||||
if (DoInvoke())
|
||||
{
|
||||
Complete(true, this.completionException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static object End(out object[] outputs, IAsyncResult result)
|
||||
{
|
||||
InvokeAsyncResult invokeResult = AsyncResult.End<InvokeAsyncResult>(result);
|
||||
|
||||
outputs = invokeResult.outputs;
|
||||
|
||||
return invokeResult.returnValue;
|
||||
}
|
||||
|
||||
// We pass the exception to another thread
|
||||
[SuppressMessage("Reliability", "Reliability104")]
|
||||
[SuppressMessage("Microsoft.Design", "CA1031")]
|
||||
static void FinishComplete(IAsyncResult result)
|
||||
{
|
||||
if (result.CompletedSynchronously)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InvokeAsyncResult invokeResult = result.AsyncState as InvokeAsyncResult;
|
||||
Fx.Assert(invokeResult != null, "Async state should have been of type InvokeAsyncResult.");
|
||||
|
||||
try
|
||||
{
|
||||
invokeResult.durableInstance.EndFinishOperation(result);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (Fx.IsFatal(e))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
invokeResult.completionException = e;
|
||||
}
|
||||
|
||||
invokeResult.Complete(false, invokeResult.completionException);
|
||||
}
|
||||
|
||||
// We pass the exception to another thread
|
||||
[SuppressMessage("Reliability", "Reliability104")]
|
||||
[SuppressMessage("Microsoft.Design", "CA1031")]
|
||||
static void InvokeComplete(IAsyncResult resultParameter)
|
||||
{
|
||||
if (resultParameter.CompletedSynchronously)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InvokeAsyncResult invokeResult = resultParameter.AsyncState as InvokeAsyncResult;
|
||||
|
||||
Fx.Assert(invokeResult != null,
|
||||
"Async state should have been of type InvokeAsyncResult.");
|
||||
|
||||
try
|
||||
{
|
||||
invokeResult.returnValue = invokeResult.invoker.innerInvoker.InvokeEnd(invokeResult.serviceInstance, out invokeResult.outputs, resultParameter);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (Fx.IsFatal(e))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
ServiceErrorHandler.MarkException(e);
|
||||
invokeResult.completionException = e;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (invokeResult.DoFinish())
|
||||
{
|
||||
invokeResult.Complete(false, invokeResult.completionException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We pass the exception to another thread
|
||||
[SuppressMessage("Reliability", "Reliability104")]
|
||||
[SuppressMessage("Microsoft.Design", "CA1031")]
|
||||
static void StartComplete(IAsyncResult resultParameter)
|
||||
{
|
||||
if (resultParameter.CompletedSynchronously)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InvokeAsyncResult invokeResult = resultParameter.AsyncState as InvokeAsyncResult;
|
||||
|
||||
Fx.Assert(invokeResult != null,
|
||||
"Async state should have been of type InvokeAsyncResult.");
|
||||
|
||||
try
|
||||
{
|
||||
invokeResult.serviceInstance = invokeResult.durableInstance.EndStartOperation(resultParameter);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (Fx.IsFatal(e))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
invokeResult.Complete(false, e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (invokeResult.DoInvoke())
|
||||
{
|
||||
invokeResult.Complete(false, invokeResult.completionException);
|
||||
}
|
||||
}
|
||||
|
||||
// We pass the exception to another thread
|
||||
[SuppressMessage("Reliability", "Reliability104")]
|
||||
[SuppressMessage("Microsoft.Design", "CA1031")]
|
||||
bool DoFinish()
|
||||
{
|
||||
try
|
||||
{
|
||||
IAsyncResult result = this.durableInstance.BeginFinishOperation(this.invoker.completesInstance, this.invoker.contractCausesSave, this.completionException, finishCallback, this);
|
||||
|
||||
if (result.CompletedSynchronously)
|
||||
{
|
||||
this.durableInstance.EndFinishOperation(result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (Fx.IsFatal(e))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
this.completionException = e;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// We pass the exception to another thread
|
||||
[SuppressMessage("Reliability", "Reliability104")]
|
||||
[SuppressMessage("Microsoft.Design", "CA1031")]
|
||||
bool DoInvoke()
|
||||
{
|
||||
bool finishNow = false;
|
||||
|
||||
try
|
||||
{
|
||||
IAsyncResult result = null;
|
||||
|
||||
using (OperationContextScope operationScope = new OperationContextScope(this.operationContext))
|
||||
{
|
||||
result = this.invoker.innerInvoker.InvokeBegin(this.serviceInstance, this.inputs, invokeCallback, this);
|
||||
}
|
||||
|
||||
if (result.CompletedSynchronously)
|
||||
{
|
||||
this.returnValue = this.invoker.innerInvoker.InvokeEnd(this.serviceInstance, out this.outputs, result);
|
||||
finishNow = true;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (Fx.IsFatal(e))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
ServiceErrorHandler.MarkException(e);
|
||||
this.completionException = e;
|
||||
finishNow = true;
|
||||
}
|
||||
|
||||
if (finishNow)
|
||||
{
|
||||
if (DoFinish())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
//----------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
static class WellKnownContextProperties
|
||||
{
|
||||
public const string ConversationId = "conversationId";
|
||||
public const string InstanceId = "instanceId";
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
class WorkflowDispatchContext : IDisposable
|
||||
{
|
||||
|
||||
[ThreadStatic]
|
||||
static WorkflowDispatchContext workflowDispatchContext = null;
|
||||
bool isWorkflowStarting;
|
||||
bool synchronous;
|
||||
|
||||
public WorkflowDispatchContext(bool synchronous)
|
||||
: this(synchronous, false)
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
public WorkflowDispatchContext(bool synchronous, bool isWorkflowStarting)
|
||||
{
|
||||
this.synchronous = synchronous;
|
||||
this.isWorkflowStarting = isWorkflowStarting;
|
||||
workflowDispatchContext = this;
|
||||
}
|
||||
|
||||
public static WorkflowDispatchContext Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return workflowDispatchContext;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSynchronous
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.synchronous;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsWorkflowStarting
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.isWorkflowStarting;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
workflowDispatchContext = null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
using System.Workflow.Runtime;
|
||||
using System.Runtime.Diagnostics;
|
||||
using System.ServiceModel.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
|
||||
class WorkflowDurableInstance : DurableInstance
|
||||
{
|
||||
WorkflowOperationAsyncResult currentOperationInvocation;
|
||||
WorkflowInstanceContextProvider instanceContextProvider;
|
||||
bool shouldCreateNew = false;
|
||||
object thisLock = new object();
|
||||
WorkflowDefinitionContext workflowDefinition;
|
||||
WorkflowInstance workflowInstance = null;
|
||||
|
||||
public WorkflowDurableInstance(WorkflowInstanceContextProvider instanceContextProvider, Guid instanceId, WorkflowDefinitionContext workflowDefinition, bool createNew)
|
||||
:
|
||||
base(instanceContextProvider, instanceId)
|
||||
{
|
||||
if (workflowDefinition == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("workflowDefinition");
|
||||
}
|
||||
|
||||
this.workflowDefinition = workflowDefinition;
|
||||
this.shouldCreateNew = createNew;
|
||||
this.instanceContextProvider = instanceContextProvider;
|
||||
}
|
||||
|
||||
public WorkflowOperationAsyncResult CurrentOperationInvocation
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.currentOperationInvocation;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.currentOperationInvocation = value;
|
||||
}
|
||||
}
|
||||
|
||||
public WorkflowInstance GetWorkflowInstance(bool canCreateInstance)
|
||||
{
|
||||
if (this.workflowInstance == null)
|
||||
{
|
||||
lock (thisLock)
|
||||
{
|
||||
if (shouldCreateNew)
|
||||
{
|
||||
if (canCreateInstance)
|
||||
{
|
||||
this.workflowInstance = this.workflowDefinition.CreateWorkflow(this.InstanceId);
|
||||
shouldCreateNew = false;
|
||||
|
||||
if (DiagnosticUtility.ShouldTraceInformation)
|
||||
{
|
||||
string traceText = SR.GetString(SR.TraceCodeWorkflowDurableInstanceActivated, InstanceId);
|
||||
TraceUtility.TraceEvent(TraceEventType.Information,
|
||||
TraceCode.WorkflowDurableInstanceActivated, traceText,
|
||||
new StringTraceRecord("DurableInstanceDetail", traceText),
|
||||
this, null);
|
||||
}
|
||||
using (new WorkflowDispatchContext(true, true))
|
||||
{
|
||||
this.workflowInstance.Start();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Make sure we clean up this InstanceContext;
|
||||
DurableErrorHandler.CleanUpInstanceContextAtOperationCompletion();
|
||||
//Inform InstanceLifeTimeManager to clean up record for InstanceId;
|
||||
if (this.instanceContextProvider.InstanceLifeTimeManager != null)
|
||||
{
|
||||
this.instanceContextProvider.InstanceLifeTimeManager.CleanUp(this.InstanceId);
|
||||
}
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FaultException(new DurableDispatcherAddressingFault()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.workflowInstance = this.workflowDefinition.WorkflowRuntime.GetWorkflow(InstanceId);
|
||||
|
||||
if (DiagnosticUtility.ShouldTraceInformation)
|
||||
{
|
||||
string traceText = SR.GetString(SR.TraceCodeWorkflowDurableInstanceLoaded, InstanceId);
|
||||
TraceUtility.TraceEvent(TraceEventType.Information,
|
||||
TraceCode.WorkflowDurableInstanceLoaded, traceText,
|
||||
new StringTraceRecord("DurableInstanceDetail", traceText),
|
||||
this, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return workflowInstance;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,295 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
using System.Runtime;
|
||||
using System.Runtime.Diagnostics;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Workflow.Runtime;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics;
|
||||
|
||||
class WorkflowInstanceContextProvider : DurableInstanceContextProvider
|
||||
{
|
||||
bool hasCheckedForExtension;
|
||||
WorkflowInstanceLifetimeManagerExtension instanceLifeTimeManager;
|
||||
ServiceHostBase serviceHostBase;
|
||||
WaitCallback workflowActivationCompleteCallback;
|
||||
WorkflowDefinitionContext workflowDefinitionContext;
|
||||
|
||||
|
||||
public WorkflowInstanceContextProvider(ServiceHostBase serviceHostBase, bool isPerCall, WorkflowDefinitionContext workflowDefinitionContext)
|
||||
: base(serviceHostBase, isPerCall)
|
||||
{
|
||||
if (workflowDefinitionContext == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("workflowDefinitionContext");
|
||||
}
|
||||
|
||||
this.workflowDefinitionContext = workflowDefinitionContext;
|
||||
this.serviceHostBase = serviceHostBase;
|
||||
this.workflowActivationCompleteCallback = Fx.ThunkCallback(new WaitCallback(this.OnWorkflowActivationCompleted));
|
||||
}
|
||||
|
||||
public WorkflowInstanceLifetimeManagerExtension InstanceLifeTimeManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!hasCheckedForExtension)
|
||||
{
|
||||
this.instanceLifeTimeManager = this.serviceHostBase.Extensions.Find<WorkflowInstanceLifetimeManagerExtension>();
|
||||
hasCheckedForExtension = true;
|
||||
}
|
||||
return this.instanceLifeTimeManager;
|
||||
}
|
||||
}
|
||||
|
||||
public override InstanceContext GetExistingInstanceContext(Message message, IContextChannel channel)
|
||||
{
|
||||
InstanceContext instanceContext = base.GetExistingInstanceContext(message, channel);
|
||||
|
||||
if (instanceContext != null && this.InstanceLifeTimeManager != null)
|
||||
{
|
||||
WorkflowDurableInstance workflowDurableInstance = instanceContext.Extensions.Find<WorkflowDurableInstance>();
|
||||
|
||||
if (workflowDurableInstance == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||||
new InvalidOperationException(
|
||||
SR2.GetString(
|
||||
SR2.RequiredInstanceContextExtensionNotFound,
|
||||
typeof(WorkflowDurableInstance).Name)));
|
||||
}
|
||||
|
||||
this.InstanceLifeTimeManager.NotifyWorkflowActivationComplete(
|
||||
workflowDurableInstance.InstanceId,
|
||||
this.workflowActivationCompleteCallback,
|
||||
new WorkflowActivationCompletedCallbackState
|
||||
(
|
||||
workflowDurableInstance.InstanceId,
|
||||
instanceContext),
|
||||
false);
|
||||
}
|
||||
|
||||
return instanceContext;
|
||||
}
|
||||
|
||||
|
||||
public override void InitializeInstanceContext(InstanceContext instanceContext, Message message, IContextChannel channel)
|
||||
{
|
||||
base.InitializeInstanceContext(instanceContext, message, channel);
|
||||
|
||||
WorkflowDurableInstance workflowDurableInstance = instanceContext.Extensions.Find<WorkflowDurableInstance>();
|
||||
|
||||
if (workflowDurableInstance == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||||
new InvalidOperationException(
|
||||
SR2.GetString(
|
||||
SR2.RequiredInstanceContextExtensionNotFound,
|
||||
typeof(WorkflowDurableInstance).Name)));
|
||||
}
|
||||
|
||||
if (this.InstanceLifeTimeManager != null)
|
||||
{
|
||||
this.InstanceLifeTimeManager.NotifyWorkflowActivationComplete(workflowDurableInstance.InstanceId,
|
||||
this.workflowActivationCompleteCallback,
|
||||
new WorkflowActivationCompletedCallbackState
|
||||
(workflowDurableInstance.InstanceId, instanceContext),
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsIdle(InstanceContext instanceContext)
|
||||
{
|
||||
if (instanceContext == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("instanceContext");
|
||||
}
|
||||
|
||||
WorkflowDurableInstance workflowDurableInstance = instanceContext.Extensions.Find<WorkflowDurableInstance>();
|
||||
|
||||
if (workflowDurableInstance == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||||
new InvalidOperationException(
|
||||
SR2.GetString(
|
||||
SR2.RequiredInstanceContextExtensionNotFound,
|
||||
typeof(WorkflowDurableInstance).Name)));
|
||||
}
|
||||
|
||||
if (this.InstanceLifeTimeManager != null)
|
||||
{
|
||||
return (!this.InstanceLifeTimeManager.IsInstanceInMemory(workflowDurableInstance.InstanceId)) &&
|
||||
base.IsIdle(instanceContext);
|
||||
}
|
||||
return base.IsIdle(instanceContext);
|
||||
}
|
||||
|
||||
public override void NotifyIdle(InstanceContextIdleCallback callback, InstanceContext instanceContext)
|
||||
{
|
||||
WorkflowDurableInstance workflowDurableInstance = instanceContext.Extensions.Find<WorkflowDurableInstance>();
|
||||
|
||||
if (workflowDurableInstance == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||||
new InvalidOperationException(
|
||||
SR2.GetString(
|
||||
SR2.RequiredInstanceContextExtensionNotFound,
|
||||
typeof(WorkflowDurableInstance).Name)));
|
||||
}
|
||||
|
||||
if (this.InstanceLifeTimeManager != null)
|
||||
{
|
||||
if (this.InstanceLifeTimeManager.IsInstanceInMemory(workflowDurableInstance.InstanceId))
|
||||
{
|
||||
this.InstanceLifeTimeManager.NotifyWorkflowActivationComplete(workflowDurableInstance.InstanceId,
|
||||
Fx.ThunkCallback(new WaitCallback(this.OnWorkflowActivationCompleted)),
|
||||
new WorkflowActivationCompletedCallbackState
|
||||
(
|
||||
workflowDurableInstance.InstanceId,
|
||||
instanceContext,
|
||||
callback),
|
||||
true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (base.IsIdle(instanceContext))
|
||||
{
|
||||
callback(instanceContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.NotifyIdle(callback, instanceContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
base.NotifyIdle(callback, instanceContext);
|
||||
}
|
||||
}
|
||||
|
||||
protected override DurableInstance OnCreateNewInstance(Guid instanceId)
|
||||
{
|
||||
if (DiagnosticUtility.ShouldTraceInformation)
|
||||
{
|
||||
string traceText = SR2.GetString(SR2.InstanceContextProviderCreatedNewInstance, "Workflow", instanceId);
|
||||
TraceUtility.TraceEvent(TraceEventType.Information,
|
||||
TraceCode.ActivatingMessageReceived, SR.GetString(SR.TraceCodeActivatingMessageReceived),
|
||||
new StringTraceRecord("NewInstanceDetail", traceText),
|
||||
this, null);
|
||||
}
|
||||
|
||||
return new WorkflowDurableInstance(this, instanceId, this.workflowDefinitionContext, true);
|
||||
}
|
||||
|
||||
protected override DurableInstance OnGetExistingInstance(Guid instanceId)
|
||||
{
|
||||
return new WorkflowDurableInstance(this, instanceId, this.workflowDefinitionContext, false);
|
||||
}
|
||||
|
||||
void OnWorkflowActivationCompleted(object state)
|
||||
{
|
||||
WorkflowActivationCompletedCallbackState callbackState = (WorkflowActivationCompletedCallbackState) state;
|
||||
|
||||
lock (callbackState.InstanceContext.ThisLock)
|
||||
{
|
||||
if (base.Cache.Contains(callbackState.InstanceId, callbackState.InstanceContext))
|
||||
{
|
||||
WorkflowDurableInstance durableInstance = callbackState.InstanceContext.Extensions.Find<WorkflowDurableInstance>();
|
||||
if (durableInstance != null
|
||||
&& durableInstance.CurrentOperationInvocation != null
|
||||
&& durableInstance.CurrentOperationInvocation.HasWorkflowRequestContextBeenSerialized
|
||||
&& !durableInstance.CurrentOperationInvocation.IsCompleted)
|
||||
{
|
||||
// If we are here, it means the workflow instance completed, terminated, or otherwise unloaded without
|
||||
// completing the current operation invocation. In such case, we want to make the best effort to let
|
||||
// service model to consider this operation invocation failed.
|
||||
try
|
||||
{
|
||||
durableInstance.CurrentOperationInvocation.SendFault(
|
||||
WorkflowOperationErrorHandler.CreateUnhandledException(
|
||||
new InvalidOperationException(SR2.GetString(SR2.WorkflowServiceUnloadedWithoutSendingResponse))),
|
||||
null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (Fx.IsFatal(e))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IChannel[] incomingChannels = new IChannel[callbackState.InstanceContext.IncomingChannels.Count];
|
||||
callbackState.InstanceContext.IncomingChannels.CopyTo(incomingChannels, 0);
|
||||
|
||||
if (callbackState.InstanceContext.IncomingChannels.Count != 0)
|
||||
{
|
||||
foreach (IChannel channel in incomingChannels)
|
||||
{
|
||||
callbackState.InstanceContext.IncomingChannels.Remove(channel);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Call notify only when IncomingChannels Collection is empty.
|
||||
if (callbackState.InstanceContextIdleCallback != null)
|
||||
{
|
||||
callbackState.InstanceContextIdleCallback(callbackState.InstanceContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WorkflowActivationCompletedCallbackState
|
||||
{
|
||||
InstanceContext instanceContext;
|
||||
InstanceContextIdleCallback instanceContextIdleCallback;
|
||||
Guid instanceId;
|
||||
|
||||
public WorkflowActivationCompletedCallbackState(Guid instanceId, InstanceContext instanceContext)
|
||||
: this(instanceId, instanceContext, null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public WorkflowActivationCompletedCallbackState(Guid instanceId, InstanceContext instanceContext, InstanceContextIdleCallback callback)
|
||||
{
|
||||
this.instanceId = instanceId;
|
||||
this.instanceContext = instanceContext;
|
||||
this.instanceContextIdleCallback = callback;
|
||||
}
|
||||
|
||||
|
||||
public InstanceContext InstanceContext
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.instanceContext;
|
||||
}
|
||||
}
|
||||
|
||||
public InstanceContextIdleCallback InstanceContextIdleCallback
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.instanceContextIdleCallback;
|
||||
}
|
||||
}
|
||||
|
||||
public Guid InstanceId
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.instanceId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,394 @@
|
||||
//----------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime;
|
||||
using System.Runtime.Diagnostics;
|
||||
using System.ServiceModel.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Workflow.Runtime;
|
||||
using System.Workflow.Runtime.Hosting;
|
||||
using System.Diagnostics;
|
||||
|
||||
class WorkflowInstanceLifetimeManagerExtension : IExtension<ServiceHostBase>
|
||||
{
|
||||
|
||||
readonly Action<object> cachedInstanceExpirationTimerCallback;
|
||||
TimeSpan cachedInstanceExpiration;
|
||||
bool hasPersistenceService;
|
||||
Dictionary<Guid, InstanceRecord> instanceRecordMap;
|
||||
|
||||
//This class takes self contained lock, never calls out external method with lock taken.
|
||||
object lockObject;
|
||||
WorkflowRuntime workflowRuntime;
|
||||
|
||||
public WorkflowInstanceLifetimeManagerExtension(WorkflowRuntime workflowRuntime, TimeSpan cachedInstanceExpiration, bool hasPersistenceService)
|
||||
{
|
||||
this.workflowRuntime = workflowRuntime;
|
||||
this.cachedInstanceExpiration = cachedInstanceExpiration;
|
||||
this.instanceRecordMap = new Dictionary<Guid, InstanceRecord>();
|
||||
this.lockObject = new object();
|
||||
this.cachedInstanceExpirationTimerCallback = new Action<object>(this.OnTimer);
|
||||
this.hasPersistenceService = hasPersistenceService;
|
||||
|
||||
|
||||
RegisterEvents();
|
||||
}
|
||||
|
||||
//This is called when InstanceContext is taken down behind us;
|
||||
//1. UnhandledException causing InstanceContext to Abort;
|
||||
//2. Activating Request to Non-Activating operation;
|
||||
public void CleanUp(Guid instanceId)
|
||||
{
|
||||
//If no WorkflowInstance actively running for this InstanceId;
|
||||
//This will be last opportunity to cleanup their record; to avoid
|
||||
//growth of this HashTable.
|
||||
InstanceRecord instanceRecord;
|
||||
|
||||
lock (this.lockObject)
|
||||
{
|
||||
if (this.instanceRecordMap.TryGetValue(instanceId, out instanceRecord))
|
||||
{
|
||||
if (!instanceRecord.InstanceLoadedOrStarted)
|
||||
{
|
||||
if (instanceRecord.UnloadTimer != null)
|
||||
{
|
||||
instanceRecord.UnloadTimer.Cancel();
|
||||
}
|
||||
this.instanceRecordMap.Remove(instanceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IExtension<ServiceHostBase>.Attach(ServiceHostBase owner)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IExtension<ServiceHostBase>.Detach(ServiceHostBase owner)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public bool IsInstanceInMemory(Guid instanceId)
|
||||
{
|
||||
InstanceRecord instanceRecord;
|
||||
|
||||
lock (this.lockObject)
|
||||
{
|
||||
if (instanceRecordMap.TryGetValue(instanceId, out instanceRecord))
|
||||
{
|
||||
return instanceRecord.InstanceLoadedOrStarted;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//Assumption: Message arrived means; instance turned to Executing state.
|
||||
public void NotifyMessageArrived(Guid instanceId)
|
||||
{
|
||||
CancelTimer(instanceId, false);
|
||||
}
|
||||
|
||||
public void NotifyWorkflowActivationComplete(Guid instanceId, WaitCallback callback, object state, bool fireImmediatelyIfDontExist)
|
||||
{
|
||||
bool instanceFound;
|
||||
InstanceRecord instanceRecord;
|
||||
|
||||
lock (this.lockObject)
|
||||
{
|
||||
instanceFound = instanceRecordMap.TryGetValue(instanceId, out instanceRecord);
|
||||
if (instanceFound)
|
||||
{
|
||||
instanceRecord.Callback = callback;
|
||||
instanceRecord.CallbackState = state;
|
||||
}
|
||||
else if (!fireImmediatelyIfDontExist)
|
||||
{
|
||||
instanceRecord = new InstanceRecord();
|
||||
instanceRecord.Callback = callback;
|
||||
instanceRecord.CallbackState = state;
|
||||
instanceRecordMap.Add(instanceId, instanceRecord);
|
||||
}
|
||||
}
|
||||
|
||||
if (!instanceFound && fireImmediatelyIfDontExist)
|
||||
{
|
||||
//Instance is not in-memory; Notify immediately.
|
||||
callback(state);
|
||||
}
|
||||
}
|
||||
|
||||
public void ScheduleTimer(Guid instanceId)
|
||||
{
|
||||
InstanceRecord instanceRecord;
|
||||
lock (this.lockObject)
|
||||
{
|
||||
if (this.instanceRecordMap.TryGetValue(instanceId, out instanceRecord))
|
||||
{
|
||||
if (instanceRecord.UnloadTimer != null)
|
||||
{
|
||||
instanceRecord.UnloadTimer.Cancel();
|
||||
}
|
||||
else
|
||||
{
|
||||
instanceRecord.UnloadTimer = new IOThreadTimer(this.cachedInstanceExpirationTimerCallback, instanceId, true);
|
||||
}
|
||||
instanceRecord.UnloadTimer.Set(this.cachedInstanceExpiration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CancelTimer(Guid instanceId, bool markActivationCompleted)
|
||||
{
|
||||
InstanceRecord instanceRecord;
|
||||
WaitCallback callback = null;
|
||||
object callbackState = null;
|
||||
|
||||
lock (this.lockObject)
|
||||
{
|
||||
if (instanceRecordMap.TryGetValue(instanceId, out instanceRecord))
|
||||
{
|
||||
if (instanceRecord.UnloadTimer != null)
|
||||
{
|
||||
instanceRecord.UnloadTimer.Cancel();
|
||||
instanceRecord.UnloadTimer = null;
|
||||
}
|
||||
|
||||
if (markActivationCompleted)
|
||||
{
|
||||
instanceRecordMap.Remove(instanceId);
|
||||
callback = instanceRecord.Callback;
|
||||
callbackState = instanceRecord.CallbackState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (callback != null)
|
||||
{
|
||||
callback(callbackState);
|
||||
}
|
||||
}
|
||||
|
||||
void OnTimer(object state)
|
||||
{
|
||||
Guid instanceId = (Guid) state;
|
||||
|
||||
try
|
||||
{
|
||||
if (this.hasPersistenceService)
|
||||
{
|
||||
this.workflowRuntime.GetWorkflow(instanceId).TryUnload();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.workflowRuntime.GetWorkflow(instanceId).Abort();
|
||||
|
||||
if (DiagnosticUtility.ShouldTraceInformation)
|
||||
{
|
||||
string traceText = SR2.GetString(SR2.AutoAbortingInactiveInstance, instanceId);
|
||||
TraceUtility.TraceEvent(TraceEventType.Information,
|
||||
TraceCode.WorkflowDurableInstanceAborted, SR.GetString(SR.TraceCodeWorkflowDurableInstanceAborted),
|
||||
new StringTraceRecord("InstanceDetail", traceText),
|
||||
this, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (PersistenceException)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.workflowRuntime.GetWorkflow(instanceId).Abort();
|
||||
|
||||
if (DiagnosticUtility.ShouldTraceInformation)
|
||||
{
|
||||
string traceText = SR2.GetString(SR2.AutoAbortingInactiveInstance, instanceId);
|
||||
TraceUtility.TraceEvent(TraceEventType.Information,
|
||||
TraceCode.WorkflowDurableInstanceAborted, SR.GetString(SR.TraceCodeWorkflowDurableInstanceAborted),
|
||||
new StringTraceRecord("InstanceDetail", traceText),
|
||||
this, null);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (Fx.IsFatal(e))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (Fx.IsFatal(e))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnWorkflowAborted(object sender, WorkflowEventArgs args)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("args");
|
||||
}
|
||||
|
||||
CancelTimer(args.WorkflowInstance.InstanceId, true);
|
||||
}
|
||||
|
||||
void OnWorkflowCompleted(object sender, WorkflowCompletedEventArgs args)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("args");
|
||||
}
|
||||
|
||||
CancelTimer(args.WorkflowInstance.InstanceId, true);
|
||||
}
|
||||
|
||||
void OnWorkflowIdled(object sender, WorkflowEventArgs args)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("args");
|
||||
}
|
||||
ScheduleTimer(args.WorkflowInstance.InstanceId);
|
||||
}
|
||||
|
||||
void OnWorkflowLoaded(object sender, WorkflowEventArgs args)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("args");
|
||||
}
|
||||
|
||||
InstanceRecord instanceRecord;
|
||||
|
||||
lock (this.lockObject)
|
||||
{
|
||||
if (!this.instanceRecordMap.TryGetValue(args.WorkflowInstance.InstanceId, out instanceRecord))
|
||||
{
|
||||
instanceRecord = new InstanceRecord();
|
||||
this.instanceRecordMap.Add(args.WorkflowInstance.InstanceId, instanceRecord);
|
||||
}
|
||||
instanceRecord.InstanceLoadedOrStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
void OnWorkflowStarted(object sender, WorkflowEventArgs args)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("args");
|
||||
}
|
||||
|
||||
InstanceRecord instanceRecord;
|
||||
|
||||
lock (this.lockObject)
|
||||
{
|
||||
if (!this.instanceRecordMap.TryGetValue(args.WorkflowInstance.InstanceId, out instanceRecord))
|
||||
{
|
||||
instanceRecord = new InstanceRecord();
|
||||
this.instanceRecordMap.Add(args.WorkflowInstance.InstanceId, instanceRecord);
|
||||
}
|
||||
instanceRecord.InstanceLoadedOrStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
void OnWorkflowTerminated(object sender, WorkflowTerminatedEventArgs args)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("args");
|
||||
}
|
||||
|
||||
CancelTimer(args.WorkflowInstance.InstanceId, true);
|
||||
}
|
||||
|
||||
void OnWorkflowUnloaded(object sender, WorkflowEventArgs args)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("args");
|
||||
}
|
||||
|
||||
CancelTimer(args.WorkflowInstance.InstanceId, true);
|
||||
}
|
||||
|
||||
void RegisterEvents()
|
||||
{
|
||||
//Events marking the beggining of activation cycle.
|
||||
this.workflowRuntime.WorkflowLoaded += OnWorkflowLoaded;
|
||||
this.workflowRuntime.WorkflowCreated += OnWorkflowStarted;
|
||||
|
||||
//Events marking the end of activation cycle.
|
||||
this.workflowRuntime.WorkflowAborted += OnWorkflowAborted;
|
||||
this.workflowRuntime.WorkflowCompleted += OnWorkflowCompleted;
|
||||
this.workflowRuntime.WorkflowTerminated += OnWorkflowTerminated;
|
||||
this.workflowRuntime.WorkflowUnloaded += OnWorkflowUnloaded;
|
||||
|
||||
//Event which triggers the idle unload timer.
|
||||
this.workflowRuntime.WorkflowIdled += OnWorkflowIdled;
|
||||
}
|
||||
|
||||
class InstanceRecord
|
||||
{
|
||||
object callbackState;
|
||||
WaitCallback instanceActivationCompletedCallBack;
|
||||
bool loadedOrStarted = false;
|
||||
IOThreadTimer unloadTimer;
|
||||
|
||||
public WaitCallback Callback
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.instanceActivationCompletedCallBack;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.instanceActivationCompletedCallBack = value;
|
||||
}
|
||||
}
|
||||
|
||||
public object CallbackState
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.callbackState;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.callbackState = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool InstanceLoadedOrStarted
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.loadedOrStarted;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.loadedOrStarted = value;
|
||||
}
|
||||
}
|
||||
|
||||
public IOThreadTimer UnloadTimer
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.unloadTimer;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.unloadTimer = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
//----------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
class WorkflowInstanceProvider : DurableInstanceProvider
|
||||
{
|
||||
WorkflowInstanceContextProvider instanceContextProvider;
|
||||
|
||||
public WorkflowInstanceProvider(WorkflowInstanceContextProvider instanceContextProvider)
|
||||
: base(instanceContextProvider)
|
||||
{
|
||||
this.instanceContextProvider = instanceContextProvider;
|
||||
}
|
||||
|
||||
public override void ReleaseInstance(InstanceContext instanceContext, object instance)
|
||||
{
|
||||
WorkflowDurableInstance workflowDurableInstance = null;
|
||||
|
||||
//If InstanceContext is taken down due to Exception(Like PersistenceException);
|
||||
//Make sure we inform LifeTimeManager to cleanup the record.
|
||||
if (instanceContext.State == CommunicationState.Faulted || instanceContext.Aborted)
|
||||
{
|
||||
if (this.instanceContextProvider.InstanceLifeTimeManager != null)
|
||||
{
|
||||
workflowDurableInstance = (WorkflowDurableInstance) instance;
|
||||
this.instanceContextProvider.InstanceLifeTimeManager.CleanUp(workflowDurableInstance.InstanceId);
|
||||
}
|
||||
}
|
||||
base.ReleaseInstance(instanceContext, instance);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,391 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime;
|
||||
using System.Runtime.Diagnostics;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Workflow.Runtime;
|
||||
using System.Workflow.Runtime.Hosting;
|
||||
using System.Diagnostics;
|
||||
|
||||
sealed class WorkflowOperationAsyncResult : AsyncResult
|
||||
{
|
||||
static readonly object[] emptyObjectArray = new object[] { };
|
||||
|
||||
static Action<object> waitCallback = new Action<object>(WorkflowOperationAsyncResult.DoWork);
|
||||
static SendOrPostCallback sendOrPostCallback = Fx.ThunkCallback(new SendOrPostCallback(waitCallback));
|
||||
|
||||
Guid instanceIdGuid;
|
||||
string instanceIdString;
|
||||
bool isOneway;
|
||||
IDictionary<string, string> outgoingContextProperties = SerializableReadOnlyDictionary<string, string>.Empty;
|
||||
object[] outputs = emptyObjectArray;
|
||||
object returnValue;
|
||||
long time;
|
||||
|
||||
public WorkflowOperationAsyncResult(WorkflowOperationInvoker workflowOperationInvoker,
|
||||
WorkflowDurableInstance workflowDurableInstance, object[] inputs,
|
||||
AsyncCallback callback, object state, long time)
|
||||
: base(callback, state)
|
||||
{
|
||||
if (inputs == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("inputs");
|
||||
}
|
||||
|
||||
if (workflowDurableInstance == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("workflowDurableInstance");
|
||||
}
|
||||
|
||||
if (workflowOperationInvoker == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("workflowOperationInvoker");
|
||||
}
|
||||
|
||||
string queueName;
|
||||
|
||||
WorkflowRequestContext workflowRequestContext = new WorkflowRequestContext(
|
||||
this,
|
||||
inputs,
|
||||
GetContextProperties());
|
||||
|
||||
queueName = workflowOperationInvoker.StaticQueueName;
|
||||
|
||||
if (workflowRequestContext.ContextProperties.Count > 1) //DurableDispatchContextProperty.
|
||||
{
|
||||
queueName = QueueNameHelper.Create(workflowOperationInvoker.StaticQueueName, workflowRequestContext.ContextProperties);
|
||||
}
|
||||
|
||||
WorkflowInstance workflowInstance = workflowDurableInstance.GetWorkflowInstance
|
||||
(workflowOperationInvoker.CanCreateInstance);
|
||||
|
||||
AsyncCallbackState callbackState = new AsyncCallbackState(workflowRequestContext,
|
||||
workflowInstance, workflowOperationInvoker.DispatchRuntime.SynchronizationContext,
|
||||
workflowOperationInvoker.InstanceLifetimeManager, queueName);
|
||||
|
||||
this.isOneway = workflowOperationInvoker.IsOneWay;
|
||||
this.instanceIdGuid = workflowInstance.InstanceId;
|
||||
this.time = time;
|
||||
|
||||
ActionItem.Schedule(waitCallback, callbackState);
|
||||
|
||||
if (DiagnosticUtility.ShouldTraceVerbose)
|
||||
{
|
||||
string traceText = SR2.GetString(SR2.WorkflowOperationInvokerItemQueued, this.InstanceId, queueName);
|
||||
TraceUtility.TraceEvent(TraceEventType.Verbose,
|
||||
TraceCode.WorkflowOperationInvokerItemQueued, SR.GetString(SR.TraceCodeWorkflowOperationInvokerItemQueued),
|
||||
new StringTraceRecord("ItemDetails", traceText),
|
||||
this, null);
|
||||
}
|
||||
}
|
||||
|
||||
public long BeginTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.time;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasWorkflowRequestContextBeenSerialized
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal string InstanceId
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.instanceIdString == null)
|
||||
{
|
||||
Fx.Assert(!this.instanceIdGuid.Equals(Guid.Empty), "WorkflowOperationInvokerAsyncResut.instanceIdGuid != Guid.Empty");
|
||||
this.instanceIdString = this.instanceIdGuid.ToString();
|
||||
}
|
||||
|
||||
return this.instanceIdString;
|
||||
}
|
||||
}
|
||||
|
||||
public static object End(WorkflowOperationAsyncResult result, out object[] outputs)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
AsyncResult.End<WorkflowOperationAsyncResult>(result);
|
||||
}
|
||||
finally
|
||||
{
|
||||
//Application Fault's should carry Context Properties
|
||||
result.PromoteContextProperties();
|
||||
}
|
||||
|
||||
outputs = result.outputs;
|
||||
return result.returnValue;
|
||||
}
|
||||
|
||||
public void SendFault(Exception exception, IDictionary<string, string> contextProperties)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("exception");
|
||||
}
|
||||
|
||||
if (!IsCompleted)
|
||||
{
|
||||
this.outgoingContextProperties = (contextProperties != null) ? new ContextDictionary(contextProperties) : null;
|
||||
}
|
||||
base.Complete(false, exception);
|
||||
}
|
||||
|
||||
public void SendResponse(object returnValue, object[] outputs, IDictionary<string, string> contextProperties)
|
||||
{
|
||||
if (outputs == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("outputs");
|
||||
}
|
||||
|
||||
if (!IsCompleted)
|
||||
{
|
||||
this.returnValue = returnValue;
|
||||
this.outputs = outputs;
|
||||
this.outgoingContextProperties = (contextProperties != null) ? new ContextDictionary(contextProperties) : null;
|
||||
}
|
||||
base.Complete(false);
|
||||
}
|
||||
|
||||
//No-op for two-ways.
|
||||
internal void MarkOneWayOperationCompleted()
|
||||
{
|
||||
if (this.isOneway && !this.IsCompleted)
|
||||
{
|
||||
base.Complete(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void DoWork(object state)
|
||||
{
|
||||
if (state == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("state");
|
||||
}
|
||||
|
||||
AsyncCallbackState callbackState = (AsyncCallbackState) state;
|
||||
|
||||
bool executingWork = false;
|
||||
try
|
||||
{
|
||||
//If SyncContext is enabled
|
||||
//We have to do another post to get to correct thread.
|
||||
if (callbackState.SynchronizationContext != null)
|
||||
{
|
||||
SynchronizationContext synchronizationContext = callbackState.SynchronizationContext;
|
||||
callbackState.SynchronizationContext = null;
|
||||
|
||||
SynchronizationContextWorkflowSchedulerService.SynchronizationContextPostHelper.Post(
|
||||
synchronizationContext,
|
||||
WorkflowOperationAsyncResult.sendOrPostCallback,
|
||||
callbackState);
|
||||
}
|
||||
else //We are in correct thread to do the work.
|
||||
{
|
||||
using (new WorkflowDispatchContext(true))
|
||||
{
|
||||
callbackState.WorkflowRequestContext.SetOperationBegin();
|
||||
executingWork = true;
|
||||
|
||||
if (callbackState.WorkflowInstanceLifeTimeManager != null)
|
||||
{
|
||||
callbackState.WorkflowInstanceLifeTimeManager.NotifyMessageArrived(callbackState.WorkflowInstance.InstanceId);
|
||||
}
|
||||
|
||||
callbackState.WorkflowInstance.EnqueueItemOnIdle(
|
||||
callbackState.QueueName,
|
||||
callbackState.WorkflowRequestContext,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (QueueException e)
|
||||
{
|
||||
WorkflowOperationFault operationFault = new WorkflowOperationFault(e.ErrorCode);
|
||||
|
||||
try
|
||||
{
|
||||
if (callbackState.WorkflowInstanceLifeTimeManager != null)
|
||||
{
|
||||
callbackState.WorkflowInstanceLifeTimeManager.ScheduleTimer(callbackState.WorkflowInstance.InstanceId);
|
||||
}
|
||||
callbackState.WorkflowRequestContext.SendFault(new FaultException(operationFault), null);
|
||||
}
|
||||
catch (Exception unhandled)
|
||||
{
|
||||
if (Fx.IsFatal(unhandled))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
// ignore exception; we made best effort to propagate the exception back to the invoker thread
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (Fx.IsFatal(e))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (callbackState.WorkflowInstanceLifeTimeManager != null)
|
||||
{
|
||||
callbackState.WorkflowInstanceLifeTimeManager.ScheduleTimer(callbackState.WorkflowInstance.InstanceId);
|
||||
}
|
||||
//We should field only user code exception; Everything else should go abort path.
|
||||
callbackState.WorkflowRequestContext.GetAsyncResult().SendFault(e, null);
|
||||
}
|
||||
catch (Exception e1)
|
||||
{
|
||||
if (Fx.IsFatal(e1))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
// ignore exception; we made best effort to propagate the exception back to the invoker thread
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
if (executingWork)
|
||||
{
|
||||
callbackState.WorkflowRequestContext.SetOperationCompleted();
|
||||
}
|
||||
}
|
||||
catch (Exception e1)
|
||||
{
|
||||
if (Fx.IsFatal(e1))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
// ignore exception; we made best effort to propagate the exception back to the invoker thread }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IDictionary<string, string> GetContextProperties()
|
||||
{
|
||||
Fx.Assert(OperationContext.Current != null, "Called from non service thread");
|
||||
|
||||
ContextMessageProperty incomingContextProperties = null;
|
||||
if (OperationContext.Current.IncomingMessageProperties != null
|
||||
&& ContextMessageProperty.TryGet(OperationContext.Current.IncomingMessageProperties, out incomingContextProperties))
|
||||
{
|
||||
return incomingContextProperties.Context;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SerializableReadOnlyDictionary<string, string>.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
void PromoteContextProperties()
|
||||
{
|
||||
Fx.Assert(OperationContext.Current != null, "Called from non service thread");
|
||||
|
||||
if (outgoingContextProperties != null)
|
||||
{
|
||||
ContextMessageProperty context;
|
||||
if (!ContextMessageProperty.TryGet(OperationContext.Current.OutgoingMessageProperties, out context))
|
||||
{
|
||||
new ContextMessageProperty(this.outgoingContextProperties).AddOrReplaceInMessageProperties(OperationContext.Current.OutgoingMessageProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (KeyValuePair<string, string> contextElement in this.outgoingContextProperties)
|
||||
{
|
||||
context.Context[contextElement.Key] = contextElement.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AsyncCallbackState
|
||||
{
|
||||
WorkflowInstanceLifetimeManagerExtension instanceLifeTimeManager;
|
||||
IComparable queueName;
|
||||
SynchronizationContext synchronizationContext;
|
||||
WorkflowInstance workflowInstance;
|
||||
WorkflowRequestContext workflowRequestContext;
|
||||
|
||||
public AsyncCallbackState(
|
||||
WorkflowRequestContext workflowRequestContext,
|
||||
WorkflowInstance workflowInstance,
|
||||
SynchronizationContext synchronizationContext,
|
||||
WorkflowInstanceLifetimeManagerExtension instanceLifeTimeManager,
|
||||
IComparable queueName)
|
||||
{
|
||||
this.workflowInstance = workflowInstance;
|
||||
this.workflowRequestContext = workflowRequestContext;
|
||||
this.synchronizationContext = synchronizationContext;
|
||||
this.queueName = queueName;
|
||||
this.instanceLifeTimeManager = instanceLifeTimeManager;
|
||||
}
|
||||
|
||||
public IComparable QueueName
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.queueName;
|
||||
}
|
||||
}
|
||||
|
||||
public SynchronizationContext SynchronizationContext
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.synchronizationContext;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.synchronizationContext = value;
|
||||
}
|
||||
}
|
||||
|
||||
public WorkflowInstance WorkflowInstance
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.workflowInstance;
|
||||
}
|
||||
}
|
||||
|
||||
public WorkflowInstanceLifetimeManagerExtension WorkflowInstanceLifeTimeManager
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.instanceLifeTimeManager;
|
||||
}
|
||||
}
|
||||
|
||||
public WorkflowRequestContext WorkflowRequestContext
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.workflowRequestContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
//----------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Dispatcher
|
||||
{
|
||||
using System.ServiceModel.Channels;
|
||||
|
||||
class WorkflowOperationErrorHandler : DurableErrorHandler
|
||||
{
|
||||
public WorkflowOperationErrorHandler(bool includeDebugInfo)
|
||||
: base(includeDebugInfo)
|
||||
{
|
||||
}
|
||||
|
||||
public static Exception CreateUnhandledException(Exception innerException)
|
||||
{
|
||||
if (innerException == null)
|
||||
{
|
||||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("innerException");
|
||||
}
|
||||
return new WorkflowOperationUnhandledException(innerException);
|
||||
}
|
||||
|
||||
protected override Exception GetExceptionToTrace(Exception error)
|
||||
{
|
||||
return error.InnerException;
|
||||
}
|
||||
|
||||
protected override bool IsUserCodeException(Exception error)
|
||||
{
|
||||
return error is WorkflowOperationUnhandledException;
|
||||
}
|
||||
|
||||
class WorkflowOperationUnhandledException : Exception
|
||||
{
|
||||
public WorkflowOperationUnhandledException(Exception innerException)
|
||||
: base(SR2.GetString(SR2.WorkflowOperationUnhandledException), innerException)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user