Dealing with ExecutionEngineException, code contracts and WF

I recently battled the scary ExecutionEngineException. It’s scary because the exception itself does not provide any information about what has gone wrong and why. Figuring out why the exception is being thrown can get a little tricky. In my scenario, [VS] was crashing when trying to display a WF designer. After attaching another [VS] instance to debug the crash, I found that the exception being thrown in the designer was ExecutionEngineException. The place it was being thrown was in an evaluati... [更多...]

Beware of lifetime manager policy locks in Unity

I have created a caching dependency injection slice in order to squeeze more performance from the DAL in a workflow service. What I found was that the service always hit a timeout when the caching slice was put into the Unity configuration. I spent half a day working with AppFabric monitoring, event logs and all the information I could get out of diagnostic tracing for WCF, WF, WIF and custom sources. After not being able to get any answers along with futile debugging efforts, I realised that I ... [更多...]

TFS and WF4: The diff noise problem

For a long time the most popular post I have on this site is about how to configure [VS] to use WinMerge as the merge/diff tool for TFS rather than using the feature poor out of the box software. Sometimes the nature of the files under development result in version differences that have a lot of noise regardless of the diff/merge tool that you use. Unfortunately WF is one of the common offenders. I absolutely love WF, but am disappointed that designer state information is persisted with the work... [更多...]

BusinessFailureScope activity with deep nested support

I wrote a series of posts late last year about a custom WF activity that collates a set of business failures that child evaluator activities identify into a single exception. At the time, the only way I could get the child evaluator activities to communicate failures to the parent scope activity was by using a custom WF extension to store the failures and manage the exception throwing logic. The relationship between the parent scope activity and child evaluator activity works like this. The ... [更多...]

WF content correlation and security

I have posted previously about using content correlation in WF services to implement a service session. One issue that must be highlighted regarding content correlation is about the security of the session in relation to hijack attacks. I am writing a workflow service that is a combination of IIS, WCF, WF, WIF and AppFabric. WIF is used to secure the WCF service to ensure that only authenticated users can hit the endpoint. WIF then handles claim demands raised depending on the actions taken wit... [更多...]

Calling a workflow service operation multiple times

Ron Jacobs has just answered an interesting question over on his blog. The question is about whether a workflow service operation can be invoked multiple times. Ron does not provide the details of the question but the example he provides implies that the implementation of the two invocations of the same service operation may be different as the same operation name is implemented twice in the workflow. This seems like a design issue as far as the service goes but the question itself is still inte... [更多...]

Custom DisposalScope activity

The previous post outlined the issues with working with unmanaged and IDisposable resources in WF. To recap, the issues with these resources in WF are: Persistence Restoration after persistence Error handling Messy WF experience to handle this correctly A custom activity can handle these issues in a much more elegant way than the partially successful implementation in the previous post. The design goals of this activity are: take in a resource of a generi... [更多...]

Working with unmanaged and IDisposable resources in WF

Generally speaking you will want to steer clear of unmanaged or IDisposable resources when authoring WF workflows. The primary reasons for this are: Persistence Restoration after persistence Error handling A workflow may be persisted at any time (with the exception of a No Persist Zone). There are two problems for unmanaged or IDisposable resources with regard to persistence. Firstly, the resource may not be released when the workflow is persisted. Secondly the state of the resource when th... [更多...]

Custom IfThen activity

The two most common WF activities to use when implementing decision branching are the If activity and the Flowchart activity. Using a Flowchart is overkill for a simple/singular decision branch so the If is often chosen. Unfortunately the If activity that comes with WF4 forces an Else branch to be visible on the designer even if you are not using it. This can make workflows unnecessarily cluttered. The solution is to author a custom activity that only executes an If-Then branch rather than an... [更多...]

WF Retry activity

One of the tools missing out of the WF toolbox is the ability to run some retry logic. Applications often have known scenarios where something can go wrong such that a retry of the last action could get a successful outcome. One such example is a connection timeout to a database. You may want to try a couple of times before throwing the exception in order to get more success over time. The specific scenario I am addressing is a little different. I have created some custom MSF providers that wil... [更多...]
Rory Primrose | Disable Trace UseGlobalLock For Better Tracing Performance

