[WP7Dev][Reactive] Rendre les Reactive Extensions Plus Stables

This article is available in english.

Lorsque l’on développe des applications .NET, les exceptions non gérées dans des threads ont le désagréable effet de terminer le processus courant.

Dans l’exemple suivant :

1
2
3
4
5
6
7
8
9
10
11
12
    static void Main(string[] args)
{
var t = new Thread(ThreadMethod);
t.Start();

Console.ReadLine();
}

private static void ThreadMethod()
{
Thread.Sleep(1000); throw new Exception();
}

Cette exception très simple va invariablement terminer le processus, et pour empêcher cela, l’exception doit être gérée correctement :

1
2
3
4
5
6
7
8
9
10
11
    private static void ThreadMethod()
{
try
{
Thread.Sleep(1000); throw new Exception();
}
catch (Exception e)
{
// TODO: Log and report the exception
}
}

Cela rend des classes comme System.Threading.Thread, System.Threading.Timer ou System.Threading.ThreadPool très dangereuses à utiliser si l’on veut une application toujours en fonctionnement. Il est alors requis qu’aucune exception ne sorte non gérée d’un des handlers donnés à une de ces classes.

Même si il est possible, en utilisant l’évènement AppDomain.UnhandledException, d’être notifié lorsqu’une exception a été levée et n’a pas été gérée proprement, la plupart du temps cela mène à l’arrêt de l’application.  Ce comportement a été introduit en .NET 2.0, afin d’éviter que les exceptions non gérées soit ignorées silencieusement.

Ceci étant, bien que cela soit un comportement par défaut tout à fait approprié, dans un environnement d’entreprise, je fais en sorte d’imposer des règles d’analyse statique de code (via VS2010 ou NDepend) qui empêchent l’utilisation en direct de ces classes. Cela permet de forcer l’utilisation de classes wrappers qui interceptent les exceptions avec un filtre très large afin de les logger et de les rapporter, mais qui ne termine pas le processus.

Bien entendu, cela dénote la présence d’un véritable bug qui doit être analysé, puisque les exceptions ne doivent pas être gérées aussi tard.

 

Le cas du Reactive Framework

Dans Silverlight pour Windows Phone 7, et dans n’importe quelle application .NET 3.5 or .NET 4.0 qui utilise les Reactive Extensions, il est très simple de changer de threads.

Les opérateurs Réactifs comme Timer, BufferWithTime, ObserveOn ou SubscribeOn permettent l’utilisation de Schedulers spécifiques comme ThreadPool, TaskPool ou NewThread, et si un subscriber ne gère pas l’exception proprement, le processus se terminera aussitôt.

Cet exemple adapté au Reactive Framework termine l’application :

1
2
3
4
5
6
7
8
9
10
11
12
    static void Main(string[] args)
{
Observable.Timer(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10))
.Subscribe(_=> ThreadMethod());

Console.ReadLine();
}

private static void ThreadMethod()
{
throw new Exception();
}

L’opérateur Observable.Timer utilise la classe System.Threading.Timer et cela la rend autant vulnérable aux problèmes de terminaison. Chaque subscriber doit gérer les exceptions lancées dans le delegate OnNext, ou l’application se terminera.

Aussi, ne pensez pas que le delegate OnError passé à la méthode Observable.Subscribe va gérer les exceptions levées durant l’execution du code dans OnNext. OnError va simplement notifier des erreurs levées durant l’execution des operateurs précédents, et non pas le courant.

La Méthode d’extension IScheduler.AsSafe()

Malheureusement, il n’est pour l’instant pas possible de surcharger le scheduler par défaut utilisé en interne par les opérateurs Réactifs. Le seul moyen de gérer toutes les exceptions non gérées est d’utiliser l’opérateur ObserveOn, et d’intercepter les appels à la méthode IScheduler.Schedule. Les appels peuvent alors être décorés avec des gestionnaires d’exceptions qui loggent et raportent l’exception, sans terminer le processus.

Donc, pour généraliser ce comportement de logging et de rapport d’erreur, j’ai donc créé la méthode d’extension AsSafe() que l’on peut placer tout en haut d’une expression Réactive :

1
2
3
    Observable.Timer(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10))
.ObserveOn(Scheduler.ThreadPool.AsSafe())
.Subscribe(_=> ThreadMethod());

Et voici le code de cette très simple extension :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public static class SafeSchedulerExtensions
{
public static IScheduler AsSafe(this IScheduler scheduler)
{
return new SafeScheduler(scheduler);
}

private class SafeScheduler : IScheduler
{
private IScheduler _source;

public SafeScheduler(IScheduler scheduler) {
this._source = scheduler;
}

public DateTimeOffset Now { get { return _source.Now; } }

public IDisposable Schedule(Action action, TimeSpan dueTime)
{
return _source.Schedule(Wrap(action), dueTime);
}

public IDisposable Schedule(Action action)
{
return _source.Schedule(Wrap(action));
}

private Action Wrap(Action action)
{
return () => {
try {
action();
}
catch (Exception e) {
// Log and report the exception.
}
};

}
}
}
Publié mardi 7 septembre 2010 20:24 par jay
Classé sous , , ,
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Commentaires

# re: [WP7Dev][Reactive] Rendre les Reactive Extensions Plus Stables @ mercredi 8 septembre 2010 09:43

Ca ne marche pas avec la méthode d'extension Catch ou avec la surcharge de subscribe qui prend un delegate dans le cas d'une erreur ?

Nicolas


Les 10 derniers blogs postés

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01

- SharePoint Online: Script PowerShell pour supprimer une colonne dans tous les sites d’une collection par Blog Technique de Romelard Fabrice le 11-27-2018, 18:01