Josh Lane on .NET RSS 2.0
 Monday, October 01, 2007

ASP.NET as WF host is an important and useful scenario for WF adoption, but there are some interesting details that are worth understanding before you tackle this yourself.

The first thing to understand is that WF itself imposes no specific threading model; rather, it must be configured to conform to any specific threading requirements of its host process.  This means that WF can be hosted in a single-threaded console or WinForms app, and also can scale up to be hosted inside ASP.NET, with all of its multithreaded goodness (thankfully hidden under the covers from the average web developer).

But there's the rub... ASP.NET uses the .NET ThreadPool to dispatch request processing.  The default WF scheduler also uses the ThreadPool to execute individual workflows; in combination this would be bad, as it results in 2 ThreadPool threads consumed per web request.  What we need is a way to re-use the ASP.NET thread to also execute the workflow.

Enter ManualWorkflowSchedulerService.  As opposed to the DefaultWorkflowSchedulerService, the manual service grants explicit control over which thread is used to execute a given workflow.  Its usage is a bit goofy... there's an extra call to ManualWorkflowSchedulerService.StartWorkflow() that comes after calling Start() on the workflow object itself.  But it is the thread on which StartWorkflow() is invoked that executes the workflow... so, a necessary evil.

Okay, all of this is fine, and frankly already well documented.  The purpose of this post is to highlight a few subtleties.

When you create an instance of ManualWorkflowSchedulerService, you must specify whether you're using active timers, or not.  The idea here is that, for workflows that might go idle (as a result of DelayActivity, or any other activity that implements IEventActivity), the manual scheduler itself cannot know when to resume such workflows (as the default scheduler can do).  So you have two choices... choosing "active timers" means a separate thread will spin up, periodically check for expired activity wait timers, and resume workflows with expired timers.  Choosing "no active timers" means you must write extra code to check for expired timers, and manually resume them when necessary.

The primary issue with active timers is that, since two threads are now involved, it's not very difficult to create a race condition where your primary ASP.NET processing thread completes before the active timer thread wakes up your idled workflow and allows it to complete its work.  The resulting problems are implementation-specific, but not good regardless.

The way to solve this problem is naturally to use some sort of thread synchronization mechanism to ensure that the main ASP.NET thread waits on the active timer thread to restart your idled workflow.  See the sample code referenced at the end of this post for an illustration of the issue, and one potential solution using thread events.

An important observation here is that ManualWorkflowSchedulerService, used in this way, implies synchronous execution of workflows.  This is counter to the relative "fire-and-forget"-ness of the default scheduler.

On the other hand, if you don't use active timers with the manual scheduler, it is *your* responsibility to detect expired workflow timers, and to then resume the idled workflows (by calling WorkflowInstance.Resume() ).  This generally means spinning up your own secondary thread (might as well just use an active timer) or periodic polling from your main thread (ick).  Again, see the sample code for an illustration of the issue and one possible resolution.

A final note... given the synchronous nature of ASP.NET request processing (setting aside async ASP.NET handlers for the moment) I can't generally recommend the use of async activities in workflows used inside ASP.NET.  By all means, use WF to define processing logic for HTTP requests inside ASP.NET.  Just tread lightly with the use of DelayActivity, HandleExternalEventActivity, etc. inside such workflows.

Here's the sample code I refer to above, that illustrates what I'm describing here (requires VS.NET 2008 beta 2).

Enjoy!

Monday, October 01, 2007 1:07:42 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -
ASP.NET | WF
Disclaimer

Don't blame my employer(s)... all of this is my fault.

© Copyright 2009 Josh Lane
Sign In
Locations of visitors to this page
DasBlog theme 'Business' created by Christoph De Baene (delarou)