Section courante

A propos

Section administrative du site

 Langage  Elément  Tutoriel  Aide 
ABAP/4
Ada
Assembleur
Assembly & bytecode
ASP (Active Server Pages)
Basic
C
C++
C# (C Sharp)
Cobol
ColdFusion
Fortran
HTML
Java
JavaScript
LISP
Logo
LotusScript
Oberon
Pascal
Perl
PHP
PL/1
Prolog
Python
Rebol
REXX
Ruby
Rust
SAS
NoSQL
SQL
Swift
X++ (Axapta)
GNAT
SMALLAda
VHDL
Assembleur 370
Assembleur 1802
Assembleur 4004
Assembleur 6502
Assembleur 6800
Assembleur 68000
Assembleur 8080 et 8085
Assembleur 8089
Assembleur 80x86
Assembleur AGC4
Assembleur ARM
Assembleur DPS 8000
Assembleur i860
Assembleur Itanium
Assembleur MIPS
Assembleur PDP-11
Assembleur PowerPC
Assembleur RISC-V
Assembleur SPARC
Assembleur SuperH
Assembleur UNIVAC I
Assembleur VAX
Assembleur Z80
Assembleur Z8000
Assembleur z/Architecture
ASSEMBLER/MONITOR 64
Micol Assembler
GFA Assembler
A86
MASM (Macro Assembler)
TASM (Turbo Assembler)
CIL
Jasmin
LLVM
MSIL
Parrot
P-Code (PCode)
SWEET16
G-Pascal
ASP 1.0
ASP 2.0
ASP 3.0
ASP.NET
ASP.NET Core
ABasiC (Amiga)
Adam SmartBASIC
Altair BASIC
AmigaBASIC (Amiga)
AMOS Basic (Amiga)
Atari Basic (Atari 400, 600 XL, 800, 800XL)
Basic Apple II (Integer BASIC/APPLESOFT)
Basic Commodore 64 (CBM-BASIC)
Basic Commodore 128 (BASIC 7.0)
Basic Commodore VIC-20 (CBM-BASIC 2.0)
Basic Coco 1 (Color Basic)
Basic Coco 2 (Extended Color Basic)
Basic Coco 3 (Extended Color Basic 2.0)
BASICA (PC DOS)
Basic Pro
BBC BASIC
Blitz BASIC (Amiga)
DarkBASIC
Dartmouth BASIC
GFA-Basic (Atari ST/Amiga)
GWBASIC (MS-DOS)
Liberty BASIC
Locomotive BASIC (Amstrad CPC)
MSX-Basic
Omikron Basic (Atari ST)
Oric Extended Basic
Power Basic
Quick Basic/QBasic (MS-DOS)
Sinclair BASIC (ZX80, ZX81, ZX Spectrum)
ST BASIC (Atari ST)
Turbo Basic
Vintage BASIC
VBScript
Visual Basic (VB)
Visual Basic .NET (VB .NET)
Visual Basic pour DOS
Yabasic
BeckerBASIC
SIMONS' BASIC
Basic09 d'OS-9
Disk Extended Color Basic
Basic09 d'OS-9
Disk Extended Color Basic
Access
Excel
Visual Basic pour Windows
Visual Basic .NET pour Windows
C Shell Unix (csh)
C pour Amiga
C pour Atari ST
C pour DOS
C pour Falcon030
C pour GEMDOS (Atari ST)
C pour Linux
C pour PowerTV OS
C pour OS/2
C pour Unix
C pour Windows
Aztec C
CoCo-C
GNU C
HiSoft C
IBM C/2
Introl-C
Lattice C
Microsoft C
MinGW C
MSX-C
Open Watcom C
OS-9 C Compiler
Pure C
Quick C
Turbo C
HiSoft C for Atari ST
HiSoft C for CP/M (Amstrad CPC)
C++ pour OS/2
C++ pour Windows
Borland C++
C++Builder
IBM VisualAge C++
Intel C++
MinGW C++
Open Watcom C++
Symantec C++
Turbo C++
Visual C++
Visual C++ .NET
Watcom C++
Zortech C++
C# (C Sharp) pour Windows
Apple III Cobol
Microsoft Cobol
BlueDragon
Lucee
OpenBD
Railo
Smith Project
Microsoft Fortran
WATFOR-77
CSS
FBML
Open Graph
SVG
XML
XSL/XSLT
LESS
SASS
GCJ (GNU)
JSP
Jython
Visual J++
Node.js
TypeScript
AutoLISP
ACSLogo
LotusScript pour Windows
Amiga Oberon
Oberon .NET
Apple Pascal
Delphi/Kylix/Lazarus
Free Pascal
GNU Pascal
HighSpeed Pascal
IBM Personal Computer Pascal
Lisa Pascal
Maxon Pascal
MPW Pascal
OS-9 Pascal
OSS Personal Pascal
Pascal-86
Pascal du Cray Research
Pascal/VS
Pascal-XT
PURE Pascal
QuickPascal
RemObjets Chrome
Sun Pascal
THINK Pascal
Tiny Pascal (TRS-80)
Turbo Pascal
UCSD Pascal
VAX Pascal
Virtual Pascal
Turbo Pascal for CP/M-80
Turbo Pascal for DOS
Turbo Pascal for Macintosh
Turbo Pascal for Windows
CodeIgniter (Cadre d'application)
Drupal (Projet)
Joomla! (Projet)
Phalanger (PHP .NET)
phpBB (Projet)
Smarty (balise)
Twig (balise)
Symfony (Cadre d'application)
WordPress (Projet)
Zend (Cadre d'application)
PL360
PL/M-80
PL/M-86
Turbo Prolog
CPython
IronPython
Jython
PyPy
AREXX
Regina REXX
JMP
Btrieve
Cassandra
Clipper
CouchDB
dBASE
Hbase
Hypertable
MongoDB
Redis
Access
BigQuery
DB2
H2
Interbase
MySQL
Oracle
PostgreSQL
SAP HANA
SQL Server
Sybase
U-SQL
Introduction
Configuration de l'environnement de développement
Fabriquer le LibSass
Profilage de LibSass
Utilisation de l'API
LibSass et Unicode
Composantes internes de SourceMap
Allocateur de mémoire personnalisé
Implémentation du pointeur intelligent LibSass
Les plugiciels
Compilation avec des fichiers makefiles
Compilation avec autotools
Compilation de LibSass avec MinGW (makefiles)
Compilation de LibSass avec Visual Studio
Compilation sur Mac OS X
Création d'une bibliothèque système partagée
Ebuilds pour Gentoo Linux
Contexte Sass
Valeur Sass
Fonction Sass
Importateur Sass
Exemple de code pour le contexte Sass
Contexte Sass interne
Exemple de code pour la valeur Sass
Valeur Sass interne
Exemple de code pour la fonction Sass
Fonction Sass interne
Exemple de code pour l'importateur Sass
Importateur Sass interne
Préface
Notes légales
Dictionnaire
Recherche

Implémentation du pointeur intelligent LibSass

LibSass utilise des pointeurs intelligents très similaires à shared_ptr, connu par Boost ou C++11. L'implémentation est un peu moins modulaire, car inutile. Plusieurs options de débogage à la compilation sont disponibles pour déboguer les cycles de vie de la mémoire.

Classes mémoire

SharedObj

Classe de base pour les implémentations de noeuds actuels. Cela garantit que chaque objet possède un compteur de référence et d'autres valeurs :

  1. class AST_Node : public SharedObj { ... };

SharedPtr (classe de base pour SharedImpl)

Classe de base conservant le pointeur. Le compteur de références est entreposé directement dans l'objet pointeur (SharedObj).

SharedImpl (hérite de SharedPtr)

Il s'agit de la classe de base principale pour les objets que vous utilisez dans votre code. Elle garantit que la mémoire vers laquelle elle pointe sera supprimée lorsque toutes les copies vers le même objet/la même mémoire seront hors de portée.

  1. Class* pointer = new Class(...);
  2. SharedImpl<Class> obj(pointer);

Pour éviter au développeur de devoir saisir la classe modèle à chaque fois, nous avons créé des typedefs pour chaque spécialisation de noeud AST disponible :

  1. typedef SharedImpl<Number> Number_Obj;
  2. Number_Obj number = SASS_MEMORY_NEW(...);

Cycles de vie de la mémoire

Récupération de pointeurs

On utilise souvent le terme «récupération». Il désigne le moment où un pointeur brut, échappant à tout contrôle, est assigné à un objet compté par référence (XYZ_Obj = XYZ_Ptr). À partir de ce moment, la mémoire est automatiquement libérée dès que l'objet sort de la portée (mais seulement si le compteur de références atteint zéro). L'essentiel est que vous n'ayez pas à vous soucier de la gestion de la mémoire.

Détachement d'objets

Il est parfois impossible de renvoyer directement les objets comptés par référence (voir les problèmes de types de retour covariants invalides ci-dessous). Cependant, il est souvent nécessaire d'utiliser des objets de référence dans une fonction pour éviter les fuites en cas de déclenchement d'une erreur. Pour cela, vous pouvez utiliser le détachement, qui détache la mémoire du pointeur de l'objet compté par référence. Ainsi, lorsque l'objet compté par référence sort de la portée, la mémoire attachée ne sera pas libérée. Vous êtes alors à nouveau responsable de la libération de la mémoire (il suffit de l'assigner à nouveau à un objet compté par référence).

Références circulaires

Les implémentations de mémoire comptée par référence sont sujettes aux références circulaires. Ce problème peut être résolu en utilisant un ramasse-miettes multigénérationnel. Cependant, pour notre cas d'utilisation, cela semble excessif. Il n'existe actuellement aucun moyen pour les utilisateurs (code Sass) de créer des références circulaires. Nous pouvons donc contourner ce problème potentiel. Les développeurs doivent toutefois être conscients de cette limitation.

Il existe, à mon avis, deux endroits où des références circulaires peuvent se produire : le membre source de chaque sélecteur ; et le code d'extension (gestion des noeuds). Le moyen le plus simple d'éviter ce problème est d'affecter uniquement des clones d'objets complets à ces membres. Si vous savez que la durée de vie des objets est supérieure à celle de la référence créée, vous pouvez également stocker uniquement le pointeur brut. En cas de besoin, ce problème peut être résolu avec des pointeurs faibles.

Résolution des problèmes de types de retour covariants invalides

Si vous n'êtes pas familier avec le problème mentionné, vous pouvez vous renseigner sur les types de retour covariants et les fonctions virtuelles.

Ils ont rencontré ce problème au moins avec le modèle de visiteur CRTP (eval, expand, listize,...). Cela signifie qu'ils ne peuvent pas renvoyer directement les objets référencés. Ils sont obligés de renvoyer des pointeurs bruts, sinon ils auraient besoin de nombreux upcasts explicites et coûteux de la part des appelants/consommateurs.

Fonctions simples allouant de nouveaux noeuds AST

Lors de l'étape d'analyse, nous créons souvent de nouveaux objets et pouvons simplement renvoyer un pointeur unique (ce qui signifie que la propriété revient clairement à l'appelant). L'appelant/consommateur est responsable de la libération de la mémoire.

  1. typedef Number* Number_Ptr;
  2. int parse_integer() {
  3.   ... // faire l'analyse
  4.   return 42;
  5. }
  6. Number_Ptr parse_number() {
  7.   Number_Ptr p_nr = SASS_MEMORY_NEW(...);
  8.   p_nr->value(parse_integer());
  9.   return p_nr;
  10. }
  11. Number_Obj nr = parse_number();

Le modèle ci-dessus est recommandé pour des cas aussi simples.

Allouer de nouveaux nouds AST dans des fonctions pouvant générer des erreurs.

L'exemple précédent comporte un inconvénient majeur, compte tenu de son implémentation plus réaliste générant une erreur. L'erreur peut se produire en profondeur dans une autre fonction. Dans ce cas, le stockage de pointeurs bruts à libérer entraînerait une fuite.

  1. int parse_integer() {
  2.   ... // faire l'analyse
  3.   if (error) throw(error);
  4.   return 42;
  5. }

Avec cette fonction parse_integer, l'exemple précédent entraînerait une fuite de mémoire. C'est assez évident, car la mémoire allouée ne sera pas libérée, n'ayant jamais été affectée à une valeur SharedObj. Par conséquent, le code ci-dessus serait plus approprié :

  1. typedef Number* Number_Ptr;
  2. int parse_integer() {
  3.   ... // faire l'analyse
  4.   if (error) throw(error);
  5.   return 42;
  6. }
  7. // cette fuite est due au retour du pointeur devant renvoyer Number_Obj à la place, bien que ce ne soit pas possible pour les virtuels !
  8. Number_Ptr parse_number() {
  9.   Number_Obj nr = SASS_MEMORY_NEW(...);
  10.   nr->value(parse_integer()); // déclencheurs
  11.   return &nr; // Ptr depuis Obj
  12. }
  13. Number_Obj nr = parse_number();
  14. // sera désormais libéré automatiquement

L'exemple ci-dessus ne fonctionnera malheureusement pas tel quel, car nous renvoyons un Number_Ptr depuis cette fonction. Par conséquent, l'objet alloué dans la fonction est déjà supprimé lorsqu'il est récupéré par l'appelant. Une solution simple pour ce cas d'utilisation simplifié serait de remplacer le type de retour de parse_number par Number_Obj. C'est exactement ce que nous faisons dans l'analyseur. Cependant, comme indiqué précédemment, cela ne fonctionnera pas pour les fonctions virtuelles en raison de types de retour covariants non valides !

Renvoyer des objets gérés depuis des fonctions virtuelles

La solution simple serait de simplement créer une nouvelle copie sur la mémoire de tas et de la renvoyer. Mais cela semble une solution très peu élégante. Pourquoi ne pas simplement indiquer à l'objet de le traiter comme un objet nouvellement alloué ? Et c'est possible. J'ai ajouté une méthode de détachement indiquant à l'objet de survivre à la désallocation jusqu'à la prochaine récupération. Cela signifie qu'il y aura une fuite s'il n'est pas récupéré par le consommateur.

  1. typedef Number* Number_Ptr;
  2. int parse_integer() {
  3.   ... // faire l'analyse
  4.   if (error) throw(error);
  5.   return 42;
  6. }
  7. Number_Ptr parse_number() {
  8.   Number_Obj nr = SASS_MEMORY_NEW(...);
  9.   nr->value(parse_integer()); // throws
  10.   return nr.detach();
  11. }
  12. Number_Obj nr = parse_number();
  13. // sera désormais libéré automatiquement

Options de débogage à la compilation

Pour activer le débogage mémoire, vous devez définir DEBUG_SHARED_PTR. Cela peut être fait dans include/sass/base.h :

  1. #define DEBUG_SHARED_PTR

Cela affichera la mémoire perdue à la sortie sur stderr. Vous pouvez également utiliser setDbg(true) sur des variables spécifiques pour générer des événements d'augmentation, de diminution et autres.

Pourquoi réinventer la roue quand il existe shared_ptr de C++11 ?

Tout d'abord, l'implémentation d'une classe de pointeur intelligent n'est pas si difficile. Mais il y a des avantages plus importants :

Sécurité des processus léger

Comme indiqué précédemment, ce n'est pas thread-safe pour le moment. Mais ils n'en ont de toute façon pas besoin. Et ils supposent qu'ils ne partagerons probablement jamais de noeuds AST entre différents processus légers.



PARTAGER CETTE PAGE SUR
Dernière mise à jour : Mardi, le 8 octobre 2024