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 :

Limiter le temps d'execution d'une fonction


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Modérateur
    Avatar de gangsoleil
    Homme Profil pro
    Manager / Cyber Sécurité
    Inscrit en
    Mai 2004
    Messages
    10 150
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Manager / Cyber Sécurité

    Informations forums :
    Inscription : Mai 2004
    Messages : 10 150
    Par défaut Limiter le temps d'execution d'une fonction
    Bonjour,

    Je joue un peu avec des fonctions "a la con", qui pourraient bien ne pas me rendre la main aussi vite que je le souhaite. Du coup, je voudrai limiter leur temps d'execution, mais je n'y arrive pas... Je suis sous Linux et Unix, mais pas d'environnement Windows.

    Dans l'exemple suivant, je rentre bien dans le handler, mais je ne sais pas comment faire pour que celui-ci "arrete" la fonction compute. Il est bien sur impossible dans mon cas de mettre une variable globale que je testerai dans compute(), puisque dans la vraie vie, je ne maitrise pas vraiment ce que fait cette fonction.
    Bref, je cherche un moyen de sortir de compute au bout d'un temps donne, et je n'y arrive pas... Et donc si vous aviez une idee pour m'aider, ca serait super !

    Merci.

    SSCCE a compiler avec :
    gcc -O2 -W -Wall -o test test_timer.c -lrt
    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
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <errno.h>
    #include <pthread.h>
    #include <sys/ipc.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <time.h>
    #include <sys/msg.h>
    #include <sys/sem.h>
    #include <sys/shm.h>
    #include <sys/time.h>
    #include <sys/types.h>
     
    timer_t timerid;
     
    void time_handler (void)
    {
      printf ("debug in handler\n");
      timer_gettime (timerid, (struct itimerspec *)NULL);
    }
     
     
    void compute (void)
    {
      int i = 0;
      int j = 0;
     
      while (1)
      {
        i = j++;
      }
    }
     
    int main (void)
    {
      struct itimerspec max_time;
      struct sigaction signal_action;
      struct sigevent signal_event;
      int retour = 0;
     
      max_time.it_interval.tv_sec  = 0;
      max_time.it_interval.tv_nsec = 0;
      max_time.it_value.tv_sec     = 2;
      max_time.it_value.tv_nsec    = 0;
     
      signal_action.sa_handler = (void*)time_handler;
      sigemptyset (&signal_action.sa_mask);
      signal_action.sa_flags = 0;
     
      errno = 0;
      retour = sigaction (SIGUSR1, &signal_action, (struct sigaction *)NULL);
      if (retour == -1)
      {
        fprintf (stderr, "Error %d on sigaction : %s\n", errno, strerror(errno));
        exit (-1);
      }
     
      signal_event.sigev_notify = SIGEV_SIGNAL;
      signal_event.sigev_signo = SIGUSR1;
     
      if (timer_create (CLOCK_REALTIME, &signal_event, &timerid) == -1)
      {
        fprintf (stderr, "Error %d on timer creation : %s\n", errno, strerror(errno));
        exit (-1);
      }
     
      printf ("before settime\n");
      timer_settime (timerid, 0, &max_time, (struct itimerspec*)NULL);
     
      compute();
      printf ("after compute\n");
     
      return EXIT_SUCCESS;
    }
    "La route est longue, mais le chemin est libre" -- https://framasoft.org/
    Les règles du forum

  2. #2
    Modérateur
    Avatar de gangsoleil
    Homme Profil pro
    Manager / Cyber Sécurité
    Inscrit en
    Mai 2004
    Messages
    10 150
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Manager / Cyber Sécurité

    Informations forums :
    Inscription : Mai 2004
    Messages : 10 150
    Par défaut
    A force de chercher, je viens de me souvenir de setjmp et longjmp, qui pourraient me convenir si appelable avec des signaux, ce dont je n'ai pas la moindre idee...

    Alors oui, les tests fonctionnent (CF code ci-dessous), ou presque (j'ai un soucis avec le code de retour de timer_gettime, mais bon, c'est pas forcement un soucis vu que c'est un test et que c'est un "connerie" acceptable), mais je crains un peu que ce soit comme les signaux et les threads, qui font globalement tres mauvais menage...

    Est-ce que vous pensez que c'est une solution envisageable savez si setjmp/longjmp supportent l'utilisation des signaux ?
    "La route est longue, mais le chemin est libre" -- https://framasoft.org/
    Les règles du forum

  3. #3
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 442
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 442
    Par défaut
    Bonsoir,

    Citation Envoyé par gangsoleil Voir le message
    Je joue un peu avec des fonctions "a la con", qui pourraient bien ne pas me rendre la main aussi vite que je le souhaite. Du coup, je voudrai limiter leur temps d'execution, mais je n'y arrive pas... Je suis sous Linux et Unix, mais pas d'environnement Windows.

    Dans l'exemple suivant, je rentre bien dans le handler, mais je ne sais pas comment faire pour que celui-ci "arrete" la fonction compute.
    En fait, ça dépend si tu veux suspendre l'exécution pendant un moment ou si tu veux y mettre fin. Dans les deux cas, je pense que le plus simple consiste à lancer un thread et à le contrôler, voire le tuer.

    Si tu veux faire cela dans un seul fil d'exécution, il sera difficile de faire cela proprement puisque la fonction n'est pas à proprement parler une entité autonome. Vu du système, il s'agira toujours du fonctionnement normal de ton programme. Une solution consisterait alors à programmer un timer, à mettre un handler sur SIGALRM et faire un longjmp() à l'échéance.

    EDIT : Crosspost.

  4. #4
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 747
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 747
    Par défaut
    Oui mais pour moi il y a 2 questions:
    1) Est-ce qu'on peut tuer un thread quoiqu'il arrive?
    Par exemple, une "sandbox" qui est parti dans un long traitement, est-ce qu'un simple "kill" va l'arrêter, ou éventuellement va-t-elle avoir une "réaction" pas trop lente?

    2) La désallocation de la mémoire?
    Parce que si on ne sait pas ce que fait le "traitement", comment on peut être sûr qu'il n'y aura pas de fuites mémoire?
    En C++ (), c'est d'ailleurs le problème de longjmp qui n'appelle pas les destructeurs non-triviaux.

  5. #5
    Modérateur
    Avatar de gangsoleil
    Homme Profil pro
    Manager / Cyber Sécurité
    Inscrit en
    Mai 2004
    Messages
    10 150
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Manager / Cyber Sécurité

    Informations forums :
    Inscription : Mai 2004
    Messages : 10 150
    Par défaut
    Citation Envoyé par foetus Voir le message
    1) Est-ce qu'on peut tuer un thread quoiqu'il arrive?
    Quoi qu'il arrive, je ne sais pas. Dans mon cas, c'est un appel de fonction (externe) bloquant, qui reagit tres bien a un kill basique, donc ca pourrait jouer aussi.

    2) La désallocation de la mémoire?
    Parce que si on ne sait pas ce que fait le "traitement", comment on peut être sûr qu'il n'y aura pas de fuites mémoire?
    En C++ (), c'est d'ailleurs le problème de longjmp qui n'appelle pas les destructeurs non-triviaux.
    L'avantage du C, c'est que tu sais ce que tu alloues, donc normalement, tu devrais savoir ce qu'il te reste a desallouer .
    Bon, troll mis a part, c'est certain qu'il est difficile de savoir a quel moment tu quittes le thread, mais je pense que dans ce cas, l'utilisation de la virgule peut etre (vraiment) utile :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    free (mon_obj), mon_obj = NULL;
    Sauf erreur de ma part (ce qui est fort possible), c'est traite comme une operation "atomique", qui devrait donc ne pas etre arretee au milieu, meme si tu interrompts le thread. M'enfin vu l'heure, je ne miserai pas cher la-dessus.

    Dans tous les cas, merci Obsidian et Foetus pour vos reponses rapides a mon probleme a la con.
    "La route est longue, mais le chemin est libre" -- https://framasoft.org/
    Les règles du forum

  6. #6
    Membre chevronné
    Inscrit en
    Juillet 2012
    Messages
    231
    Détails du profil
    Informations forums :
    Inscription : Juillet 2012
    Messages : 231
    Par défaut
    Citation Envoyé par gangsoleil Voir le message
    Bon, troll mis a part, c'est certain qu'il est difficile de savoir a quel moment tu quittes le thread, mais je pense que dans ce cas, l'utilisation de la virgule peut etre (vraiment) utile :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    free (mon_obj), mon_obj = NULL;
    Sauf erreur de ma part (ce qui est fort possible), c'est traite comme une operation "atomique", qui devrait donc ne pas etre arretee au milieu, meme si tu interrompts le thread. M'enfin vu l'heure, je ne miserai pas cher la-dessus.
    Absolument pas.
    Au passage, le concept d'opération atomique n'existe même pas en C avant le C11.

    Sinon, pour l'approche setjmp/longjmp, fait attention. C'est très facile de tomber sur un UB.
    Et même sans aller jusque là, tu risque de leaker: si ta fonction compute alloue de la mémoire, ouvre des fichiers, ou que-sais-je, ça ne sera pas libéré.

    Citation Envoyé par Médinoc Voir le message
    Pour le coup du thread, que se passe-t-il si un thread est tué au cours d'un malloc()/free()?
    Ça dépend de comment tu le tue.

    pthread_kill est dangereux je pense, pour les raisons que tu cites (et en plus, selon le signal handler, même si tu vises un thread tu risques de tuer tout le processus...).
    pthread_cancel est plus sûr (encore que, ça dépend) car il ne va pas arrêter le thread à n'importe quel moment (il va attendre un cancellation point). Cela dit, si le thread ne passe jamais dans un cancellation point...

  7. #7
    Membre chevronné
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    1 849
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 1 849
    Par défaut
    Salut,

    Le sujet m’intéresse et j'ai donc une petite question : lorsqu'on kill un thread, l'OS est capable de désalloué la mémoire réservée par des malloc ?

  8. #8
    Modérateur
    Avatar de gangsoleil
    Homme Profil pro
    Manager / Cyber Sécurité
    Inscrit en
    Mai 2004
    Messages
    10 150
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Manager / Cyber Sécurité

    Informations forums :
    Inscription : Mai 2004
    Messages : 10 150
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    Une solution consisterait alors à programmer un timer, à mettre un handler sur SIGALRM et faire un longjmp() à l'échéance.
    Un truc comme ca ? Ca fonctionne, mais j'ai eu de (gros) soucis avec les signaux et les threads, et je ne voudrais pas me retrouver avec le meme genre de probleme lors de la mise en prod...

    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
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <errno.h>
    #include <pthread.h>
    #include <sys/ipc.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <time.h>
    #include <sys/msg.h>
    #include <sys/sem.h>
    #include <sys/shm.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <setjmp.h>
     
    timer_t timerid;
    static jmp_buf buf;
    struct itimerspec toto;
     
    void time_handler (void)
    {
      long i;
      printf ("debug in handler\n");
      i = timer_gettime (timerid, &toto);
      if (i == 0)
      {
        /* no more time */
        printf ("debug in handler 2 i %ld\n", i);
        longjmp (buf, 1);
      }
      printf ("debug in handler 3 , i %ld\n", i);
    }
     
     
    void compute (void)
    {
      int i = 0;
      int j = 0;
     
      while (1)
      {
        i = j++;
      }
    }
     
    int main (void)
    {
      struct itimerspec max_time;
      struct sigaction signal_action;
      struct sigevent signal_event;
      int retour = 0;
     
      max_time.it_interval.tv_sec  = 0;
      max_time.it_interval.tv_nsec = 0;
      max_time.it_value.tv_sec     = 2;
      max_time.it_value.tv_nsec    = 0;
     
      signal_action.sa_handler = (void*)time_handler;
      sigemptyset (&signal_action.sa_mask);
      signal_action.sa_flags = 0;
     
      errno = 0;
      retour = sigaction (SIGUSR1, &signal_action, (struct sigaction *)NULL);
      if (retour == -1)
      {
        fprintf (stderr, "Error %d on sigaction : %s\n", errno, strerror(errno));
        exit (-1);
      }
     
      signal_event.sigev_notify = SIGEV_SIGNAL;
      signal_event.sigev_signo = SIGUSR1;
     
      if (timer_create (CLOCK_REALTIME, &signal_event, &timerid) == -1)
      {
        fprintf (stderr, "Error %d on timer creation : %s\n", errno, strerror(errno));
        exit (-1);
      }
     
      printf ("before settime\n");
      timer_settime (timerid, 0, &max_time, (struct itimerspec*)NULL);
     
      if (setjmp(buf) == 0)
      {
        compute();
      };
      printf ("after compute\n");
     
      return EXIT_SUCCESS;
    }
    "La route est longue, mais le chemin est libre" -- https://framasoft.org/
    Les règles du forum

  9. #9
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 747
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 747
    Par défaut
    Et pourquoi par un truc un peu bête, si on sait que les traitements sont finis et pas trop longs
    On programme un "Thread pool" (avec tous les problèmes de paramètrages) et on donne à un thread un traitement.
    Si ce traitement est trop long, on laisse le thread de côté finir sa vie dans un état wait.

    Par contre, il faut faire une gestion parce que s'il y a trop de threads qui sont en attente, il faudra en refaire

  10. #10
    Modérateur
    Avatar de gangsoleil
    Homme Profil pro
    Manager / Cyber Sécurité
    Inscrit en
    Mai 2004
    Messages
    10 150
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Manager / Cyber Sécurité

    Informations forums :
    Inscription : Mai 2004
    Messages : 10 150
    Par défaut
    Citation Envoyé par foetus Voir le message
    si on sait que les traitements sont finis et pas trop longs
    C'est bien mon soucis : je suis dépendant de trucs externes, qui peuvent ne plus répondre (et donc bloquer indéfiniment), et c'est pour cette raison que je cherche a limiter leur temps d’exécution.

    Neanmoins, je note l’idée dans un coin (coin), car elle peut être intéressante dans un autre contexte.
    "La route est longue, mais le chemin est libre" -- https://framasoft.org/
    Les règles du forum

  11. #11
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 492
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 492
    Billets dans le blog
    1
    Par défaut
    Je ne connaissais pas setjmp et longjmp, ça peut être utile (bien que sans doute à manier avec beaucoup de précautions !).

    Je ne sais pas comment je le ferais en C, il me manque des connaissances sur les fonctions de "haut-niveau" pour faire cela, mais j'ai fait quelque chose de similaire en Java il y a peu. Voici le mode opératoire si ça peut te servir :
    - Je lance mon traitement dans un thread séparé.
    - Je demande à un objet Timer de scheduler l'annulation du traitement dans x seconds (en Java, cela passe par un TimerTask)
    - Si le traitement se termine avant, il annule le TimerTask qui devait l'annuler ; le thread porteur du traitement se termine.
    - Si le traitement dépasse le temps imparti, le TimerTask est exécuté et alors le thread porteur du traitement est arrêté.

    Cela pose problème dans ton cas si tu dois libérer de la mémoire ou si tu dois récupérer un résultat à la suite du traitement. Sinon, cela devrait correspondre.

    Mais ta dernière solution semble très bien aussi ^^

    Sauf erreur de ma part (ce qui est fort possible), c'est traite comme une operation "atomique", qui devrait donc ne pas etre arretee au milieu, meme si tu interrompts le thread
    Personnellement, j'aurai dit qu'il n'y a qu'une instruction assembleur qui ne peut pas être interrompue. Regarde cet article par exemple : http://www.blaess.fr/christophe/2012...non-atomicite/

    les signaux et les threads, qui font globalement tres mauvais menage...
    C'est un fait communément admis ou c'est ton expérience qui parle ?

Discussions similaires

  1. ajouté le temps d'execution d'une fonction
    Par maissaab dans le forum wxPython
    Réponses: 1
    Dernier message: 18/03/2011, 15h20
  2. Réponses: 4
    Dernier message: 18/05/2007, 15h37
  3. chronometrer le temps d'execution d'une fonction
    Par semaj_james dans le forum C
    Réponses: 3
    Dernier message: 17/02/2006, 15h11
  4. limit et temps d'execution avec oracle et PHP
    Par dor_boucle dans le forum Oracle
    Réponses: 20
    Dernier message: 10/12/2005, 14h31
  5. Execution d'une fonction lors de l'appui sur CTRL+ALT+I
    Par cyberlewis dans le forum Windows
    Réponses: 4
    Dernier message: 17/03/2004, 01h35

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