16

Quoting from an answer to Typesetting the entire Song That Never Ends

I ran this code once, and aborted the process after it reported producing something like 47,000 pages (in a very short amount of time). Unfortunately, my computer complained that the resulting pdf file was "damaged" and could not be opened. A better (perhaps impossible?) answer would produce a file such that the output could actually be viewed once the process was killed (or died).

This got me curious (and it is merely a curiosity). Can TeX die gracefully? Since its output is a PDF, presumably it needs to do some non-trivial stuff at the end to ensure that the PDF is valid so merely killing TeX is too abrupt.

A more graceful method would be for TeX to ask at each shipout "Shall I continue?", but that suffers from the other extreme: it is too intrusive as the user needs to keep giving commands to keep TeX going.

Off the top of my head, the only way I can think of to communicate with a running process in a non-blocking way is via a file. At each shipout (or other), TeX can check for the existence of a file and continue if it doesn't exist, and if it does then read the file and carry out whatever actions said file contains (thus allowing for more refined communication).

Is there a better way?

Andrew Stacey
  • 153,724
  • 43
  • 389
  • 751
  • 2
    As per usual, I have absolutely no idea how to tag this one. – Andrew Stacey Jan 30 '13 at 10:56
  • Suggested tag: {to-TeX-or-not-to-TeX}. ;) – Count Zero Jan 30 '13 at 11:01
  • 3
    I think 'DVI mode' is the plan here, as each shipped out page can normally be viewed even if the run terminates – Joseph Wright Jan 30 '13 at 11:11
  • @JosephWright So then I can "kill" it and know that up to the previous shipout is viewable. But what about the more refined version? Suppose I want TeX to take some action other than dying after receiving a signal. – Andrew Stacey Jan 30 '13 at 15:11
  • @AndrewStacey Just tell it not to die:-) – David Carlisle Jan 30 '13 at 15:24
  • 1
    JFTR: If you hit ctrl-c and then x then also pdftex will produce a valid PDF. – Stephan Lehmke Jan 30 '13 at 16:37
  • @StephanLehmke I was hoping no-one would mention that - I just found it out when testing David's solution. I claim I was misled by my client, y'honour. (In that I assumed that that was what the person in the linked question had tried.) – Andrew Stacey Jan 30 '13 at 16:46
  • (not having tested) I assume the 47,000 pages caused some sort of overflow and the PDF was invalid because of that... – Stephan Lehmke Jan 30 '13 at 17:02
  • 1
    @StephanLehmke Oddly enough, I haven't tested it up to that many pages either! (Hopefully my question expands sufficiently on the original "die nicely" to survive this astonishing revelation.) – Andrew Stacey Jan 30 '13 at 17:07
  • @StephanLehmke I don't know which client Andrew uses but when stopping a nonstopmode run with C-cC-k from within emacs, the dvi/pdf is corrupted and can not be opened via xdvi or a pdf viewer. However running from the command line, ctrl-c and then X does the trick as you said. –  Jan 31 '13 at 10:29
  • @jfbu Of course if you explicitly kill the TeX process, the PDF will be invalid. The same happens when killing the process from the console with kill. But in this case no amount of interaction will save you. When you abort by hitting X, you'll see the "postamble" in the console, loading fonts and such. So TeX is definitely finishing gracefully. – Stephan Lehmke Feb 01 '13 at 07:37

3 Answers3

10

If you set \pausing=1, TeX will stop after each line of input read from the file.

  • What does "stop" mean in this context? Will it halt completely, or will it wait for some "please continue" signal? Either way, it seems to suffer from one of the issues I don't want as I basically want to be able to asynchronously send a signal to TeX. – Andrew Stacey Jan 30 '13 at 15:09
  • @AndrewStacey: It will wait for user input. :-) – Martin Schröder Jan 30 '13 at 15:28
  • Right, so this is an implementation of my "graceful" method. The problem with this one is that I have to keep telling it to continue. What it would enable is having a controlling script which actually handled the signals and told TeX to keep going unless the script received the right signal. So, useful, but not quite what I was looking for. – Andrew Stacey Jan 30 '13 at 15:31
8

enter image description here

Run this file

\def\resume{\let\foo\relax}

\count0=0
\def\foo{\advance\count0 1 \foo}

