Being Cellfish

Stuff I wished I've found in some blog (and sometimes did)

September, 2012

Change of Address
This blog has moved to blog.cellfish.se.
Posts
  • Being Cellfish

    Task-based Asynchronous Pattern - WithTimeout

    • 2 Comments

    The same way we in CCR sometimes wanted to add a timeout to an existing "task" you probably want to do the same in TAP. So here are two extension methods you could use to add a timeout to any task of your choice:

     1: public async static Task WithTimeout(this Task task, TimeSpan timeout)
     2: {
     3:     var cancelationSource = new CancellationTokenSource();
     4:     var delay = Task.Delay(timeout, cancelationSource.Token);
     5:     await Task.WhenAny(task, delay);
     6:     if (task.IsCompleted)
     7:     {
     8:         cancelationSource.Cancel();
     9:     }
     10:     else
     11:     {
     12:         throw new TimeoutException();
     13:     }
     14: }
     15:         
     16: public async static Task<T> WithTimeout<T>(this Task<T> task, TimeSpan timeout)
     17: {
     18:     await ((Task)task).WithTimeout(timeout);
     19:     return await task;
     20: }
    
  • Being Cellfish

    Task-based Asynchronous Pattern - Introduction

    • 1 Comments

    With .Net 4.5 and async/await we have yet another pattern for asynchronous programming and it's time for you to really embrace this. The pattern is called Task-based Asynchronous Pattern, or TAP for short. in my opinion a good asynchronous pattern makes asynchronous code look synchronous. i think it's something with our brains that just have a hard time grasping asynchronously in a good way. But We have to wait and see if there will be a downside with TAP because a pattern that hides something too well might also cause problems, especially when it comes to edge cases.

    Anyway, what is TAP? Well you use tasks and in my opinion you typically avoid using the ContinueWith method. The use of ContinueWith will probably be more to optimize than make the code easy to understand. Apart from that (my own observation) a TAP method follow the following rules:

    • Returns a running (a.k.a. hot) Task or Task<T>.
    • Has an Async suffix in the method name, except for task combinators such as "WhenAll".
    • Returns quickly to the caller.
    • Is overloaded to accept cancellation token and/or IProgress<T> if it supports cancellation/progress reporting.
    • Does not do blocking I/O on the calling thread.

    Over the next few days I'll give you some nifty task combinators you might want to use.

  • Being Cellfish

    Task-based Asynchronous Pattern - WhenSome

    • 0 Comments

    I don't think this is the most common case, but sometimes you have a large number of tasks and you're interested in the result from a few of them, but not all of them. Here are some extension methods to wait for some tasks (no pun intended):

     1: public static Task WhenSome(int target, params Task[] tasks)
     2: {
     3:     return TaskExtensions.WhenSome(target, new Progress<int>(), tasks);
     4: }
     5:  
     6: public static Task<T[]> WhenSome<T>(int target, params Task<T>[] tasks)
     7: {
     8:     return TaskExtensions.WhenSome(target, new Progress<T>(), tasks);
     9: }
     10:  
     11: public async static Task WhenSome(
     12:     int target, IProgress<int> progress, params Task[] tasks)
     13: {
     14:     if (target > tasks.Length)
     15:     {
     16:         throw new ArgumentOutOfRangeException("target");
     17:     }
     18:  
     19:     var remainingTasks = new List<Task>(tasks);
     20:     int completedTasks = 0;
     21:     while (completedTasks < target)
     22:     {
     23:         await Task.WhenAny(tasks);
     24:         var stillRemainingTasks = new List<Task>(remainingTasks.Count - 1);
     25:         for (
     26:             int i = 0; 
     27:             i < remainingTasks.Count && completedTasks < target; 
     28:             i++)
     29:         {
     30:             if (remainingTasks[i].IsCompleted)
     31:             {
     32:                 progress.Report(++completedTasks);
     33:             }
     34:             else
     35:             {
     36:                 stillRemainingTasks.Add(remainingTasks[i]);
     37:             }
     38:         }
     39:  
     40:         remainingTasks = stillRemainingTasks;
     41:     }
     42: }
     43:  
     44: public async static Task<T[]> WhenSome<T>(
     45:     int target, IProgress<T> progress, params Task<T>[] tasks)
     46: {
     47:     if (target > tasks.Length)
     48:     {
     49:         throw new ArgumentOutOfRangeException("target");
     50:     }
     51:  
     52:     var remainingTasks = new List<Task<T>>(tasks);
     53:     var result = new List<T>(target);
     54:     while (result.Count < target)
     55:     {
     56:         await Task.WhenAny(tasks);
     57:         var stillRemainingTasks = new List<Task<T>>(
     58:             remainingTasks.Count - 1);
     59:         for (
     60:             int i = 0; 
     61:             i < remainingTasks.Count && result.Count < target; i++)
     62:         {
     63:             if (remainingTasks[i].IsCompleted)
     64:             {
     65:                 result.Add(remainingTasks[i].Result);
     66:                 progress.Report(remainingTasks[i].Result);
     67:             }
     68:             else
     69:             {
     70:                 stillRemainingTasks.Add(remainingTasks[i]);
     71:             }
     72:         }
     73:  
     74:         remainingTasks = stillRemainingTasks;
     75:     }
     76:  
     77:     return result.ToArray();
     78: }
    
  • Being Cellfish

    Task-based Asynchronous Pattern - WhenAllOrError

    • 0 Comments

    A very common scenario when you scatter and gather, i.e. start a number or parallell tasks and then wait for them all, is that you really just want to wait for all if they all succeed. in the case there is an error you typically want to wait no more, handle the error, cancel any tasks not already completed and then move on. Here are a few extension methods I would use in this case:

     1: public static Task WhenAllOrError(params Task[] tasks)
     2: {
     3:     return WhenAllOrError(new Progress<int>(), tasks);
     4: }
     5:  
     6: public static Task<T[]> WhenAllOrError<T>(params Task<T>[] tasks)
     7: {
     8:     return WhenAllOrError(new Progress<T>(), tasks);
     9: }
     10:  
     11: public static Task WhenAllOrError(
     12:     IProgress<int> progress, 
     13:     params Task[] tasks)
     14: {
     15:     var errorResult = new TaskCompletionSource<bool>();
     16:     int completed = 0;
     17:     foreach (var task in tasks)
     18:     {
     19:         task.ContinueWith(
     20:             t =>
     21:                 {
     22:                     progress.Report(Interlocked.Increment(ref completed));
     23:                     if (t.IsCanceled)
     24:                     {
     25:                         errorResult.TrySetCanceled();
     26:                     }
     27:                     else if (t.IsFaulted)
     28:                     {
     29:                         errorResult.TrySetException(
     30:                             t.Exception.InnerException);
     31:                     }
     32:                 });
     33:     }
     34:  
     35:     return Task.WhenAny(errorResult.Task, Task.WhenAll(tasks)).Unwrap();
     36: }
     37:  
     38: public async static Task<T[]> WhenAllOrError<T>(
     39:     IProgress<T> progress, 
     40:     params Task<T>[] tasks)
     41: {
     42:     var errorResult = new TaskCompletionSource<T[]>();
     43:     foreach (var task in tasks)
     44:     {
     45:         task.ContinueWith(
     46:             t =>
     47:                 {
     48:                     if (t.IsCanceled)
     49:                     {
     50:                         errorResult.TrySetCanceled();
     51:                     }
     52:                     else if (t.IsFaulted)
     53:                     {
     54:                         errorResult.TrySetException(
     55:                             t.Exception.InnerException);
     56:                     }
     57:                     else
     58:                     {
     59:                         progress.Report(t.Result);
     60:                         return t.Result;
     61:                     }
     62:                             
     63:                     return default(T);
     64:                 });
     65:     }
     66:  
     67:     return await await Task.WhenAny(errorResult.Task, Task.WhenAll(tasks));
     68: }
    

    Naturally there is a little code duplication between the Task and the Task<T> versions but starting new tasks just to make a Task into a Task<T> does not feel motivated here I think.

  • Being Cellfish

    Task-based Asynchronous Pattern - WithCancellation

    • 0 Comments

    if you're working with a Task based API that does not follow the TAP rules and hence does not expose an option to cancel you can always add your own this way:

     1: public static Task<T> WithCancellation<T>(
     2:     this Task<T> task, 
     3:     CancellationToken cancellationToken)
     4: {
     5:     var taskCompletionSource = new TaskCompletionSource<T>();
     6:     var cancellationRegistration = cancellationToken.Register(
     7:         () => taskCompletionSource.TrySetCanceled());
     8:     task.ContinueWith(
     9:         t =>
     10:             {
     11:                 cancellationRegistration.Dispose();
     12:                 if (t.IsCanceled)
     13:                 {
     14:                     taskCompletionSource.TrySetCanceled();
     15:                 }
     16:                 else if (t.IsFaulted)
     17:                 {
     18:                     taskCompletionSource.TrySetException(
     19:                         t.Exception.InnerException);
     20:                 }
     21:                 else
     22:                 {
     23:                     taskCompletionSource.TrySetResult(t.Result);
     24:                 }
     25:             });
     26:     return taskCompletionSource.Task;
     27: }
    

    This naturally will not actually stop the task you wrap this way but at least it returns control to the code waiting for completion faster.

  • Being Cellfish

    One way to make management understand development

    • 0 Comments

    I spent the weekend catching up on my RSS feeds and read this interesting story on how to explain why two developers pair programming would only produce a hundred lines of code in a single day. Brilliant idea to use business plans to explain what development really is about.

  • Being Cellfish

    Not really a robot, but...

    • 0 Comments

    Remember the Great Ball Contraption from earlier this year? There has been an update and it is awesome! My favourite part is the ball throwing one...

  • Being Cellfish

    Writing legacy code on purpose

    • 0 Comments

    Earlier this week I was working on a feature where I was using a specific framework and I got frustrated because my usual ways of faking dependencies in my unit tests were being blocked by the framework I was using. All my usual first option approaches just felt wrong and became cumbersome to use. After a discussion with a colleague I realized that the solution was to use my number one option for dependency injection in legacy code; protected virtual methods. I realized that even though I was writing a completely new class, the fact it had to inherit from a class in the framework used made my newly created class into a legacy class. Legacy as in hard to test. As soon as I started to treat my new class as legacy code it was a breeze to make it easy to test and decoupled from the rest of the code using the standard approaches I have when adding unit tests to legacy code.

    So even though it makes me feel a little dirty that I write a legacy code class on purpose I think the result is better than the alternatives in this case.

Page 1 of 1 (8 items)