/* ================================================================================ RPL/2 (R) version 4.1.20 Copyright (C) 1989-2015 Dr. BERTRAND Joël This file is part of RPL/2. RPL/2 is free software; you can redistribute it and/or modify it under the terms of the CeCILL V2 License as published by the french CEA, CNRS and INRIA. RPL/2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL V2 License for more details. You should have received a copy of the CeCILL License along with RPL/2. If not, write to info@cecill.info. ================================================================================ */ #include "rpl-conv.h" // Les fonctions malloc() et free() sont surchargées pour appeler // les fonctions rpl_malloc() et rpl_free(). Elles sont désactivées pour // éviter d'avoir un allocateur récursif. #undef malloc #undef realloc #undef free // Classes : // -1 : trop grand pour utiliser l'allocateur, on utilise l'allocateur par // défaut du système. La classe -1 est aussi utilisée lorsque le buffer // n'a pas encore été alloué. Dans ce cas, il est initialisé à NULL. static size_t tailles[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, 65536, 0 }; static int longueur_tailles = 0; /* ================================================================================ Recherche de la longueur optimale du buffer ================================================================================ Entrée : longueur du buffer à allouer -------------------------------------------------------------------------------- Sortie : indice de la liste candidate, -1 si aucune liste -------------------------------------------------------------------------------- Effets de bord : néant ================================================================================ */ static int recherche_longueur_buffer_optimale(size_t longueur) { int a; int b; int m; a = 0; b = longueur_tailles - 1; if (longueur > tailles[b]) { return(-1); } while((b - a) > 1) { m = (a + b) / 2; if (longueur <= tailles[m]) { b = m; } else { a = m; } } return(b); } /* ================================================================================ Allocation d'une enveloppe de buffer Contrairement au malloc() de la libc, cet allocateur fournit une structure (l'enveloppe) contenant un pointeur sur la zone allouée ainsi que la longueur de la zone allouée et une information de classe pour gérer le cache. La zone allouée est comporte un pointeur sur l'enveloppe puis un buffer traditionnel. ================================================================================ Entrée : état du processus courant -------------------------------------------------------------------------------- Sortie : enveloppe de buffer -------------------------------------------------------------------------------- Effets de bord : néant ================================================================================ */ static inline struct_buffer * allocation_enveloppe_buffer(struct_processus *s_etat_processus) { struct_buffer *s_buffer; if ((*s_etat_processus).pointeur_enveloppes_buffers > 0) { s_buffer = (*s_etat_processus).enveloppes_buffers [--(*s_etat_processus).pointeur_enveloppes_buffers]; } else { if ((s_buffer = sys_malloc(sizeof(struct_buffer))) == NULL) { (*s_etat_processus).erreur_systeme = d_es_allocation_memoire; return(NULL); } } return(s_buffer); } /* ================================================================================ Libération d'une enveloppe de buffer Ne libère pas le buffer qui doit être libéré à part. ================================================================================ Entrée : état du processus courant, enveloppe -------------------------------------------------------------------------------- Sortie : néant -------------------------------------------------------------------------------- Effets de bord : néant ================================================================================ */ static inline void liberation_enveloppe_buffer(struct_processus *s_etat_processus, struct_buffer *s_buffer) { if ((*s_etat_processus).pointeur_enveloppes_buffers < TAILLE_CACHE) { (*s_etat_processus).enveloppes_buffers [(*s_etat_processus).pointeur_enveloppes_buffers++] = s_buffer; } else { sys_free(s_buffer); } return; } /* ================================================================================ Initialisation des structures de données de l'allocateur ================================================================================ Entrée : état du processus courant (contient les données nécessaires au fonctionnement de l'allocateur par thread) -------------------------------------------------------------------------------- Sortie : néant -------------------------------------------------------------------------------- Effets de bord : néant ================================================================================ */ void initialisation_allocateur_buffer(struct_processus *s_etat_processus) { int i; if (longueur_tailles == 0) { while(tailles[longueur_tailles] != 0) { longueur_tailles++; } } if (((*s_etat_processus).cache_buffer = sys_malloc(((size_t) longueur_tailles) * sizeof(unsigned char **))) == NULL) { (*s_etat_processus).erreur_systeme = d_es_allocation_memoire; return; } if (((*s_etat_processus).pointeur_cache_buffer = sys_malloc(((size_t) longueur_tailles) * sizeof(int))) == NULL) { (*s_etat_processus).erreur_systeme = d_es_allocation_memoire; return; } for(i = 0; i < longueur_tailles; i++) { if (((*s_etat_processus).cache_buffer[i] = sys_malloc(TAILLE_CACHE * sizeof(unsigned char *))) == NULL) { (*s_etat_processus).erreur_systeme = d_es_allocation_memoire; return; } (*s_etat_processus).pointeur_cache_buffer[i] = 0; } (*s_etat_processus).pointeur_enveloppes_buffers = 0; return; } /* ================================================================================ Libération des structures de données de l'allocateur ================================================================================ Entrée : état du processus courant (contient les données nécessaires au fonctionnement de l'allocateur par thread) -------------------------------------------------------------------------------- Sortie : néant -------------------------------------------------------------------------------- Effets de bord : néant ================================================================================ */ void liberation_allocateur_buffer(struct_processus *s_etat_processus) { int i; int j; for(i = 0; i < longueur_tailles; i++) { for(j = 0; j < (*s_etat_processus).pointeur_cache_buffer[i]; j++) { sys_free((*s_etat_processus).cache_buffer[i][j]); } sys_free((*s_etat_processus).cache_buffer[i]); } sys_free((*s_etat_processus).cache_buffer); sys_free((*s_etat_processus).pointeur_cache_buffer); for(i = 0; i < (*s_etat_processus).pointeur_enveloppes_buffers; i++) { sys_free((*s_etat_processus).enveloppes_buffers[i]); } return; } /* ================================================================================ Allocation d'un buffer et de son enveloppe ================================================================================ Entrée : état du processus courant, longueur du buffer -------------------------------------------------------------------------------- Sortie : néant -------------------------------------------------------------------------------- Effets de bord : néant ================================================================================ */ struct_buffer * allocation_buffer(struct_processus *s_etat_processus, size_t longueur) { int classe; struct_buffer *s_buffer; if ((s_buffer = allocation_enveloppe_buffer(s_etat_processus)) == NULL) { (*s_etat_processus).erreur_systeme = d_es_allocation_memoire; return(NULL); } if (longueur == 0) { (*s_buffer).buffer = NULL; classe = -1; } else { classe = recherche_longueur_buffer_optimale(longueur); if (classe >= 0) { // La classe correspond à la longueur effectivement disponible // dans le buffer alloué. Or il faut ajouter à ce buffer un // pointeur vers l'enveloppe (struct_buffer *). if ((*s_etat_processus).pointeur_cache_buffer[classe] > 0) { (*s_buffer).buffer = (*s_etat_processus).cache_buffer[classe] [--(*s_etat_processus).pointeur_cache_buffer[classe]]; } else { if (((*s_buffer).buffer = sys_malloc((tailles[classe] * sizeof(unsigned char)) + sizeof(struct_buffer *))) == NULL) { (*s_etat_processus).erreur_systeme = d_es_allocation_memoire; return(NULL); } } } else { if (((*s_buffer).buffer = sys_malloc((((size_t) longueur) * sizeof(unsigned char)) + sizeof(struct_buffer *))) == NULL) { (*s_etat_processus).erreur_systeme = d_es_allocation_memoire; return(NULL); } } } (*s_buffer).classe = classe; (*s_buffer).longueur_requise = longueur; // (*s_buffer).buffer est un pointeur sur un 'unsigned char *' et se // compose d'un pointeur vers s_buffer suivi d'une zone variable. // s_buffer est le pointeur sur l'enveloppe. // (*s_buffer).buffer est le pointeur sur le début de la zone allouée // aux données. (*(((struct_buffer **) (*s_buffer).buffer))) = s_buffer; return(s_buffer); } /* ================================================================================ Libération d'un buffer et de son enveloppe ================================================================================ Entrée : état du processus courant, longueur du buffer -------------------------------------------------------------------------------- Sortie : néant -------------------------------------------------------------------------------- Effets de bord : néant ================================================================================ */ void liberation_buffer(struct_processus *s_etat_processus, struct_buffer *s_buffer) { if ((*s_buffer).classe < 0) { if ((*s_buffer).buffer != NULL) { sys_free((*s_buffer).buffer); } } else { if ((*s_etat_processus).pointeur_cache_buffer[(*s_buffer).classe] < TAILLE_CACHE) { (*s_etat_processus).cache_buffer[(*s_buffer).classe] [(*s_etat_processus).pointeur_cache_buffer [(*s_buffer).classe]++] = (*s_buffer).buffer; } else { sys_free((*s_buffer).buffer); } } liberation_enveloppe_buffer(s_etat_processus, s_buffer); return; } /* ================================================================================ Allocation d'un buffer et de son enveloppe. Le pointeur retourné est le pointeur sur le début de la zone utilisable pour être conforme au malloc() de la libc. ================================================================================ Entrée : état du processus courant, longueur du buffer -------------------------------------------------------------------------------- Sortie : pointeur sur un void -------------------------------------------------------------------------------- Effets de bord : néant ================================================================================ */ void * rpl_malloc(struct_processus *s_etat_processus, size_t s) { struct_buffer *s_buffer; void *pointeur; if (pthread_mutex_lock(&((*s_etat_processus).mutex_allocation_buffer)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(NULL); } if ((s_buffer = allocation_buffer(s_etat_processus, s)) == NULL) { pthread_mutex_unlock(&((*s_etat_processus).mutex_allocation_buffer)); (*s_etat_processus).erreur_systeme = d_es_allocation_memoire; return(NULL); } pointeur = (*s_buffer).buffer + sizeof(struct_buffer *); if (pthread_mutex_unlock(&((*s_etat_processus).mutex_allocation_buffer)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(NULL); } return(pointeur); } /* ================================================================================ Réallocation d'un buffer et de son enveloppe. Le pointeur retourné est le pointeur sur le début de la zone utilisable pour être conforme au malloc() de la libc. ================================================================================ Entrée : état du processus courant, longueur du buffer -------------------------------------------------------------------------------- Sortie : pointeur sur un void -------------------------------------------------------------------------------- Effets de bord : néant ================================================================================ */ void * rpl_realloc(struct_processus *s_etat_processus, void *ptr, size_t s) { struct_buffer *s_ancien_buffer; struct_buffer *s_nouveau_buffer; size_t longueur_copie; void *pointeur; if (ptr == NULL) { return(rpl_malloc(s_etat_processus, s)); } if (pthread_mutex_lock(&((*s_etat_processus).mutex_allocation_buffer)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(NULL); } if ((s_nouveau_buffer = allocation_buffer(s_etat_processus, s)) == NULL) { pthread_mutex_unlock(&((*s_etat_processus).mutex_allocation_buffer)); (*s_etat_processus).erreur_systeme = d_es_allocation_memoire; return(NULL); } s_ancien_buffer = (*((struct_buffer **) (ptr - sizeof(struct_buffer *)))); longueur_copie = ((*s_ancien_buffer).longueur_requise > s) ? s : (*s_ancien_buffer).longueur_requise; memcpy((*s_nouveau_buffer).buffer + sizeof(struct_buffer *), (*s_ancien_buffer).buffer + sizeof(struct_buffer *), longueur_copie); liberation_buffer(s_etat_processus, s_ancien_buffer); pointeur = (*s_nouveau_buffer).buffer + sizeof(struct_buffer *); if (pthread_mutex_unlock(&((*s_etat_processus).mutex_allocation_buffer)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(NULL); } return(pointeur); } /* ================================================================================ Libération d'un buffer et de son enveloppe. ================================================================================ Entrée : état du processus courant, longueur du buffer -------------------------------------------------------------------------------- Sortie : pointeur sur un void -------------------------------------------------------------------------------- Effets de bord : néant ================================================================================ */ void rpl_free(struct_processus *s_etat_processus, void *ptr) { struct_buffer *s_buffer; if (ptr == NULL) { return; } s_buffer = (*((struct_buffer **) (ptr - sizeof(struct_buffer *)))); if (pthread_mutex_lock(&((*s_etat_processus).mutex_allocation_buffer)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return; } liberation_buffer(s_etat_processus, s_buffer); if (pthread_mutex_unlock(&((*s_etat_processus).mutex_allocation_buffer)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return; } return; } // Réécriture des fonctions malloc() et free() de la libc void * sys_malloc(size_t s) { return(malloc(s)); } void sys_free(void *ptr) { free(ptr); return; } // vim: ts=4