\foo\relax\relax\relax\relax

hello I reached \the\count0 before you stopped me

\bye

For a bit then stop it at the terminal (control-c in most shells) and enter i\resume and it will finish cleanly:

 pdftex oops
This is pdfTeX, Version 3.1415926-2.4-1.40.13 (TeX Live 2012)
 restricted \write18 enabled.
entering extended mode
(./oops.tex
! Interruption.
\foo ->\advance \count 0 1
                           \foo 
l.7 \foo
        \relax\relax\relax\relax
? i\resume
[18672624{/usr/local/texlive/2012/texmf-var/fonts/map/pdftex/updmap/pdftex.map}
] )</usr/local/texlive/2012/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb
>
Output written on oops.pdf (1 page, 14709 bytes).
Transcript written on oops.log.
David Carlisle
  • 757,742
  • I like it! Quick question: why all the \relaxing? – Andrew Stacey Jan 30 '13 at 15:29
  • the \relax aren't needed in this version plan a was to stop the loop with a gobble command but depending on when exactly you hit ^C you needed to eat a different number of tokens so I stuck in some more so gobblefour was always safe but \let is better – David Carlisle Jan 30 '13 at 15:33
6

Maybe setting a timer in \everypar? I used \pdfelapsedtime as shown in my post at Benchmarking various operations of TeX. In the MWE that illustrates the technique, I have used a simple

\ifdim\result sp <100000sp  \fibo \else Finish\fi

to do the time check. Code is rough and just a concept.

\documentclass[a4paper]{article}
\usepackage{fp}   
\begin{document}
    \makeatletter
  \pdfresettimer 

\newcount\numbertimes
    \newcount\numone
    \newcount\numtwo 
    \newcount\savenumone
\def\fibonacci#1{..
    \numbertimes=2\numone=0
    \numtwo=1
    \loop
       \advance\numone  by  \numtwo  
       \savenumone=\the\numone  \numone=\numtwo  \numtwo=\savenumone 
       \advance\numbertimes  by 1 \ifnum \numbertimes<#1
    \repeat  
    \ifnum\numbertimes=#1 \advance\numone  by   \numtwo\fi 
    }



\def\fibo{%
\fibonacci{30}\fibonacci{30}\fibonacci{30}\fibonacci{30}
\fibonacci{30}\fibonacci{30}\fibonacci{30}\fibonacci{30}
\fibonacci{30}\fibonacci{30}\fibonacci{30}\fibonacci{30}
\fibonacci{30}\fibonacci{30}\fibonacci{30}\fibonacci{30}
\fibonacci{30}\fibonacci{30}\fibonacci{30}\fibonacci{30}
\fibonacci{30}\fibonacci{30}\fibonacci{30}\fibonacci{30}
\fibonacci{30}\fibonacci{30}\fibonacci{30}\fibonacci{30}
\fibonacci{30}\fibonacci{30}\fibonacci{30}\fibonacci{30}
\fibonacci{30}\fibonacci{30}\fibonacci{30}\fibonacci{30}
\fibonacci{30}\fibonacci{30}\fibonacci{30}\fibonacci{30}
\fibonacci{30}\fibonacci{30}\fibonacci{30}\fibonacci{30}
\fibonacci{30}\fibonacci{30}\fibonacci{30}\fibonacci{30}
\fibonacci{30}\fibonacci{30}\fibonacci{30}\fibonacci{30}
\fibonacci{30}\fibonacci{30}\fibonacci{30}\fibonacci{30}
\fibonacci{30}\fibonacci{30}\fibonacci{30}\fibonacci{30}
\fibonacci{30}\fibonacci{30}\fibonacci{30}\fibonacci{30}
\fibonacci{30}\fibonacci{30}\fibonacci{30}\fibonacci{30}
\fibonacci{30}\fibonacci{30}\fibonacci{30}\fibonacci{30}
}

\fibo\fibo\fibo\fibo\fibo\fibo\fibo\fibo\fibo\fibo


\FPset\result{\the\pdfelapsedtime}
\FPmul\result{\result}{100} 
\FPround\result{\result}{16}
\result\thinspace 


\ifdim\result sp <100000sp  \fibo \else Finish\fi


\makeatother


\end{document}

Change the "Finish" to \enddocument to terminate.

yannisl
  • 117,160