[WP7Dev] Attention à l’attribut [ThreadStatic] dans Silverlight pour Windows Phone 7

This post is available in english.

En d’autres mots, il n’est pas supporté !

Et le pire est que l’on est même pas averti que ce n’est pas supporté... Le code compile, mais l'attribut n'a aucun effet ! On peut bien entendu lire l’article “the differences between silverlight on Windows and Windows Phone”, mais bon, il est facile de l’oublier. Peut-être qu’une règle d’analyse statique de code pourrait empêcher cela.

Mais enfin, vous voudrez probablement utiliser ThreadStatic parce que vous en avez besoin. Mais comme ce n’est pas supporté, vous pourriez aller vers Thread.GetNamedDataSlot, vous me direz.

Pas de chance. Ce n’est pas supporté non plus.

Cela nous laisse à l’implémentation de notre propre TLS, à la main.

 

Mise à jour de Umbrella pour Silverlight sur Windows Phone

Je suis un grand fan d’Umbrella, et la première fois que j’ai eu à utiliser Dictionary<>.TryGetValue et son magique paramètre “out”, dans l'essai de portage de mon application de Controle à Distance, j’ai décidé de porter Umbrella vers Windows Phone 7. Pour enfin utiliser GetValueOrDefault sans le réécrire, encore.

J’ai réussi à faire passer la majorité des tests unitaires, excepté ceux qui émettent du code, utilisent des fonctionnalités du web, utilisent les sérialiseurs xml et binaires, appellent des méthodes privées via la réflection, et ainsi de suite.

Il y a quelques autres parties qui nécessitaient d’être mise à jour, parceque la classe TypeDescriptor n’est pas disponible en WP7, et il faut passer par un try/catch pour vérifier qu’une valeur est convertible d’un type vers un autre. Mais ce n’est pas vraiment gênant, et cela fonctionne comme attendu.

 

ThreadLocalSource dans Umbrella

Umbrella contient une classe nommée ThreadLocalSource qui encapsule le comportement du TLS, et il est très facile de créer une variable statique de ce type, plutôt que d’utiliser une variable statique ThreadStatic.

Les exemples Quick Start en font ce genre d’utilisation :

1
2
3
4
5
6
7
8
9
10
    ISource<int> threadLocal = new ThreadLocalSource<int>(1);

int valueOnOtherThread = 0;

Thread thread = new Thread(() => valueOnOtherThread = threadLocal.Value);
thread.Start();
thread.Join();

Assert.Equal(1, threadLocal.Value);
Assert.Equal(0, valueOnOtherThread);

La Thread principale place la valeur à 1, et l’autre thread essaye de lire la même variable et elle doit être différente. (La valeur par défaut d’un int, qui est 0).

 

Mise à jour de ThreadLocalSource pour éviter l’utilisation de ThreadStatic

L’implémentation du TLS dans .NET est principalement un dictionnaire de paires de string/object qui est attaché à chaque thread active. Donc pour mimiquer cela, on a simplement besoin de créer une liste de toutes les threads qui ont besoin de stocker quelque chose pour elles-mêmes, et l’encapsuler proprement.

On peut créer une variable comme celle-ci :

1
    private static Tuple<WeakReference, IDictionary<string, T>>[] _tls;

Cette variable est intentionnellement du type d’un tableau pour tenter d’utiliser la localité spatiale en mémoire. Puisque sur cette plateforme il ne devrait pas y avoir beaucoup de threads, il est tout à fait acceptable de parcourir le tableau pour en trouver une. Cette approche tente d’être sans locks, en utilisant un mécanisme de “retry” pour mettre à jour le tableau. Le type WeakReference est utilisé pour éviter de garder une référence vers les threads une fois qu’elles sont terminées.

Donc, pour faire la mise à jour du tableau, on peut s’y prendre comme suit :

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
42
43
44
45
46
47
    private static IDictionary<string, T> GetValuesForThread(Thread thread)
{
// Find the TLS for the specified thread
var query = from entry in _tls

// Only get threads that are still alive
let t = entry.T.Target as Thread

// Get the requested thread
where t != null && t == thread
select entry.U;

var localStorage = query.FirstOrDefault();

if (localStorage == null)
{
bool success = false;

// The storage for the new Thread
localStorage = new Dictionary<string, T>();

while(!success)
{
// store the original array so we can check later if there has not
// been anyone that has updated the array at the same time we did
var originalTls = _tls;

var newTls = new List<Tuple<WeakReference, IDictionary<string, T>>>();

// Add the slots for which threads references are still alive
newTls.AddRange(_tls.Where(t => t.T.IsAlive));

var newSlot = new Tuple<WeakReference, IDictionary<string, T>>()
{
T = new WeakReference(thread),
U = localStorage
};

newTls.Add(newSlot);

// If no other thread has changed the array, replace it.
success = Interlocked.CompareExchange(ref _tls, newTls.ToArray(), originalTls) != _tls;
}
}

return localStorage;
}


L’utilisation d’une approche sans lock comme celle-ci devrait limiter la contention autour de l’utilisation de cette classe de simili-TLS. Il pourrait y avoir, de temps à autres, des manipulations faites plusieurs fois lors de “race conditions” sur la mise à jour de la variable _tls, mais cela est tout à fait acceptable. De plus, les livelocks ne peuvent pas arriver, considérant le type de système préemptif sur lequel WP7 fonctionne.

J’ai l’impression que développer sur cette plateforme va nécessiter plein de petits “hacks” du genre... Ca va être intéressant !

Publié dimanche 20 juin 2010 00:11 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] Attention à l’attribut [ThreadStatic] dans Silverlight pour Windows Phone 7 @ dimanche 20 juin 2010 11:32

Pas mal comme blague.

Du coup pour ce qui est des alertes sur les dépendances, NDepend peut aider un peu :

//

<Name>Check for ThreadStaticAttribute usage</Name>

<Name>Check for Thread.GetNamedDataSlot usage</Name>

coq

# re: [WP7Dev] Attention à l’attribut [ThreadStatic] dans Silverlight pour Windows Phone 7 @ dimanche 20 juin 2010 11:37

Super, le commentaire a été purgé de toute la partie utile :-D

coq

# re: [WP7Dev] Attention à l’attribut [ThreadStatic] dans Silverlight pour Windows Phone 7 @ dimanche 20 juin 2010 15:08

En effet, NDepend peut tout à fait faire l'affaire, sauf pour Thread.GetNamedDataSlot, qui n'est tout simplement pas présent... Cela m'a fait comprendre que le début de mon post n'était pas assez précis. ThreadStatic est bien présent, mais il n'a aucun effet !

jay


Les 10 derniers blogs postés

- Les actualités de la semaine sur c2i.fr (14 mai - 20 mai) par Richard Clark le il y a 4 heures et 25 minutes

- Reactive Extensions : Consommer des services avec Rx Partie 3, les pièges à éviter par Léonard Labat le il y a 13 heures et 30 minutes

- SharePoint Blog Site, problème d’archives par Le Blog (Vert) d'Arnaud JUND le 05-20-2012, 13:09

- Soirée ALT.NET Mai - 3 présentations par #Rui le 05-18-2012, 11:59

- [ #SharePoint 2010][ #SQLServer 2012] AlwaysOn pour SharePoint (2/4) : Configuration (2e partie)… par Le blog de Patrick [MVP SharePoint] le 05-18-2012, 11:31

- Team Foundation Server 11: tous les trésors cachés du site d’équipe par Philess le 05-16-2012, 19:01

- [PowerShell 3] Télécharger et installer la documentation en ligne par Blog de SPBrouillet (Pierrick BROUILLET) le 05-16-2012, 17:36

- [#SharePoint 2010][#SQLServer 2012] AlwaysOn pour SharePoint (1/4) : Configuration (1ère partie)… par Le blog de Patrick [MVP SharePoint] le 05-16-2012, 12:10

- Job Day @MIC Brussels - .Net Developers on Mobile applications par Le Blog (Vert) d'Arnaud JUND le 05-15-2012, 20:26

- [SharePoint 2010] – SharePoint 2010, Windows (Server) 8 et des erreurs IIS sont dans une VM… par Blog de SPBrouillet (Pierrick BROUILLET) le 05-14-2012, 12:10