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
1
0
"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.