IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

Que faire après un échec d'alloc?


Sujet :

C

  1. #1
    Membre habitué
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Points : 144
    Points
    144
    Par défaut Que faire après un échec d'alloc?
    Du genre:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Toto * tots = malloc (count * sizeof(Toto) ;
    if (totos == NULL) {
        // et là, on fait quoi?
    }
    Au départ, j'ai mis des assert (totos != NULL), juste pour noter les endroits où je devrai plus tard faire quelque chose de plus sensé. C'est l'heure de m'occupper de ça, mais j'ai aucune idée de ce qui serait mieux. Au moins, l'assertion dit clairement quel est le problème et surtout où. A première vue, il me semble qu'un utilisateur ne peut rien faire de mieux que nous rapporter les faits et le message d'erreur. Alors, quoi?

    Dans un langage même légèrement de plus haut niveau, on ne s'occupe tout simplement pas de ces choses-là. Ca ne se produit pas (?). Ce qui signifie que si, par ex en python, un objet ne peut pas être alloué, l'utilisateur se retrouve face à un message d'erreur C légèrement reformulé pas le runtime python (ce sera une MemoryError de python si mes souvenirs sont corrects) : ça lui fait une belle jambe, c'est tout aussi "impertinent" et ça le laisse tout autant impuissant.
    J'aimerais bien trouver une stratégie sensée générale qui puisse s'appliquer à tous les cas où un défaut nous échappe, comme les relations avec le système de fichier ou les entrées-sorties en général.

    note: Je ne parle pas ici des défauts qui font en fait (ou devraient faire) partie de la logique de l'application, par exemple un fichier de config n'est pas là : il doit là y avoir une voie de secours.

    Denis

  2. #2
    Membre expert
    Avatar de kwariz
    Homme Profil pro
    Chef de projet en SSII
    Inscrit en
    Octobre 2011
    Messages
    898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet en SSII
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2011
    Messages : 898
    Points : 3 352
    Points
    3 352
    Par défaut
    Normalement tu ne peux rien y faire ... s'il n'y a plus de place, ben il n'y en a plus (à moins de pouvoir en faire toi-même et de prendre sur toi d'en libérer ailleurs). Tu peux éventuellement essayer une méthode qui requiert moins de mémoire en fallback, mais ce n'est pas très général (du genre lire pouis trier un tableau en mémoire, le tableau est trop grand pour tenir en mémoire, ton fallback : trier sur disque).
    Du coup mise à part émettre un message plus ou moins pertinent puis quitter l'application tu ne peux rien faire.

    EDIT: ah oui ... comme je l'avais déjà écrit assert ne doit pas être utilisé pour les erreurs runtime ... si tu veux un message avec le nom du fichier, la ligne ou le nom de la fonction il y a des macros pour ça.

  3. #3
    Expert confirmé
    Avatar de gerald3d
    Homme Profil pro
    Conducteur de train
    Inscrit en
    Février 2008
    Messages
    2 303
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Conducteur de train
    Secteur : Transports

    Informations forums :
    Inscription : Février 2008
    Messages : 2 303
    Points : 4 967
    Points
    4 967
    Billets dans le blog
    5
    Par défaut
    Je suis d'accord avec kwariz. Soit tu as moyen de faire autrement, travailler sur disque par exemple, soit tu ne peux pas.
    Dans le second cas ton application a la nécessité d'allouer cette mémoire. Si ce n'est pas possible tu affiche un message d'erreur sur le canal stderr et tu quittes l'application. De toute manière ton application ne peut plus fonctionner comme il faut.

  4. #4
    Membre habitué
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Points : 144
    Points
    144
    Par défaut qu'est-ce que 'assert' fait de mal?
    Il é méchan ?

    Ce n'est pas que je ne veux pas ne pas l'utiliser. Au contraire, chercher comment faire avec des macros qui vont bien me donnera l'occase de découvrir d'autres aspects du langage C.

    Mais je voudrais comprendre pourquoi je ne dois pas l'utiliser. Si afficher un message d'erreur pertinent sur stderr est le mieux qu'on peut faire, alors assert ne fait-il cela justement très bien?
    Le seul pb qui me vient à l'esprit, c'est que assert est peut-être réservé par convention aux tests. Dans ce cas, c'est gênant de le réutiliser ailleurs. Sinon, peut-être qu'il n'est pas compilé en mode release (il me semble avoir lu un truc comme ça qq part)?

    Donc, à moins qu'il y ait mieux, je compte faire la chose suivante: chercher la def de la macro assert (c'est bien une macro?) dans la stdlib, la comprendre, la reproduire, et appleler le résultat verify...

    Merci à vous,
    Denis

  5. #5
    Membre expert
    Avatar de kwariz
    Homme Profil pro
    Chef de projet en SSII
    Inscrit en
    Octobre 2011
    Messages
    898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet en SSII
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2011
    Messages : 898
    Points : 3 352
    Points
    3 352
    Par défaut
    Non, mais assert est prévu pour ... les assertions (détecter les erreurs de conceptions/programmations en phase de dev), c'est pourquoi si tu (ou un autre) recompiles le programme en définissant NDEBUG tous tes assert ne feront plus rien (pas bien).
    Un malloc qui plante n'est pas (toujours ) une erreur de programmation, mais une erreur runtime qui doit être traitée par ton propre handler au runtime.

    Il n'est pas interdit d'utiliser assert comme tu fais, c'est très fortement non recommandé (après tout le monde est libre).

    Il est tout aussi facile de se faire une macro/fonction utilisant un fprintf(stderr,...), __FILE__ __LINE__ sont standards __FUNC__ / __FUNCTION__ __PRETTYFUNCTION__ existent mais doivent dépendre d'une implémentation particulière je crois. *cf edit2


    EDIT: j'y vais un peu fort avec les asserts car on peut évidemment les laisser en release ... mais cela peut créer de la confusion pour d'autres personnes qui reliraient ou maintiendraient ton code. Un des réflexes est souvent : on enlève les assert, le code sera un peu plus rapide et c'est pas grave car on a confiance au code qui a été testé ...

    EDIT2: en fait c99 définit la macro __func__, gcc propose aussi __FUNCTION__ qui est l'identifiant de la fonction et __PRETTY_FUNCTION__ qui est la signature de la fonction.

    EDIT3: jette un coup d'oeil sur <err.h> dans la gnulibc : http://www.gnu.org/software/libc/man...-Messages.html

  6. #6
    Membre habitué
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Points : 144
    Points
    144
    Par défaut
    Citation Envoyé par kwariz Voir le message
    Non, mais assert est prévu pour ... les assertions (détecter les erreurs de conceptions/programmations en phase de dev), c'est pourquoi si tu (ou un autre) recompiles le programme en définissant NDEBUG tous tes assert ne feront plus rien (pas bien).
    Un malloc qui plante n'est pas (toujours ) une erreur de programmation, mais une erreur runtime qui doit être traitée par ton propre handler au runtime.

    Il n'est pas interdit d'utiliser assert comme tu fais, c'est très fortement non recommandé (après tout le monde est libre).

    Il est tout aussi facile de se faire une macro/fonction utilisant un fprintf(stderr,...), __FILE__ __LINE__ sont standards __FUNC__ / __FUNCTION__ __PRETTYFUNCTION__ existent mais doivent dépendre d'une implémentation particulière je crois. *cf edit2


    EDIT: j'y vais un peu fort avec les asserts car on peut évidemment les laisser en release ... mais cela peut créer de la confusion pour d'autres personnes qui reliraient ou maintiendraient ton code. Un des réflexes est souvent : on enlève les assert, le code sera un peu plus rapide et c'est pas grave car on a confiance au code qui a été testé ...

    EDIT2: en fait c99 définit la macro __func__, gcc propose aussi __FUNCTION__ qui est l'identifiant de la fonction et __PRETTY_FUNCTION__ qui est la signature de la fonction.

    EDIT3: jette un coup d'oeil sur <err.h> dans la gnulibc : http://www.gnu.org/software/libc/man...-Messages.html
    Oui, merci kwariz. Globalement, ce que tu dis là reflète ce que j'avais en tête. J'avais d'ailleurs déjà repéré ces macros-là (y compris d'ailleurs l'incertitude par rapport à __FUNCTION__ que tu évoques) pour me faire une macro de "vérification".
    Denis

  7. #7
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 917
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 917
    Points : 220 488
    Points
    220 488
    Billets dans le blog
    127
    Par défaut
    Bonjour,

    Dans certain cas (les plus complexes) lorsque une erreur d'allocation arrive (souvent pour cause de mémoire manquante), le programme peut tenter de libérer la mémoire d'autres module et de réessayé.
    Sinon, on affiche un message d'erreur (cela peut être dangereux car on ne sait jamais si une telle fonction alloue de la mémoire ...), on sauvegarde les données à sauvegarder et on quitte.

  8. #8
    Membre habitué
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Points : 144
    Points
    144
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    Bonjour,

    Dans certain cas (les plus complexes) lorsque une erreur d'allocation arrive (souvent pour cause de mémoire manquante), le programme peut tenter de libérer la mémoire d'autres module et de réessayé.
    Sinon, on affiche un message d'erreur (cela peut être dangereux car on ne sait jamais si une telle fonction alloue de la mémoire ...), on sauvegarde les données à sauvegarder et on quitte.
    << (cela peut être dangereux car on ne sait jamais si une telle fonction alloue de la mémoire ...) >>
    Ah, oui, le détail qui tue !!!
    Dans ce cas, vu qu'on va quitter, on peut tranquillement désallouer à peu près n'importe quoi. Mais une fonction générale qui traite ce cas-là ne peut pas savoir ce qui a été alloué, ni même si quoi que ce soit a été alloué (il peut y avoir dans l'abstrait un défaut mémoire matériel). Donc ce serait à chaque fonction allouante de prévoir ce que l'on peut désallouer, si tant est qu'elle ait quelque chose à désallouer. Cette tactique-là ne marche pas, visiblement...

    Il me semble que la solution générale serait d'allouer au démarrage un bloc bidon d'une taille suffisante pour assurer à coup sûr l'éventuelle allocation nécessaire pour des messages d'erreur. La fonction de traitement d'erreur désallouerait ce bloc juste avant l'écriture du message pour assurer la bonne exécution de cette écriture. Reste à savoir combien peuvent consommer des routines d'écriture, en rapport avec la taille du message, plus d'éventuels montants fixes, etc... Qu'en pensez-vous ?

    Denis

  9. #9
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 917
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 917
    Points : 220 488
    Points
    220 488
    Billets dans le blog
    127
    Par défaut
    D'ailleurs, pour les jeux, ils préfèrent faire une "memory pool". On alloue toute la mémoire dont l'on a besoin dès le début, comme cela, si ça ne passe pas, on le sait dès le début. Si cela passe, alors normalement on est tranquille.
    En plus, cela permet de ne pas faire d'allocation à la volée (malloc pendant la boucle principale d'exécution), chose qui est assez couteuse.

  10. #10
    Membre habitué
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Points : 144
    Points
    144
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    D'ailleurs, pour les jeux, ils préfèrent faire une "memory pool". On alloue toute la mémoire dont l'on a besoin dès le début, comme cela, si ça ne passe pas, on le sait dès le début. Si cela passe, alors normalement on est tranquille.
    En plus, cela permet de ne pas faire d'allocation à la volée (malloc pendant la boucle principale d'exécution), chose qui est assez couteuse.
    C'est pas con.
    D'un autre côté, pour de nombreuses apps et surtout pour des trucs qui doivent tourner en arrière-plan, je pense pas qu'on puisse ainsi se permettre d'allouer le maximum dont on aura (peut-être) besoin, durent toute la durée de l'exec. C pas très sympa pour les potes . Le cas particulier des jeux est sans doute que l'utilisateur ne fait que ça, quand il joue (enfin en théorie ).

    denis

  11. #11
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 130
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 130
    Points : 33 063
    Points
    33 063
    Billets dans le blog
    4
    Par défaut
    Je pense que l'erreur d'allocation se fait au cas par cas:
    - erreur à l'initialisation : y'a pas photo, l'application n'a pas réussi à allouer le minimum requis pour se lancer, on la crash et on prévient/log l'utilisateur.
    - erreur durant l'exécution, là je vois 2 cas
    > allocation nécessaire au déroulement d'une action, crash et voir ci-dessus
    > allocation à l'initialisation d'un module pour une action particulière : on prévient l'utilisateur que le module n'a pas pu être démarrer, mais inutile de crasher l'application complète.

  12. #12
    Membre habitué
    Profil pro
    amateur
    Inscrit en
    Avril 2012
    Messages
    145
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : amateur
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Avril 2012
    Messages : 145
    Points : 144
    Points
    144
    Par défaut exemple de fonction de crash
    En attendant d'avoir une idée plus claire sur ce problème de besoin de mémoire durant un traitement d'erreur (qui peut justement être un défaut de mémoire...), j'ai exploré un peut les moyens std que C nous offre pour crasher proprement , et la forme que ça pourrait avoir (sorry pour l'anglais, mais des utilisateurs potentiels n'ont aucune raison d'être particulièrement francophones, alors pas trop le choix, désolé).
    Alors, j'ai une fonction crash() générale qui prend comme params un message, la position dans le source (à partir de macros std, dont __func__ de C99), et un "contact" (à partir de macros elles-mêmes à définir). Et une version de crash spécifique pour les binz mémoire: verify_alloc, qui appelle crash.

    Voilà ce qu'un crash de verify_alloc donne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    *** error ********************************************************
    System could not provide necessary memory.
    position: function test_VERIFY_ALLOC (in toolkit.c, line 118)
    PLease report this failure, copying 2 lines above, to:
       author of 'testapp', Foo Z. Bar (foo.bar@baz.org)
    ******************************************************************
    Pour vous donner des idées, voilà comment c'est construit (ça utilise un ou deux trucs de mon toolkit dont vous conprendrez ce qu'ils sont je crois):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    /* crash !!!
    */
    #define AUTHOR "Foo Z. Bar"
    #define EMAIL "foo.bar@baz.org"
    #define APPLICATION "testapp"
    #define CONTACT format("author of '%s', %s (%s)", \
       APPLICATION, AUTHOR, EMAIL)
     
    #define SOURCE_POS format("position: function %s (in %s, line %d)", \
       __func__, __FILE__, __LINE__)
     
    void crash (string message, string source_pos, string contact) {
       line () ;
       puts ("*** error ********************************************************");
       puts (message) ;
       puts (source_pos) ;
       puts ("PLease report this failure, copying 2 lines above, to:") ;
       printf ("   %s\n", contact) ;
       puts ("******************************************************************");
       exit (EXIT_FAILURE) ;
    }
     
    // Note: verify_alloc is a macro because it uses macro SOURCE_POS which
    // itself uses macros (__FILE__, etc) relative to current position in
    // source file. The alternative is to have verify_alloc directly take
    // SOURCE_POS as arg, but since it is used on each alloc...
    const string ALLOC_ERROR_MESSAGE = 
       "System could not provide necessary memory." ;
    #define verify_alloc(p) if ((p) == NULL) \
       crash (ALLOC_ERROR_MESSAGE, SOURCE_POS, CONTACT) ;
    Le but est donc d'avoir:
    * crash comme modèle général pour toutes les anomalies qu'on ne peut pas (ou veut pas) gérer soi-même,
    * verify_alloc, exemple typique, utilisé après chaque alloc.

    Tout commentaire bienvenu, autant sur la conception que l'implémentation.
    (Ma première modif va être justement de réserver au démarrage genre 1 kb pour la construction du message d'erreur de crash, et le libérer au départ de crash.)

    Merci,
    Denis

  13. #13
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 483
    Points : 13 684
    Points
    13 684
    Billets dans le blog
    1
    Par défaut
    Mais une fonction générale qui traite ce cas-là ne peut pas savoir ce qui a été alloué, ni même si quoi que ce soit a été alloué (il peut y avoir dans l'abstrait un défaut mémoire matériel).
    Il n'y a justement pas de cas général, tout dépend de ton application et de quelle partie de l'application plante. C'est comme quand tu reviens des courses et ton frigo est plein : tu commences par jeter ce qui peut l'être ; si toujours pas de place, tu sacrifies des trucs déjà au frais mais moins importants que ce tu viens d'acheter ; si tout est important, tu pleures et tu manges en entier le pack de 6 cônes glacés que tu viens d'acheter ( = tu quittes l'application en sauvegardant ce qui peut l'être ).

    Pour reprendre l'analogie du jeu, si tu manques de mémoire pour afficher le menu d'accueil, termine l'application. Si c'est pendant l'enregistrement des meilleurs scores, osef :tu mets un message d'erreur à l'utilisateur, il sera peut-être dégouté, le jeu continue.

    Il y a encore une solution non évoquée plus haut il me semble. Souviron34 en avait parlé dans un thread similaire : tu peux attendre et retenter plus tard, avec un peu de chance il y aura de la mémoire disponible.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [KUBUNTU] Que faire après l'installation ?
    Par hackeur57 dans le forum Ubuntu
    Réponses: 19
    Dernier message: 29/12/2006, 22h48
  2. Que faire aprés un BTS?
    Par s3phi dans le forum Emploi
    Réponses: 7
    Dernier message: 03/07/2006, 12h21
  3. Réponses: 4
    Dernier message: 19/01/2006, 16h58
  4. Que faire apres une licence professionnelle??
    Par com800 dans le forum Etudes
    Réponses: 2
    Dernier message: 21/04/2005, 12h33
  5. Que faire apres un Bachelor en developpement web
    Par Turtle dans le forum Etudes
    Réponses: 9
    Dernier message: 12/03/2005, 19h35

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo