Précédent Index Suivant

Module Weak

Un pointeur faible (weak pointer) est un pointeur dont la zone mémoire pointée est récupérable à tout moment par le GC. Il peut être surprenant de parler d'une valeur qui peut disparaître à tout instant. En fait il faut voir ces pointeurs faibles comme un réservoir de valeurs encore disponibles. Cela s'avère particulièrement utile quand les ressources mémoire sont petites par rapport aux éléments à conserver. Le cas classique est la gestion d'un cache mémoire : une valeur peut être perdue, mais elle reste directement accessible tant qu'elle existe.

En Objective CAML on ne peut pas manipuler directement des pointeurs faibles, mais uniquement des tableaux de pointeurs faibles. Le module Weak définit le type abstrait 'a Weak.t correspondant au type 'a option array, vecteur de pointeurs faibles de type 'a. Le type concret 'a option est défini de la manière suivante :
type 'a option = None | Some of 'a;;
Les fonctions principales de ce module sont définies à la figure 9.14.

fonction type
create int -> 'a t
set 'a t -> int -> 'a option -> unit
get 'a t -> int -> 'a option
check 'a t -> int -> bool

Figure 9.14 : fonctions principales du module Weak


La fonction create alloue l'espace d'un tableau de pointeurs faibles dont chaque élément est initialisé par None. La fonction set range une valeur de type 'a option à un indice donné. La fonction get retourne la valeur d'indice n contenue dans un tableau de pointeurs faibles. La valeur retournée est alors référencée dans l'ensemble des racines du GC et n'est plus récupérable tant que dure ce référencement. On vérifie l'existence effective d'une valeur soit en utilisant la fonction check, soit par filtrage sur les motifs du type 'a option. La première solution est indépendante de la représentation des pointeurs faibles.

Les fonctions habituelles sur les structures linéaires existent aussi : length, pour la longueur, fill et blit pour les copies de parties de tableaux.

Exemple : cache d'images

Dans une application de construction d'images, il n'est pas rare de travailler sur plusieurs images. Quand on passe d'une image à une autre, la première est sauvegardée dans un fichier et la suivante est chargée à partir d'un autre fichier. On ne conserve en général que les noms des dernières images traitées. Pour éviter des accès au disque trop fréquents tout en n'occupant pas trop d'espace mémoire, on utilise un cache mémoire qui contient les dernières images chargées. Le contenu du cache mémoire peut être libéré si nécessaire. On l'implante par un tableau de pointeurs faibles, laissant à la charge du GC la décision de libération de ces images. Le chargement d'une image cherche tout d'abord dans le cache si l'image y est, si oui elle devient l'image courante, sinon son fichier est lu.

On définit une table des images de la manière suivante :

# type table_of_images = {
size : int;
mutable ind : int;
mutable name : string;
mutable current : Graphics.color array array;
cache : ( string * Graphics.color array array) Weak.t } ;;
Le champ size donne la taille du tableau ; le champ ind, l'indice de l'image courante ; le champ name, le nom de de l'image courante ; le champ current, l'image courante et le champ cache contient le tableau de pointeurs faibles sur les images. Il contient les dernières images chargées et leur nom.

La fonction init_table initialise la table avec une première image.

# let open_image filename =
let ic = open_in filename
in let i = ((input_value ic) : Graphics.color array array)
in ( close_in ic ; i ) ;;
val open_image : string -> Graphics.color array array = <fun>

# let init_table n filename =
let i = open_image filename
in let c = Weak.create n
in Weak.set c 0 (Some (filename,i)) ;
{ size=n; ind=0; name = filename; current = i; cache = c } ;;
val init_table : int -> string -> table_of_images = <fun>


Le chargement d'une nouvelle image conservera l'image courante dans la table et chargera la nouvelle. Pour ce faire, il faut tout d'abord tenter de trouver l'image dans le cache.

# exception Found of int * Graphics.color array array ;;
# let search_table filename table =
try
for i=0 to table.size-1 do
if i<>table.ind then match Weak.get table.cache i with
Some (n,img) when n=filename -> raise (Found (i,img))
| _ -> ()
done ;
None
with Found (i,img) -> Some (i,img) ;;



# let load_table filename table =
if table.name = filename then () (* l'image est l'image courante *)
else
match search_table filename table with
Some (i,img) ->
(* l'image trouvée devient l'image courante *)
table.current <- img ;
table.name <- filename ;
table.ind <- i
| None ->
(* l'image n'est pas dans le cache, il faut la charger *)
(* trouver une place vide dans le cache *)
let i = ref 0 in
while (!i<table.size && Weak.check table.cache !i) do incr i done ;
(* si aucune n'est libre, on en prend une pleine *)
( if !i=table.size then i:=(table.ind+1) mod table.size ) ;
(* on charge l'image à cette place
et elle devient l'image courante *)
table.current <- open_image filename ;
table.ind <- !i ;
table.name <- filename ;
Weak.set table.cache table.ind (Some (filename,table.current)) ;;
val load_table : string -> table_of_images -> unit = <fun>
La fonction load_table teste si l'image demandée est l'image courante, sinon elle vérifie dans le cache si celle-ci existe, si ce n'est pas le cas elle la charge du disque; dans les deux cas elle en fait l'image courante.

Pour tester ce programme, on utilise la fonction d'affichage du cache suivante :

# let print_table table =
for i = 0 to table.size-1 do
match Weak.get table.cache ((i+table.ind) mod table.size) with
None -> print_string "[] "
| Some (n,_) -> print_string n ; print_string " "
done ;;
val print_table : table_of_images -> unit = <fun>


Puis on teste le programme suivant :

# let t = init_table 10 "IMAGES/animfond.caa" ;;
val t : table_of_images =
{size=10; ind=0; name="IMAGES/animfond.caa";
current=
[|[|7437734; 7437734; 7437734; 7437734; 7437734; 7437734; 7437734;
7437734; 7437734; 7437734; 7437734; 7437734; 7440038; 7440038; ...|];
...|];
cache=...}
# load_table "IMAGES/anim.caa" t ;;
- : unit = ()
# print_table t ;;
IMAGES/anim.caa [] [] [] [] [] [] [] [] IMAGES/animfond.caa - : unit = ()


Cette technique de cache peut s'adapter à différentes applications.






Précédent Index Suivant