Disable Trace UseGlobalLock For Better Tracing Performance

I have had a performance bottleneck in a load test that I have been running. The load test run against some tracing components which ultimately invoke TraceSource and TraceListener methods. I have been wondering why performance drops through the floor as more and more users come online and the call count increases. I have used Reflector a lot of times to review the implementation of TraceSource and TraceListener to get a feel for what they do. I remembered that global locking may be a problem.

The methods on TraceSource check TraceInternal.UseGlobalLock (also referenced by Trace.UseGlobalLock) which is determined by the system.diagnostics/trace/useGlobalLock configuration value:

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 

  <system.diagnostics> 

    <trace useGlobalLock="false" /> 

    <sources> 
      <source name="MySource" 
              switchValue="All"> 
        <listeners> 
          <add name="ListenerName" 
              type="MyApplication.LoadTests.LoadTestTraceListener, MyApplication.LoadTests" /> 
        </listeners> 
      </source> 
    </sources>    
  </system.diagnostics> 
</configuration> 

TraceSource checks whether a global lock should be used when a trace message is written.

If global locking is enabled (the default value), then a lock (TraceInternal.critSec) call is made before looping through each listener defined for the source to write the message to the listener. With multiple threads invoking trace methods, each invocation is blocking all the other threads while it is writing its trace message to the collection of trace listeners. This is regardless of where each trace invocation occurs in the application code base.

If global locking is disabled, TraceSource loops through the trace listeners without a lock. It then checks each listener for whether it is thread safe (TraceListener.IsThreadSafe, false by default). If the listener is thread safe, the message is written directly to the listener. Otherwise, a lock is obtained on the listener itself before writing the message. If the application is configured to a single source and listener, then this will actually have the same affect as a global lock. However, if multiple sources and listeners are used, locking on each listener has the advantage that tracing a message in one part of the application will not lock up other threads that write trace messages in another part of the application using a different listener.

Ideally you would use trace listeners that are thread safe but most of the ones out of the box are not. System.Diagnostics.EventSchemaTraceListener and System.Diagnostics.EventProviderTraceListener are the only two provided with the .Net framework that are thread safe.

I find that System.Diagnostics.XmlWriterTraceListener is the best listener for my purposes so this limits the performance that I can get out of tracing. However, I tend to use a unique source and listener per assembly in a solution. While this improves locking performance encountered, this also makes for a good segregation of tracing data that can be merged together if required using SvcTraceView.exe.

On a side note, I created a custom listener to use in my load test because I wanted to be able to test the performance of my tracing components while minimizing the performance "noise" of the .Net framework part of the tracing execution. The listener looks something like this:

using System; 
using System.Diagnostics; 

namespace MyApplication.LoadTests 
{ 
    /// <summary> 
    /// The <see cref="LoadTestTraceListener"/> 
    /// class is used to help run load tests against the tracing components. 
    /// </summary> 
    public class LoadTestTraceListener : TraceListener 
    { 
        /// <summary> 
        /// Writes the provided message to the listener you create in the derived class. 
        /// </summary> 
        /// <param name="message">A message to write.</param> 
        public override void Write(String message) 
        { 
            Debug.WriteLine(message); 
        } 

        /// <summary> 
        /// Writes a message to the listener you create in the derived class, followed by a line terminator. 
        /// </summary> 
        /// <param name="message">A message to write.</param> 
        public override void WriteLine(String message) 
        { 
            Debug.WriteLine(message); 
        }

        /// <summary> 
        /// Gets a value indicating whether the trace listener is thread safe. 
        /// </summary> 
        /// <value></value> 
        /// <returns>true if the trace listener is thread safe; otherwise, false. The default is false. 
        /// </returns> 
        public override Boolean IsThreadSafe 
        { 
            get 
            { 
                return true; 
            } 
        } 
    } 
} 

This listener will output the trace messages in debug mode, but the compiler will not include the debug statements under a release build. It also indicates that the listener is thread safe to improve performance of TraceSource implementations that use this listener when global locking is disabled.

blog comments powered by Disqus