Έχεις δίκιο, στον έλεγχο του RetryCount λείπει το else. Έπρεπε να είναι
if (retryCount == 0)
tcs.SetException(_original.Exception.InnerExceptions);
else
Retry(func, retryCount - 1, tcs);
αντί για
if (retryCount == 0)
tcs.SetException(_original.Exception.InnerExceptions);
Retry(func, retryCount - 1, tcs);
Στον κώδικα του Νίκου έχεις κάνει μία "κακή" αλλαγή. Η προσθήκη του Thread.Sleep θα προκαλέσει πρόβλημα γιατί αχρηστεύει ένα thread από το thread pool. Σκοπός του TPL είναι να απαλλαγούμε από απευθείας παρεμβάσεις στα threads.
Αν θέλεις να προσθέσεις κάποια καθυστέρηση, θα πρέπει να χρησιμοποιήσεις tasks για να κάνεις κάτι σαν την StartNewDelayed από τα ParallelExtensionExtras για να εκτελέσεις το Retry αφού περάσει το timeout. Σε αυτή την περίπτωση ο κώδικας αλλάζει στο παρακάτω:
private static Task<T> Retry<T>(Func<T> func, int retryCount, int delay, TaskCompletionSource<T> tcs = null)
{
if (tcs == null)
tcs = new TaskCompletionSource<T>();
Task.Factory.StartNew(func).ContinueWith(_original =>
{
if (_original.IsFaulted)
{
if (retryCount == 0)
tcs.SetException(_original.Exception.InnerExceptions);
else
Task.Factory.StartNewDelayed(delay).ContinueWith(t =>
{
Retry(func, retryCount - 1, delay,tcs);
});
}
else
tcs.SetResult(_original.Result);
});
return tcs.Task;
}
Αυτό που προσπαθώ να κάνω να παίξει και κάτι πάει στραβά, είναι το Retry με async/await. Κανονικά ο παρακάτω κώδικας θα έπρεπε να παίζει αλλά κρεμάει χωρίς καν unhandled exception, όταν κάνω compile σε Visual Studio 2010 με Async CTP.
private static async Task<T> Retry<T>(Func<T> func, int retryCount)
{
while (true)
{
try
{
var result = await TaskEx.Run(func);
return result;
}
catch
{
if (retryCount == 0)
throw;
retryCount--;
}
}
}
Υποθέτω ότι το rewriting που κάνει ο compiler τουλάχιστον στο Async CTP αποτυγχάνει όταν δει παραπάνω από δύο exceptions στο catch. Επίσης, δεν μπορώ να έχω await μέσα στο catch, κάνοντας το Delay επίσης δύσκολο. Κρίμα, αν έπαιζε το Retry θα ήταν τόσο καθαρό όσο η αρχική έκδοση!
Όταν ο compiler συναντάει τα async/await keywords στην ουσία δημιουργεί ένα task iterator ο οποίος τρέχει ως ξεχωριστό task τον κώδικα μεταξύ των awaits. Τα πράγματα γίνονται δύσκολα όταν υπάρχει catch/finally και παρότι ο compiler κάνει αρκετά καλή δουλειά, δεν μπορείς να έχεις await μέσα σε catch ή finally, ενώ όπως φαίνεται παραπάνω, τα απανωτά exception προκαλούν εγκεφαλικό.
Όπως θα πει και ο Νίκος, το async της F# είναι ανώτερο από το αντίστοιχο του .NET 
Παναγιώτης Καναβός, Freelancer
Twitter: http://www.twitter.com/pkanavos