2009-10-22

Windows Services and the Network

Let's make this very clear: a service should not use or change drive mappings at all. See KB180362 (INFO: Services and Redirected Drives) (webcite) and Services and Redirected Drives (MSDN) (webcite) for more information. If a service needs to use network resources, it should use UNC paths.

Network drive mappings are handled differently on different Windows versions. In addition, network drive mappings are one type of an "MS-DOS Device Name", so they fall under the additional complications described in Local and Global MS-DOS Device Names (webcite).

Note that a service running as LocalService (webcite) uses anonymous credentials to access network resources, and services running as NetworkService (webcite) or LocalSystem (webcite) use machine account credentials.

2009-10-21

Managed Windows Services - The Basics

Managed (.NET) Windows Services suffer from a lack of sufficient information in the .NET MSDN documentation. Earlier this year, the BCL team put a post on their blog that fills in the gaps: How .NET Managed Services Interact with the Service Control Manager. The Service Control Manager (SCM) is the part of Windows that controls starting and stopping Windows Services.

Services and the .NET ServiceBase Class

In a nutshell, the static ServiceBase.Run method provides a main loop for services, giving the service's main thread to the SCM. Once control has been passed off, ServiceBase will invoke the service entry points such as ServiceBase.OnStart and ServiceBase.OnStop as a response to SCM requests.

Properly Implementing ServiceBase.OnStart and ServiceBase.OnStop

The service enters the "starting" state before ServiceBase.OnStart is called, and only enters the "started" state when OnStart returns. So, a service that is always "starting" and never "started" is a pretty good indication that OnStart isn't returning.

OnStart cannot be a "main loop" for a service. Many services work just fine without a main loop, but if one is required, then OnStart should start a thread and then return, letting the thread run the actual main loop. If OnStart will take more than 30 seconds to return, then it should call ServiceBase.RequestAdditionalTime.

Similarly, the service enters the "stopping" state before ServiceBase.OnStop is called, and enters the "stopped" state when OnStop returns. If OnStop will take more than 20 seconds, then it should call ServiceBase.RequestAdditionalTime.

The Current Directory

Services do not start with their current directory set to where their executable is. They usually end up running with their current directory set to the Windows or Windows System folder. It's not unusual for Windows Services to set their current directory near the beginning of their Main method, before calling ServiceBase.Run:

Environment.CurrentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

Services and Threading

Deep within the bowels of the OS, Windows Services are treated as a special sort of Console application. A Console application has a single thread by default and exits when that thread returns from Main; a Windows Service starts as a Console application and then passes ownership of its thread to the SCM by calling ServiceBase.Run. When the SCM decides to exit the service process (after all its services have been stopped), it will return control back to Main, which is expected to immediately exit.

The ServiceBase events (such as OnStart and OnStop) execute within the context of a worker thread. Therefore, the default synchronization context for .NET services is unsynchronized (e.g., SynchronizationContext.Current is null). Windows Services usually employ one of two threading models:

  1. Create a "main loop" thread within OnStart, and have this thread respond to events (including the OnStop event).
  2. Start at least one asynchronous operation (such as a Timer, listening socket, or FileSystemWatcher), and have the completion handlers take the appropriate actions.

Note that both of these models return from OnStart after a short period of time (either starting the main thread or starting an asynchronous operation).

A reminder about garbage collection is in order: if the only reference to an object is in a completion routine, then that object is eligible for garbage collection. This is true for any type of .NET process, but most often causes problems with services that choose to use the second threading model described above.

Even if a service uses a "main loop" thread, the default SynchronizationContext is still in effect, resulting in free-threaded completion routines even for EBAP components (EBAP: Event-Based Asynchronous Pattern). This means that EBAP components such as BackgroundWorker may not perform as expected. The Nito.Async library contains an ActionThread that is ideal for the "main loop" thread of a Windows Service; see the Nito.Async documentation for details and examples.

2009-10-20

SynchronizationContext Properties Summary

A few of my posts recently have dealt with surprises that I've found when interacting with different implementations of SynchronizationContext. This post is a summary of my findings.

SynchronizationContext Implementation Properties
Specific Associated Thread Synchronized Execution Sequential Execution Reentrant Send Reentrant Post Supports Equality Comparision
Windows Forms Yes Yes Yes Sometimes [1] Never Yes
Windows Presentation Foundation and Silverlight Yes Yes Yes Sometimes [1] Never No [3]
Nito Yes Yes Yes Never [2] Never No [4]
Default No No No Always Never N/A [5]
ASP.NET No Yes No Always Always [6] N/A [5]

