Aplicando um tempo limite em call-process / shell-command?

7

Em alguns casos, ao delegar trabalho a um processo externo, é útil definir um tempo limite no comando para impedir que o Emacs seja interrompido indefinidamente.
Infelizmente, o seguinte não funciona.

(with-timeout (1 nil)
  (call-process "/usr/bin/bash" nil t nil "-c" "sleep 10"))

O mesmo vale para shell-command.

Existe uma maneira de definir um tempo limite para esses processos síncronos?
Ou seja, quero que o processo seja finalizado automaticamente se não terminar dentro de um certo número de segundos. Isso é possível?

Malabarba
fonte
Eu nunca usei with-timeout, mas eu usei kill-processe delete-process.
lawlist
Não tenho certeza, mas talvez aceitar a saída do processo ajudaria. Ou seja, diria ao Emacs para tomar iniciativa e executar algum código Elisp, supostamente aguardando o processo produzir alguma saída, talvez, que também seria um bom momento para interromper o processo se o tempo limite expirasse.
wvxvw
@wvxvw A descrição call-processacima está configurando a saída a ser impressa no buffer atual (eu obtenho o mesmo efeito se eu passar em um buffer de saída diferente). É isso que você quer dizer?
Malabarba 25/03
Desculpe, eu confundi algumas coisas. Eu quis dizer isso: gnu.org/software/emacs/manual/html_node/elisp/…, mas isso é relevante apenas para os processos assíncronos. Quando estou em casa, vou procurar as call-processfontes, mas agora estou começando a suspeitar que não há como matá-lo com o tempo limite.
wvxvw
Na verdade, tentei procurar no Github: github.com/emacs-mirror/emacs/blob/… e, não, não vejo nenhum código que possa definir o tempo limite do processo.
wvxvw

Respostas:

4

Enquanto call-processestiver em execução, o emacs processará eventos, with-timeoutnão funcionará sem isso:

O tempo limite é verificado sempre que o Emacs aguarda algum tipo de evento externo (como entrada do teclado, entrada de subprocessos ou um determinado horário); se o programa for repetido sem aguardar, o tempo limite não será detectado.

Você ainda pode usar with-timeoutcom processos (semi) síncronos.

Você realmente usará um processo assíncrono, mas aguardará de forma síncrona enquanto estiver em execução, o Emacs processará eventos quando você executar sit-for, que poderá ser executado por 0 segundos. Você pode usar o argumento timeout-forms de with-timeoutpara interromper o processo se ele ainda estiver em execução quando o tempo limite for acionado.

(with-current-buffer (get-buffer-create "*my-proc-buffer*")
  (let ((proc (start-process "myproc" (current-buffer) "bash" "-c" "sleep 4"))) ;; start an async process
    (with-timeout (2 (kill-process proc)) ;; on timeout, kill the process
      (while (process-live-p proc) ;; while process is running
        (sit-for .05)) ;; let emacs read events and run timmers (and check for timeout)
      (message "finished on time!!")))) ;; this will run only if there is no timeout
Jordon Biondo
fonte
Estou tendo um pequeno problema com isso. Não é garantido que a saída dos proces esteja no buffer de saída após a conclusão do loop. Estou contornando isso chamando outro (sit-for 0.2)após o whileloop, mas isso parece instável. Existe uma maneira de saber que a saída foi impressa no buffer?
Malabarba 25/03
Não tenho certeza sobre isso. Eu diria que, se você atingir o tempo limite, o processo não for concluído de qualquer maneira, portanto a saída não será relevante. O que você está fazendo para que a saída ainda seja necessária mesmo após um tempo limite? Você pode potencialmente mudar para o uso de um filtro de processo e encerrar o processo apenas X segundos se passaram sem saída, e não após X segundos, não importa o quê.
Jordon Biondo 25/03
Desculpe, eu não estava claro. Eu não me importo com a saída quando o tempo limite é atingido. Estou dizendo que a saída não é impressa imediatamente após o processo terminar com êxito .
Malabarba 25/03
Acho que o problema é que o (sit-for 0)emacs gasta muito tempo deixando o processo rodar e não escutando o suficiente para eventos e inserindo o texto no buffer. Quando mudo o meu para as (sit-for .05)coisas funcionam melhor e todo o texto entra no meu buffer. Mesmo para um processo curto, 0,05 segundos não é uma sobrecarga preocupante.
Jordon Biondo
Não estou muito preocupado com sobrecarga nessa situação. O processo é chamado de 5 a 8 vezes e a coisa toda é uma operação interativa que já leva pelo menos alguns segundos, portanto, mais 1 segundo não fará muita diferença. O que me preocupa um pouco é a estabilidade, porque eu tive que usar (sit-for 0.2)para que meus testes locais passassem e, mesmo assim, depois que fiz as alterações, os testes travis falharam.
Malabarba 25/03
4

Você pode conseguir isso apenas adicionando uma chamada de tempo limite do GNU ao seu comando shell, o que evita a necessidade de conhecer quaisquer detalhes sobre o comportamento do Emacs. Por exemplo, executando:

$ timeout 5 sleep 10

Retornará em 5 segundos, não em 10 (o tempo limite pressiona Ctrl-C para você).

Joseph Garvin
fonte
Obrigado, talvez eu tenha que escolher esta opção se não conseguir estabilidade com a outra. No entanto, eu preferiria não confiar em outro utilitário, para que ele também funcionasse no Windows.
Malabarba 25/03