Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Attendre la fin de l'exécution d'un processus asynchrone

13 réponses
Avatar
Julien Gilli
Bonsoir,

J'essaie, en utilisant emacs lisp et emacs 21, d'exécuter un processus
asynchrone et d'attendre jusqu'à sa terminaison pour récupérer sa sortie
complète.

Pour cela, j'essaie d'utiliser une "sentinele" avec
set-process-sentinel, et une variable globale qui me permet de
déterminer quand l'exécution du processus s'est terminée.

Voici la partie du code concernée :

(defun process-terminated-sentinel (process event)
(message event)
(cond
((not (equal (string-match "finished" event) nil))
(setq process-termination-flag 1))
)
)

(defun x509-decode-cert (cert)
(setq my-new-proc
(start-process "tmp-openssl"
"x509-decode-result"
"openssl" "x509" "-inform" "PEM" "-text" "-noout"))
(process-send-string my-new-proc cert)
(process-send-eof my-new-proc)
(process-send-eof my-new-proc)
(set-process-sentinel my-new-proc 'process-terminated-sentinel)
(setq process-termination-flag 0)
(accept-process-output my-new-proc)
(while (= process-termination-flag 0)
(accept-process-output my-new-proc)
)
(message "process terminated !")
(save-current-buffer
(set-buffer (process-buffer my-new-proc))
(setq output (buffer-string))
(kill-buffer (process-buffer my-new-proc))
)
output
)

La fonction x509-decode-cert prend un certificat au format PEM en entree
(une chaine de caracteres) et renvoie une chaine contenant la sortie de
l'execution de la commande openssl permettant de décoder ce certificat
pour rendre son contenu lisible facilement.

Le comportement que j'obtiens n'est pas celui que j'attends. Il
semblerait que la variable "process-termination-flag" ne soit jamais
modifée, alors que l'évenement "finished" est bien reçu par la
sentinelle : lorsque je presse ctrl-g, le message "finished" apparait
bien dans le mini-buffer.

Si je ne prends pas garde à ce que le processus soit terminé avant de
renvoyer sa sortie, j'obtiens la sortie complète, et je pourrais
considérer que mon code fonctionne. Cependant, le code ne me parait pas
être assez robuste sans utiliser de sentinelle : il serait possible de
renvoyer la sortie partielle du processus avant que ce dernier ne soit
terminé.

Quelle est la bonne façon de récupérer _toute_ la sortie d'une processus
exécuté de manière asynchrone de manière robuste ? Il semblerait que
Xemacs propose une fonction intégrée "process-live-p" qui détermine si
un processus est toujours en cours d'exécution, est-ce qu'une telle
fonctionnalité existe pour Emacs ?

Bonne soirée.

--
Julien Gilli

10 réponses

1 2
Avatar
Matthieu Moy
Julien Gilli writes:

(while (= process-termination-flag 0)
(accept-process-output my-new-proc)
)



L'emacs-lisp n'est pas préemptif ! Avec une boucle comme celle là, tu
as une boucle infinie ...

Si tu tiens à faire une boucle d'attente de ce style, C-h f sit-for RET,
mais ça me semble être une drôle de façon de faire : Si tu veux que
ton code s'arrête et reprenne quand le processus est terminé, c'est un
processus synchrone qu'il te faut. Sinon, met le code que tu aurais
mis à la fin de ta fonction dans la sentinelle.

--
Matthieu
Avatar
Julien Gilli
Matthieu Moy wrote:
Julien Gilli writes:

(while (= process-termination-flag 0)
(accept-process-output my-new-proc)
)



L'emacs-lisp n'est pas préemptif !



C'est à dire ? Il ne permet pas la programmation concurrente ? Ou bien
Emacs ne préempte pas les tâches s'exécutant de manière concurrente et
c'est au code s'exécutant dans la boucle while de rendre volontairement
la main périodiquement à emacs pour que la sentinelle puisse paramétrer
le bon flag ?

Si tu tiens à faire une boucle d'attente de ce style, C-h f sit-for RET,
mais ça me semble être une drôle de façon de faire : Si tu veux que
ton code s'arrête et reprenne quand le processus est terminé, c'est un
processus synchrone qu'il te faut.



Apparemment, toutes les fonctions exécutant un processus de manière
synchrone ne permettent pas d'envoyer des données dans l'entrée standard
d'une processus. Il faudrait alors créer un fichier temporaire, exécuter
le processus de manière synchrone et enfin supprimer le fichier
temporaire. Si c'est possible, je préfère ne pas créer de fichier
temporaire.

Sinon, met le code que tu aurais
mis à la fin de ta fonction dans la sentinelle.



Est-ce que cela signifie que le code de sentinelle est exécuté de
manière synchrone à un instant précis ? Je pensais qu'une sentinelle
était appellée de manière asynchrone à chaque fois qu'un processus
recevait un évenement particulier. Si c'était le cas, positionner un
flag dans le code de la sentinelle ou y exécuter le code qui est censé
l'être lorsqu'un flag est positionné devrait être équivalent.

Merci pour votre attention !

--
Julien Gilli
Avatar
Matthieu Moy
Julien Gilli writes:

L'emacs-lisp n'est pas préemptif !



C'est à dire ? Il ne permet pas la programmation concurrente ?



Tu ne peux pas faire grand chose au niveau programmation concurrente:
Il n'y a pas de parallelisme réel. Une tache ne peut commencer à
s'executer que quand la précédante a terminé.

Ou bien Emacs ne préempte pas les tâches s'exécutant de manière
concurrente et c'est au code s'exécutant dans la boucle while de
rendre volontairement la main périodiquement à emacs pour que la
sentinelle puisse paramétrer le bon flag ?



Tout à fait. Cf. sit-for.

Apparemment, toutes les fonctions exécutant un processus de manière
synchrone ne permettent pas d'envoyer des données dans l'entrée standard
d'une processus.



Ça parait relativement logique : Emacs se bloque en attendant la fin
du processus, donc il ne peut rien faire en attendant.

Sinon, met le code que tu aurais
mis à la fin de ta fonction dans la sentinelle.



Est-ce que cela signifie que le code de sentinelle est exécuté de
manière synchrone à un instant précis ?



Qu'entends-tu par « synchrone » ou « asynchrone » dans ce contexte ?
En générale, quand on dit « asynchrone » pour ce genre de choses,
c'est pour dire « retourne à l'appelant avant d'avoir terminé », mais
là l'appelant, c'est Emacs (pas ton code) et on s'en fout un peu ...

Je pensais qu'une sentinelle était appellée de manière asynchrone à
chaque fois qu'un processus recevait un évenement particulier. Si
c'était le cas, positionner un flag dans le code de la sentinelle ou
y exécuter le code qui est censé l'être lorsqu'un flag est
positionné devrait être équivalent.



Si tu met le code dans la sentinelle, il sera executé quand le
processus termine, mais rien en attendant. Si tu fais une boucle
d'attente, ton Emacs continue de faire des choses en attendant, c'est
beaucoup moins efficace.

Regardes le nombre de processus qui tournent sur ta machine (sur la
mienne, au moment ou je parle, il y en a 94) et le nombre de processus
vraiment actifs en moyenne (0.1 sur ma machine au moment ou je parle).
Imagine ce qu'il se passerait si les 93 processus en attente passive
faisaient un "while (!event) {};" à la place ...

--
Matthieu
Avatar
Matthieu Moy
Julien Gilli writes:

Si tu met le code dans la sentinelle, il sera executé quand le
processus termine, mais rien en attendant.



Ce qui signifie que Emacs est tout de même capable de préempter mon
code, non ?



Non. La tâche « sentinelle » devient éligible quand ton processus
termine. Elle est executée quand la tâche en cours termine (enfin,
plus exactement, quand son tour vient dans la file des tâches
éligibles).

Si tu fais une boucle
d'attente, ton Emacs continue de faire des choses en attendant, c'est
beaucoup moins efficace.



Oui, j'en conviens, j'essaie juste de comprendre comment utiliser les
outils mis à ma disposition par Emacs pour écrire un code robuste.



Mais ou est le problème en utilisant la sentinelle directement ?

--
Matthieu
Avatar
Matthieu Moy
Julien Gilli writes:

Matthieu Moy wrote:
Julien Gilli writes:

Si tu met le code dans la sentinelle, il sera executé quand le
processus termine, mais rien en attendant.



Ce qui signifie que Emacs est tout de même capable de préempter mon
code, non ?


Non. La tâche « sentinelle » devient éligible quand ton processus
termine. Elle est executée quand la tâche en cours termine (enfin,
plus exactement, quand son tour vient dans la file des tâches
éligibles).



Apparemment, je ne spécifie nulle part dans mon code volontairement que
je donne la main à Emacs pour exécuter le code de la fonction
sentinelle.



Oui, mais justement, ça ne marche pas. Ta sentinelle n'est executée
que quand tu fais un C-g, donc quand tu interromp la boucle active à
la main, et un peu brutalement.

http://www.tac.nyc.ny.us/manuals/elisp/elisp_484.html#SEC487
«  A sentinel runs only while Emacs is waiting »

Qu'entendez vous par "utiliser la sentinelle directement" ?



Mettre le code à executer après la fin du processus directement dans
la sentinelle.

--
Matthieu
Avatar
drkm
Julien Gilli wrote:

J'essaie, en utilisant emacs lisp et emacs 21, d'exécuter un processus
asynchrone et d'attendre jusqu'à sa terminaison pour récupérer sa s ortie
complète.



Je n'ai pas réellement suivit en détail la discussion, mais
pourquoi veux-tu utiliser un processus asynchrone dans ce cas ?

--drkm
Avatar
Matthieu Moy
Julien Gilli writes:

Matthieu Moy wrote:
Julien Gilli writes:



Qu'entendez vous par "utiliser la sentinelle directement" ?





Mettre le code à executer après la fin du processus directement dans
la sentinelle.



Dans ce cas, suis-je assuré du fait que le processus asynchrone soit
terminé à la sortie de la fonction x509-decode-cert ?



Non :-(

J'ai enfin compris ton problème (je suis un peu long à la détente ;-) ).

En d'autres termes, existe-il un moyen pour etre assuré qu'avant
l'exécution d'une certaine ligne de code, un processus crée de
manière asynchrone soit terminé, sans effectuer de boucle active ?



La manière « standard » dans ce cas, c'est de passer un callback à
`x509-decode-cert'. C'est un peu chiant, mais je ne connais pas de
meilleur moyen.

--
Matthieu
Avatar
Julien Gilli
drkm wrote:
Julien Gilli wrote:


J'essaie, en utilisant emacs lisp et emacs 21, d'exécuter un processus
asynchrone et d'attendre jusqu'à sa terminaison pour récupérer sa sortie
complète.




Je n'ai pas réellement suivit en détail la discussion, mais
pourquoi veux-tu utiliser un processus asynchrone dans ce cas ?



Parce qu'il semblerait que ce soit le seul moyen de pouvoir lui envoyer
des données en entrée sans utiliser un fichier temporaire.

--
Julien Gilli
Avatar
Matthieu Moy
Julien Gilli writes:

Je ne vois pas comment un callback pourrait résoudre mon problème.
Pouvez vous détailler un peu plus votre idée s'il vous plaît ?



Tu voudrais faire qqch comme:

(defun ma-fonction ()
...
(x509-decode-cert toto)
(message "titi"))

Tu peux remplacer ça par

(defun ma-fonction
...
(x509-decode-cert toto (lambda () (message "titi"))))

Et quelque chose comme ça:

(defun x509-decode-cert (arg1 callback)
...
(set-process-sentinel ...
`(lambda ()
(funcall ,callback)))
...)

--
Matthieu
Avatar
Matthieu Moy
Julien Gilli writes:

Matthieu Moy wrote:
[...]
Tu peux remplacer ça par
(defun ma-fonction
...
(x509-decode-cert toto (lambda () (message "titi"))))
Et quelque chose comme ça:
(defun x509-decode-cert (arg1 callback)
...
(set-process-sentinel ...
`(lambda ()
(funcall ,callback)))
...)



Comment cela garantit qu'après l'appel à x509-decode-cert, l'exécution
d'openssl est terminée ?



Ça ne te le garantie pas, mais ça te permet d'executer du code après
la fin de processus. C'est ce que tu veux, non ?

--
Matthieu
1 2