Being Cellfish

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

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

    How Do Software Engineers Understand Code Changes?

    • 1 Comments

    Some researchers from MSR have published a paper on how do software engineers understand code changes. To me there are no surprises, especially since I participated in the survey the paper is based on (I think I'm quoted too actually). The bottom line is that developers spend a lot of time trying to understand previous code changes and that good check-in descriptions help in the effort to understand old changes.

  • Being Cellfish

    Task-based Asynchronous Pattern - WithCancellation again!

    • 0 Comments

    Last week Stephen Toub covered WithCancellation in a more thorough way than I did. You should read his article too!

  • Being Cellfish

    XCOM interactive trailer

    • 0 Comments

    I loved the original XCOM game (and terror from the deep was OK). Will definitely play this new game when it releases next week. They did something interesting for their recent trailer; an interactive trailer where you actually get to change how the flow goes.

  • Being Cellfish

    Task-based Asynchronous Pattern - WhenAllOrErrorBatched

    • 0 Comments

    This is a variant of WhenAllorError that a colleague asked me about. His scenario was that he had a lot of tasks to complete but since they all involved making HTTP requests to other servers he did not want to start them all at once but rather start a few and then as they completed start a few more. That's how I came up with WhenAllOrErrorBatched. The idea is that instead of giving it a list of tasks you provide a batchSize (number of parallel tasks) and a function that returns new tasks that will be called until it returns null.

     1: public async static Task<T[]> WhenAllOrErrorBatched<T>(
     2:     int batchSize, Func<Task<T>> nextTask)
     3: {
     4:     var result = new List<T>(batchSize);
     5:     var pending = new List<Task<T>>(batchSize);
     6:     bool pendingTasks = true;
     7:     while (true)
     8:     {
     9:         while (pendingTasks && pending.Count < batchSize)
     10:         {
     11:             var task = nextTask();
     12:             if (task == null)
     13:             {
     14:                 pendingTasks = false;
     15:                 break;
     16:             }
     17:  
     18:             pending.Add(task);
     19:         }
     20:  
     21:         if (pending.Count == 0)
     22:         {
     23:             break;
     24:         }
     25:  
     26:         await Task.WhenAny(pending);
     27:  
     28:         for (int i = 0; i < pending.Count; i++)
     29:         {
     30:             if (pending[i].IsCompleted)
     31:             {
     32:                 result.Add(pending[i].Result);
     33:                 pending.RemoveAt(i);
     34:                 i--;
     35:             }
     36:         }
     37:     }
     38:  
     39:     return result.ToArray();
     40: }
    
  • 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

    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

    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.

  • 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.

Page 7 of 49 (481 items) «56789»