Notes

  1. Send is reentrant when invoked from the same GUI thread; it is not reentrant when invoked from other threads.
  2. Invoking Send from the thread associated with a Nito.Async.ActionDispatcherSynchronizationContext is not allowed.
  3. As of .NET 3.5 SP1, WPF will create a separate DispatcherSynchronizationContext for each window, even if both windows are on the same thread.
  4. It is possible to create two different Nito.Async.ActionDispatcherSynchronizationContext instances that refer to the same underlying Nito.Async.ActionDispatcher.
  5. The "Supports Equality Comparision" property is meaningless because this SynchronizationContext type does not have a Specific Associated Thread.
  6. The rationale behind this surprising reentrancy is that the AspNetSynchronizationContext is not associated with any threads at all, so it borrows the thread from its caller.

SynchronizationContext Implementations

The "Windows Forms" entry refers to the System.Windows.Forms.WindowsFormsSynchronizationContext, which is used by the GUI thread(s) in Windows Forms applications. Other threads in the same application may use different SynchronizationContext implementations.

The "Windows Presentation Foundation and Silverlight" entry refers to the System.Windows.Threading.DispatcherSynchronizationContext, which is used by the GUI thread(s) in Windows Presentation Foundation and Silverlight applications. Other threads in the same application may use different SynchronizationContext implementations.

The "Nito" entry refers to the Nito.Async.ActionDispatcherSynchronizationContext from the Nito.Async library. This includes Nito.Async.ActionThread threads.

The "Default" entry refers the default implementation of System.Threading.SynchronizationContext. This includes ThreadPool and Thread class threads, Windows Services, and Console applications, unless that thread replaces the default with a different SynchronizationContext.

The "ASP.NET" entry refers to the System.Web.dll:System.Web.AspNetSynchronizationContext, which is used by threads running in an application hosted by the ASP.NET runtime.

SynchronizationContext Properties

The "Specific Associated Thread" property means that the SynchronizationContext refers to a single, specific thread, and that queueing work to the SynchronizationContext will queue work to that thread. Note that multiple SynchronizationContexts may still refer to the same thread, even if this property is true.

The "Synchronized Execution" property means that all work queued to the SynchronizationContext will execute one at a time.

The "Sequential Execution" property means that all work queued to the SynchronizationContext will execute in order. If a SynchronizationContext supports Sequential Execution, then it also supports Synchronized Execution.

The "Reentrant Send" property means that the implementation of SynchronizationContext.Send will directly invoke its delegate on the current thread.

The "Reentrant Post" property means that the implementation of SynchronizationContext.Post will directly invoke its delegate on the current thread.

The "Supports Equality Comparision" property means that instances of that SynchronizationContext type may be compared for equality, and that equality implies that they refer to the same Specific Associated Thread.

Further Reading and a Useful Library

For more details, see the previous SynchronizationContext-related posts Gotchas from SynchronizationContext! and Another SynchronizationContext Gotcha: InvokeRequired?

The Nito.Async library contains a Nito.Async.SynchronizationContextRegister that can be used by a program to query properties of a SynchronizationContext implementation (except the "Supports Equality Comparision" property). This is useful when writing .NET multi-host-compatible asynchronous components.

2009-10-07

DynamicExecute Now Official

Version 3.5.4.0 of the MSBuild Extension Pack has recently been released!

This release of the project includes my DynamicExecute class (online docs), which allows build masters to include C# code right in their MSBuild scripts. This code is compiled and executed at build time, which provides a whole new level of MSBuild extensibility.

If you've ever found yourself frustrated by the lack of MSBuild usability, this should come as a real relief. :)

2009-10-01

Threading in the Business Layer

I don't have a great deal to say on the matter. This is just something that I've observed when working on a few different projects.

When writing end-user applications, I find my business layer tends to be stateful and affinitized to the UI thread. This just makes it easier, particularly when doing MVVM.

However, when writing web services, I find my business layer tends to be stateless and free-threaded. Well, sometimes they're not stateless, but they're nearly always free-threaded. This makes it easier (and more scalable) when dealing with web requests.

I don't know if this is good or bad. Lhotka, for example, has a very different approach to business objects. I really enjoyed reading his book (especially the first few chapters which deal with enterprise-level design in general), but I'm still not sold on his idea of literally moving business objects from one process to another. Maybe I'm just stuck in the 1980's...

Then again, I've never had to deal with a truly distributed application. Lhotka's examples are really based on a single application that needs to be distributed for performance or reliability reasons. When I work with GUI apps and web services, I always end up treating them as different applications. I think that's where the differences in our BO models come from.

Like I said, I don't know if my approach is best or not. This was just an observation. If you do have an opinion, feel free to let me know!