/* ================================================================================ RPL/2 (R) version 4.1.32 Copyright (C) 1989-2020 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" /* ================================================================================ Routine de gestion du cache des objets ================================================================================ Entrée : -------------------------------------------------------------------------------- Sortie : -------------------------------------------------------------------------------- Effets de bords : néant ================================================================================ */ static inline struct_arbre_variables_partagees * allocation_noeud_partage(struct_processus *s_etat_processus) { struct_arbre_variables_partagees *objet; if ((*s_etat_processus).pointeur_variables_partagees_noeud > 0) { objet = (*s_etat_processus).variables_partagees_noeud [--(*s_etat_processus).pointeur_variables_partagees_noeud]; } else { objet = malloc(sizeof(struct_arbre_variables_partagees)); } return(objet); } static inline void liberation_noeud_partage(struct_processus *s_etat_processus, struct_arbre_variables_partagees *objet) { if ((*s_etat_processus).pointeur_variables_partagees_noeud < TAILLE_CACHE) { (*s_etat_processus).variables_partagees_noeud [(*s_etat_processus).pointeur_variables_partagees_noeud++] = objet; } else { free(objet); } return; } static inline struct_arbre_variables_partagees ** allocation_tableau_noeuds_partages(struct_processus *s_etat_processus) { struct_arbre_variables_partagees **objet; if ((*s_etat_processus).pointeur_variables_tableau_noeuds_partages > 0) { objet = (*s_etat_processus).variables_tableau_noeuds_partages [--(*s_etat_processus) .pointeur_variables_tableau_noeuds_partages]; } else { objet = malloc(((size_t) (*s_etat_processus) .nombre_caracteres_variables) * sizeof(struct_arbre_variables_partagees *)); } return(objet); } static inline void liberation_tableau_noeuds_partages(struct_processus *s_etat_processus, struct_arbre_variables_partagees **objet) { if ((*s_etat_processus).pointeur_variables_tableau_noeuds_partages < TAILLE_CACHE) { (*s_etat_processus).variables_tableau_noeuds_partages [(*s_etat_processus) .pointeur_variables_tableau_noeuds_partages++] = objet; } else { free(objet); } return; } /* ================================================================================ Routine de création d'une nouvelle variable partagee ================================================================================ Entrée : -------------------------------------------------------------------------------- Sortie : -------------------------------------------------------------------------------- Effets de bords : néant ================================================================================ */ logical1 creation_variable_partagee(struct_processus *s_etat_processus, struct_variable_partagee *s_variable) { int i; struct_arbre_variables_partagees *l_variable_courante; struct_liste_variables_partagees *l_nouvel_element; unsigned char *ptr; // Ajout de la variable en tête de la liste des variables partagées if ((l_nouvel_element = malloc(sizeof(struct_liste_variables_partagees))) == NULL) { (*s_etat_processus).erreur_systeme = d_es_allocation_memoire; return(d_erreur); } if (((*l_nouvel_element).variable = malloc(sizeof( struct_variable_partagee))) == NULL) { (*s_etat_processus).erreur_systeme = d_es_allocation_memoire; return(d_erreur); } (*(*l_nouvel_element).variable) = (*s_variable); (*l_nouvel_element).pid = getpid(); (*l_nouvel_element).tid = pthread_self(); if (pthread_mutex_lock(&mutex_liste_variables_partagees) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } (*l_nouvel_element).suivant = (*(*s_etat_processus) .l_liste_variables_partagees); (*l_nouvel_element).precedent = NULL; if ((*(*s_etat_processus).l_liste_variables_partagees) != NULL) { (**(*s_etat_processus).l_liste_variables_partagees).precedent = l_nouvel_element; } (*(*s_etat_processus).l_liste_variables_partagees) = l_nouvel_element; // Ajout de la variable à la feuille de l'arbre des variables partagees if ((*(*s_etat_processus).s_arbre_variables_partagees) == NULL) { if (((*(*s_etat_processus).s_arbre_variables_partagees) = allocation_noeud_partage(s_etat_processus)) == NULL) { if (pthread_mutex_unlock(&mutex_liste_variables_partagees) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } (*s_etat_processus).erreur_systeme = d_es_allocation_memoire; return(d_erreur); } (**(*s_etat_processus).s_arbre_variables_partagees).feuille = NULL; (**(*s_etat_processus).s_arbre_variables_partagees) .noeuds_utilises = 0; (**(*s_etat_processus).s_arbre_variables_partagees).indice_tableau_pere = -1; (**(*s_etat_processus).s_arbre_variables_partagees).noeud_pere = NULL; INITIALISATION_MUTEX((**(*s_etat_processus).s_arbre_variables_partagees) .mutex_feuille); if (((**(*s_etat_processus).s_arbre_variables_partagees).noeuds = allocation_tableau_noeuds_partages(s_etat_processus)) == NULL) { if (pthread_mutex_unlock(&mutex_liste_variables_partagees) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } (*s_etat_processus).erreur_systeme = d_es_allocation_memoire; return(d_erreur); } for(i = 0; i < (*s_etat_processus).nombre_caracteres_variables; i++) { (**(*s_etat_processus).s_arbre_variables_partagees).noeuds[i] = NULL; } } l_variable_courante = (*(*s_etat_processus).s_arbre_variables_partagees); ptr = (*s_variable).nom; if (pthread_mutex_lock(&((*l_variable_courante).mutex_feuille)) != 0) { if (pthread_mutex_unlock(&mutex_liste_variables_partagees) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } while((*ptr) != d_code_fin_chaine) { BUG((*s_etat_processus).pointeurs_caracteres_variables[*ptr] < 0, uprintf("Variable=\"%s\", (*ptr)='%c'\n", (*s_variable).nom, *ptr)); if ((*l_variable_courante).noeuds[(*s_etat_processus) .pointeurs_caracteres_variables[*ptr]] == NULL) { // Le noeud n'existe pas encore, on le crée et on le marque // comme utilisé dans la structure parente. if (((*l_variable_courante).noeuds[(*s_etat_processus) .pointeurs_caracteres_variables[*ptr]] = allocation_noeud_partage(s_etat_processus)) == NULL) { if (pthread_mutex_unlock(&mutex_liste_variables_partagees) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } (*s_etat_processus).erreur_systeme = d_es_allocation_memoire; return(d_erreur); } (*l_variable_courante).noeuds_utilises++; // La feuille est par défaut vide et aucun élément du tableau noeuds // (les branches qui peuvent être issues de ce nouveau noeud) // n'est encore utilisée. (*(*l_variable_courante).noeuds[(*s_etat_processus) .pointeurs_caracteres_variables[*ptr]]).feuille = NULL; (*(*l_variable_courante).noeuds[(*s_etat_processus) .pointeurs_caracteres_variables[*ptr]]).noeuds_utilises = 0; // Le champ noeud_pere de la structure créée pointe sur // la structure parente et l'indice tableau_pere correspond à la // position réelle dans le tableau noeuds[] de la structure parente // du noeud courant. Cette valeur sera utilisée lors de la // destruction du noeud pour annuler le pointeur contenu dans // le tableau noeuds[] de la structure parente. (*(*l_variable_courante).noeuds[(*s_etat_processus) .pointeurs_caracteres_variables[*ptr]]).noeud_pere = l_variable_courante; (*(*l_variable_courante).noeuds[(*s_etat_processus) .pointeurs_caracteres_variables[*ptr]]) .indice_tableau_pere = (*s_etat_processus) .pointeurs_caracteres_variables[*ptr]; INITIALISATION_MUTEX((*(*l_variable_courante).noeuds [(*s_etat_processus).pointeurs_caracteres_variables[*ptr]]) .mutex_feuille); // Allocation du tableau noeuds[] et initialisation à zéro de // tous les pointeurs. if (((*(*l_variable_courante).noeuds[(*s_etat_processus) .pointeurs_caracteres_variables[*ptr]]).noeuds = allocation_tableau_noeuds_partages(s_etat_processus)) == NULL) { if (pthread_mutex_unlock(&mutex_liste_variables_partagees) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } (*s_etat_processus).erreur_systeme = d_es_allocation_memoire; return(d_erreur); } for(i = 0; i < (*s_etat_processus).nombre_caracteres_variables; i++) { (*(*l_variable_courante).noeuds[(*s_etat_processus) .pointeurs_caracteres_variables[*ptr]]).noeuds[i] = NULL; } } if (pthread_mutex_lock(&((*(*l_variable_courante).noeuds [(*s_etat_processus).pointeurs_caracteres_variables[*ptr]]) .mutex_feuille)) != 0) { if (pthread_mutex_unlock(&mutex_liste_variables_partagees) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } if (pthread_mutex_unlock(&((*l_variable_courante).mutex_feuille)) != 0) { if (pthread_mutex_unlock(&mutex_liste_variables_partagees) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } l_variable_courante = (*l_variable_courante).noeuds [(*s_etat_processus).pointeurs_caracteres_variables[*ptr]]; ptr++; } if ((l_nouvel_element = malloc(sizeof(struct_liste_variables_partagees))) == NULL) { if (pthread_mutex_unlock(&mutex_liste_variables_partagees) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } (*s_etat_processus).erreur_systeme = d_es_allocation_memoire; return(d_erreur); } // Dans la feuille de l'arbre des variables partagées, on ne balaie // les variables que dans l'ordre. Le champ 'reference' est alors utilisé // pour sauvegarder une référence vers la liste des variables partagées // pour pouvoir purger l'élément en cas de besoin. (*l_nouvel_element).suivant = (*l_variable_courante).feuille; (*l_nouvel_element).precedent = NULL; if ((*l_nouvel_element).suivant != NULL) { (*(*l_nouvel_element).suivant).precedent = l_nouvel_element; } (*l_nouvel_element).reference = (*(*s_etat_processus).l_liste_variables_partagees); (*l_nouvel_element).variable = (**(*s_etat_processus) .l_liste_variables_partagees).variable; (*l_variable_courante).feuille = l_nouvel_element; (*l_nouvel_element).feuille = l_variable_courante; if (pthread_mutex_unlock(&((*l_variable_courante).mutex_feuille)) != 0) { if (pthread_mutex_unlock(&mutex_liste_variables_partagees) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } if (pthread_mutex_unlock(&mutex_liste_variables_partagees) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } return(d_absence_erreur); } /* ================================================================================ Routine de recherche d'une variable partagée ================================================================================ Entrée : -------------------------------------------------------------------------------- Sortie : -------------------------------------------------------------------------------- Effets de bords : positionne le mutex sur la variable partagée ================================================================================ */ struct_liste_variables_partagees * recherche_variable_partagee(struct_processus *s_etat_processus, unsigned char *nom_variable, union_position_variable position, unsigned char origine) { int pointeur; struct_arbre_variables_partagees *l_variable_courante; struct_liste_variables_partagees *l_element_courant; unsigned char *ptr; l_variable_courante = (*(*s_etat_processus).s_arbre_variables_partagees); ptr = nom_variable; if (l_variable_courante == NULL) { (*s_etat_processus).erreur_systeme = d_es_variable_introuvable; return(NULL); } if (pthread_mutex_lock(&((*l_variable_courante).mutex_feuille)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(NULL); } while((*ptr) != d_code_fin_chaine) { pointeur = (*s_etat_processus).pointeurs_caracteres_variables[*ptr]; if (pointeur < 0) { // Caractère hors de l'alphabet des variables pthread_mutex_unlock(&((*l_variable_courante).mutex_feuille)); (*s_etat_processus).erreur_systeme = d_es_variable_introuvable; return(NULL); } if ((*l_variable_courante).noeuds[pointeur] == NULL) { // Le chemin de la variable candidate n'existe pas. pthread_mutex_unlock(&((*l_variable_courante).mutex_feuille)); (*s_etat_processus).erreur_systeme = d_es_variable_introuvable; return(NULL); } if (pthread_mutex_lock(&((*(*l_variable_courante).noeuds[pointeur]) .mutex_feuille)) != 0) { pthread_mutex_unlock(&((*l_variable_courante).mutex_feuille)); (*s_etat_processus).erreur_systeme = d_es_processus; return(NULL); } if (pthread_mutex_unlock(&((*l_variable_courante).mutex_feuille)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(NULL); } l_variable_courante = (*l_variable_courante).noeuds[pointeur]; ptr++; } if ((*l_variable_courante).feuille != NULL) { // Il existe au moins une variable partagée du nom requis. l_element_courant = (*l_variable_courante).feuille; while(l_element_courant != NULL) { if ((*(*l_element_courant).variable).origine == 'P') { if (((*(*l_element_courant).variable).variable_partagee.adresse == position.adresse) && ((*(*l_element_courant).variable).origine == origine)) { if (pthread_mutex_lock(&((*(*l_element_courant).variable) .mutex)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(NULL); } if (pthread_mutex_unlock(&((*l_variable_courante) .mutex_feuille)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(NULL); } (*s_etat_processus).pointeur_variable_partagee_courante = (*l_element_courant).variable; return(l_element_courant); } } else { if (((*(*l_element_courant).variable).variable_partagee.pointeur == position.pointeur) && ((*(*l_element_courant).variable).origine == origine)) { if (pthread_mutex_lock(&((*(*l_element_courant).variable) .mutex)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(NULL); } if (pthread_mutex_unlock(&((*l_variable_courante) .mutex_feuille)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(NULL); } (*s_etat_processus).pointeur_variable_partagee_courante = (*l_element_courant).variable; return(l_element_courant); } } l_element_courant = (*l_element_courant).suivant; } } if (pthread_mutex_unlock(&((*l_variable_courante).mutex_feuille)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(NULL); } (*s_etat_processus).pointeur_variable_statique_courante = NULL; return(NULL); } /* ================================================================================ Routine de retrait d'une variable partagée ================================================================================ Entrée : -------------------------------------------------------------------------------- Sortie : -------------------------------------------------------------------------------- Effets de bords : néant ================================================================================ */ logical1 retrait_variable_partagee(struct_processus *s_etat_processus, unsigned char *nom_variable, union_position_variable position) { struct_liste_variables_partagees *l_element_a_supprimer; struct_liste_variables_partagees *l_element_liste_a_supprimer; logical1 erreur; if ((l_element_a_supprimer = recherche_variable_partagee(s_etat_processus, nom_variable, position, ((*s_etat_processus) .mode_execution_programme == 'Y') ? 'P' : 'E')) != NULL) { if (pthread_mutex_lock(&mutex_liste_variables_partagees) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } // (*s_etat_processus).pointeur_variable_partagee_courante // pointe sur la variable à éliminer. Cette variable est celle qui // est présente dans l'une des feuilles statiques de l'arbre des // variables. l_element_liste_a_supprimer = (*l_element_a_supprimer).reference; // Suppression de la liste des variables statiques if ((*l_element_liste_a_supprimer).precedent != NULL) { // L'élément à supprimer n'est pas le premier de la liste. (*(*l_element_liste_a_supprimer).precedent).suivant = (*l_element_liste_a_supprimer).suivant; if ((*l_element_liste_a_supprimer).suivant != NULL) { // Il y a un élément suivant. On le chaîne. (*(*l_element_liste_a_supprimer).suivant).precedent = NULL; } } else { // L'élement est le premier de la liste. S'il y a un élément // suivant, on le chaîne. if ((*l_element_liste_a_supprimer).suivant != NULL) { (*(*l_element_liste_a_supprimer).suivant).precedent = NULL; } (*(*s_etat_processus).l_liste_variables_partagees) = (*l_element_liste_a_supprimer).suivant; } free(l_element_liste_a_supprimer); // Suppression depuis la feuille statique. Le champ 'precedent' ne sert // pas car la liste est simplement chaînée. if ((*l_element_a_supprimer).precedent != NULL) { // L'élément n'est pas le premier de la liste. (*(*l_element_a_supprimer).precedent).suivant = (*l_element_a_supprimer).suivant; if ((*l_element_a_supprimer).suivant != NULL) { (*(*l_element_a_supprimer).suivant).precedent = (*l_element_a_supprimer).precedent; } else { (*(*l_element_a_supprimer).precedent).suivant = NULL; } } else { // L'élément est le premier de la liste. if ((*l_element_a_supprimer).suivant != NULL) { (*(*l_element_a_supprimer).suivant).precedent = NULL; } (*(*l_element_a_supprimer).feuille).feuille = (*l_element_a_supprimer).suivant; } liberation(s_etat_processus, (*(*l_element_a_supprimer).variable) .objet); free((*(*l_element_a_supprimer).variable).nom); pthread_mutex_unlock(&((*(*l_element_a_supprimer).variable).mutex)); pthread_mutex_destroy(&((*(*l_element_a_supprimer).variable).mutex)); free((*l_element_a_supprimer).variable); free(l_element_a_supprimer); if (pthread_mutex_unlock(&mutex_liste_variables_partagees) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } erreur = d_absence_erreur; } else { erreur = d_erreur; (*s_etat_processus).erreur_systeme = d_es_variable_introuvable; } return(erreur); } /* ================================================================================ Routine de retrait des variables partagées ================================================================================ Entrée : -------------------------------------------------------------------------------- Sortie : -------------------------------------------------------------------------------- Effets de bords : néant ================================================================================ */ // Cette routine libère toutes les variables partagées de niveau non // nul, donc attachées à une expression et non un programme. logical1 retrait_variables_partagees_locales(struct_processus *s_etat_processus) { struct_liste_variables_partagees *l_element_courant; struct_liste_variables_partagees *l_element_suivant; unsigned char registre_mode_execution; if (pthread_mutex_lock(&mutex_liste_variables_partagees) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } registre_mode_execution = (*s_etat_processus).mode_execution_programme; l_element_courant = (*(*s_etat_processus).l_liste_variables_partagees); while(l_element_courant != NULL) { l_element_suivant = (*l_element_courant).suivant; (*s_etat_processus).mode_execution_programme = ((*(*l_element_courant).variable).origine == 'P') ? 'Y' : 'N'; if (((*(*l_element_courant).variable).niveau > 0) && ((*l_element_courant).pid == getpid()) && (pthread_equal((*l_element_courant).tid, pthread_self()) != 0)) { if (retrait_variable_partagee(s_etat_processus, (*(*l_element_courant).variable).nom, (*(*l_element_courant).variable).variable_partagee) == d_erreur) { if (pthread_mutex_unlock(&mutex_liste_variables_partagees) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } (*s_etat_processus).mode_execution_programme = registre_mode_execution; return(d_erreur); } } l_element_courant = l_element_suivant; } (*s_etat_processus).mode_execution_programme = registre_mode_execution; if (pthread_mutex_unlock(&mutex_liste_variables_partagees) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return(d_erreur); } return(d_absence_erreur); } /* ================================================================================ Routine de libération de l'arbre des variables partagées ================================================================================ Entrée : -------------------------------------------------------------------------------- Sortie : -------------------------------------------------------------------------------- Effets de bords : positionne le mutex sur la variable partagée ================================================================================ */ void liberation_arbre_variables_partagees(struct_processus *s_etat_processus, struct_arbre_variables_partagees *arbre) { int i; struct_liste_variables_partagees *l_element_partage_courant; struct_liste_variables_partagees *l_element_partage_suivant; // Libération de l'arbre des variables. Le contenu des variables n'est // pas détruit par cette opération, il sera détruit lors de la libération // de la liste des variables par niveau. if (arbre == NULL) { return; } if (pthread_mutex_lock(&((*arbre).mutex_feuille)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return; } l_element_partage_courant = (*arbre).feuille; while(l_element_partage_courant != NULL) { l_element_partage_suivant = (*l_element_partage_courant).suivant; if (pthread_mutex_lock(&((*(*l_element_partage_courant).variable) .mutex)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return; } free((*(*l_element_partage_courant).variable).nom); liberation(s_etat_processus, (*(*l_element_partage_courant) .variable).objet); free((*l_element_partage_courant).variable); if (pthread_mutex_unlock(&((*(*l_element_partage_courant).variable) .mutex)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return; } if (pthread_mutex_destroy(&((*(*l_element_partage_courant).variable) .mutex)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return; } free(l_element_partage_courant); l_element_partage_courant = l_element_partage_suivant; } for(i = 0; i < (*s_etat_processus).nombre_caracteres_variables; i++) { if ((*arbre).noeuds[i] != NULL) { liberation_arbre_variables_partagees(s_etat_processus, (*arbre).noeuds[i]); (*arbre).noeuds[i] = NULL; } } liberation_tableau_noeuds_partages(s_etat_processus, (*arbre).noeuds); liberation_noeud_partage(s_etat_processus, arbre); if (pthread_mutex_unlock(&((*arbre).mutex_feuille)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return; } if (pthread_mutex_destroy(&((*arbre).mutex_feuille)) != 0) { (*s_etat_processus).erreur_systeme = d_es_processus; return; } arbre = NULL; return; } // vim: ts=4