Question

I have a class that exposes a fluent interface style that I also want to be thread safe.

At the moment, calling chainable methods on an instance of the class sets up various collections with operations (Func<T>'s).

When the result is requested the real work happens. This allows for users to chain the method calls in any order such that:

var result = myFluentThing
.Execute(() => serviceCall.ExecHttp(), 5) 
.IfExecFails(() => DoSomeShizzle())
.Result<TheResultType>();

(Here, 5 is the number of times to re-try a failied service call.)

Obviously this isn't threadsafe or reentrant.

What are some common design patterns that solve this problem?

If the Execute method had to be called first I could simply returned a new instance of the class to work with each time however since any method can be called at any point in the chain, how would you solve this problem?

I'm more interested in understanding various ways to solve this rather than a single answer just to "get it working right".

I've put the full code on GitHub incase anyone needs a wider context on what I'm aiming to achieve: https://github.com/JamieDixon/ServiceManager

Was it helpful?

Solution

We can split fluent approaches into two types; mutating and non-mutating.

Mutating cases aren't very common in .NET (the fluent apporach wasn't generally until Linq introduced it's very heavy use of the fluent approach, Java in comparison uses them heavily in property setters where C# instead uses properties to give the same syntax for setting a property as setting a field). One example though is StringBuilder.

StringBuilder sb = new StringBuilder("a").Append("b").Append("c");

The basic form is:

TypeOfContainingClass SomeMethod(/*... arguments ... */)
{
  //Do something, generally mutating the current object
  //though we could perhaps mix in some non-mutating methods
  //with the mutating methods a class like this uses, for
  //consistency.
  return this;
}

This is an inherently non-threadsafe approach, because it mutates the object in question, so two calls from different threads will interfere. It's certainly possible to create a class that will be thread-safe in the face of such calls in the sense that it won't be put into an incoherent state, but generally when we take this approach we care about the results of those mutations, and those alone. E.g. with the StringbBuilder example above, we care that sb ends up holding the string "abc", a thread-safe StringBuilder would be pointless, because we wouldn't consider a guarantee that it would successfully end up either holding "abc" or "acb" to be acceptable - such a hypothetical class itself would be threadsafe, but the calling code would not.

(This does not mean that we can't use such classes in thread-safe code; we can use any classes in thread-safe code, but it doesn't help us).

Now, the non-mutating form is thread-safe, in and of itself. This doesn't mean all uses are thread-safe, but it means they can be. Consider the following LINQ code:

var results = someSource
  .Where(somePredicate)
  .OrderBy(someOrderer)
  .Select(someFactory);

This is thread-safe as long as:

  1. Iterating through someSource is thread-safe.
  2. Calling somePredicate is thread-safe.
  3. Calling someOrder is thread-safe.
  4. Calling someFactory is thread-safe.

This may seem like a lot of of criteria, but actually, the last are all the same criteria: We require our Func instances to be functional - they don't have side-effects*, but rather they return a result that depends upon their input (we can bend some rules about being functional while still being thread-safe, but let's not complicate things right now). And well, that's presumably the sort of case they were thinking of when they came up with the name Func. Note that the most common sort of case with Linq fits this description. E.g.:

var results = someSource
  .Where(item => item.IsActive)//functional. Thread-safe as long as accessing IsActive is.
  .OrderBy(item => item.Priority)//functional. Thread-safe as long as accessing Priority is.
  .Select(item => new {item.ID, item.Name});//functional. Thread-safe as long as accessing ID and Name is.

Now, with 99% of property implementations, calling the getters from multiple threads is thread-safe as long as we don't have another thread writing. This is a common scenario, so we're thread-safe in terms of being able to cater for that case safely, though we are not thread-safe in the face of another thread doing such mutations.

Similarly, we can split sources like someSource into four categories:

  1. A collection in memory.
  2. A call against a database or other source of data.
  3. An enumerable that will do a single pass through information obtained from somewhere, but where the source does not have the information needed to retrieve that information again on a second iteration.
  4. Other.

The vast majority of the first case is thread-safe in the face solely of other readers. Some are thread-safe in the face of concurrent writers too. With the second case it depends on the implementation - does it obtain a connection etc. as needed in the current thread, or use one shared between calls? With the third case, it's definitely not thread-safe unless we consider "losing" those items that another thread got instead of us to be acceptable. And well, "Other" is as "Other" does.

So, from all that, we don't have something that guarantees thread-safety, but we do have something that gives us a sufficient degree of thread-safety that if used with other components providing the degree of thread-safety we need, we get it.

100% thread-safety in the face of all possible uses? No, nothing gives you that. Really, no datatype is thread-safe, only particular groups of operations - in describing a datatype as "thread-safe" we are saying all it's member methods and properties are treadsafe, and in turn in describing a method or property as threadsafe we are saying that it in itself is threadsafe and therefore can be part of a threadsafe group of operations, but not every group of thread-safe operations is thread-safe.

If we want to implement this kind of approach we need to create a method or extension that creates an object, based on the object called on (if a member rather than an extension) and the parameters, but does not mutate.

