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.