Let's have two separate implementations of a method like Enumerable.Select for discussion:

public static IEnumerable<TResult> SelectRightNow<TSource, TResult>(
  this IEnumerable<TSource> source,
  Func<TSource, TResult> selector)
  {
    var list = new List<TResult>();
    foreach(TSource item in source)
      list.Add(selector(item));
    return list;
  }

public static IEnumerable<TResult> SelectEventually<TSource, TResult>(
  this IEnumerable<TSource> source,
  Func<TSource, TResult> selector)
  {
    foreach(TSource item in source)
      yield return selector(item);
  }

In both cases, the method immediately returns a new object that is in some way based on the contents of source. Only the second though has the sort of delayed iteration of source that we get from linq. The first actually lets us deal with some multi-threading situations better than the second, but does so nastily (if you want to e.g. get a copy while holding a lock as part of your concurrency management, do it by getting a copy while holding a lock, not in the midst of anything else).

In either case, it is the object returned that is the key to what thread-safety we can offer. The first has obtained all information about its results, so as long as it is only referenced locally to a single thread it's thread-safe. The second has the information needed to produce those results, so as long it is only referenced locally to a single thread, accessing the source is thread-safe, and invoking the Func is thread-safe, it's thread-safe (and those also applied to creating the first one in the first place).

In summary then, if we have methods that produce objects which refer solely to the source and the Funcs, we can be as thread-safe as the source and Funcs are, but no safer.

*Memoisation results in a side-effect that isn't visible from the outside, as an optimisation. Should it be used by our Funcs or by something they call into (e.g. a getter), then the memoisation would have to be implemented in a thread-safe manner for thread-safety to be possible.

OTHER TIPS

To add a bit of extra information on how I solved this problem I thought it'd be useful to post an associative answer.

The "standard" way of chaining method calls is to return an instance of the same class upon which subsiquent method calls can be made.

My original code did this by directly returning this, however, since my methods were mutating fields by building up collections of Func<T>'s, this left the consumer open to threading and reentry issues.

To solve this problem I decided to impliment ICloneable and have it return a new instance of the same class via object.MemberwiseClone(). This shallow-clone works fine my this case because the fields being added to were value types which are copied over during the shallw-clone process.

Each public method in my class now executes the instances Clone method and updates the private fields before returning the clone such that:

public class ServiceManager : IServiceManager
    {
        /// <summary>
        /// A collection of Funcs to execute if the service fails.
        /// </summary>
        private readonly List<Func<dynamic>> failedFuncs = 
                                             new List<Func<dynamic>>();

        /// <summary>
        /// The number of times the service call has been attempted.
        /// </summary>
        private int count;

        /// <summary>
        /// The number of times to re-try the service if it fails.
        /// </summary>
        private int attemptsAllowed;

        /// <summary>
        /// Gets or sets a value indicating whether failed.
        /// </summary>
        public bool Failed { get; set; }

        /// <summary>
        /// Gets or sets the service func.
        /// </summary>
        private Func<dynamic> ServiceFunc { get; set; }

        /// <summary>
        /// Gets or sets the result implimentation.
        /// </summary>
        private dynamic ResultImplimentation { get; set; }

        /// <summary>
        /// Gets the results.
        /// </summary>
        /// <typeparam name="TResult">
        /// The result.
        /// </typeparam>
        /// <returns>
        /// The TResult.
        /// </returns>
        public TResult Result<TResult>()
        {
            var result = this.Execute<TResult>();

            return result;
        }

        /// <summary>
        /// The execute service.
        /// </summary>
        /// <typeparam name="TResult">
        /// The result.
        /// </typeparam>
        /// <param name="action">
        /// The action.
        /// </param>
        /// <param name="attempts">
        /// The attempts.
        /// </param>
        /// <returns>
        /// ServiceManager.IServiceManager.
        /// </returns>
        public IServiceManager ExecuteService<TResult>(
                                   Func<TResult> action, int attempts)
        {
            var serviceManager  = (ServiceManager)this.Clone();
            serviceManager.ServiceFunc = (dynamic)action;
            serviceManager.attemptsAllowed = attempts;

            return serviceManager;
        }

        /// <summary>
        /// The if service fails.
        /// </summary>
        /// <typeparam name="TResult">
        /// The result.
        /// </typeparam>
        /// <param name="action">
        /// The action.
        /// </param>
        /// <returns>
        /// ServiceManager.IServiceManager`1[TResult -&gt; TResult].
        /// </returns>
        public IServiceManager IfServiceFailsThen<TResult>(
                                      Func<TResult> action)
        {
            var serviceManager = (ServiceManager)this.Clone();
            serviceManager.failedFuncs.Add((dynamic)action);
            return serviceManager;
        }


        /// <summary>
        /// Clones the current instance of ServiceManager.
        /// </summary>
        /// <returns>
        /// An object reprisenting a clone of the current ServiceManager.
        /// </returns>
        public object Clone()
        {
            return this.MemberwiseClone();
        }        
    }

Private methods removed for brevity. Full source code can be found here:

https://github.com/JamieDixon/ServiceManager

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top