Subject: Re: bug#69611: 30.0.50; Long bidi line with control characters freezes Emacs

From: Stephen Berman <[email-masked]>
Date: Thu, 07 Mar 2024 18:52:20 +0100

On Thu, 07 Mar 2024 17:42:44 +0200 Eli Zaretskii <[email-masked]> wrote:

>> Date: Thu, 07 Mar 2024 14:42:37 +0100
>> From:  Stephen Berman via "Bug reports for GNU Emacs,
>>  the Swiss army knife of text editors" <[email-masked]>
>>
>> When I visited a certain elisp file generated by a program of mine and
>> type `M-v', it took some time (see below for details) for the display to
>> scroll to 4% from the top (according to the mode line) and then there
>> was no further change and Emacs froze, using 100% of a CPU core.  I
>> found no way to unfreeze it within Emacs and after about 15 minutes
>> terminated the emacs process from the shell.  This is reliably
>> reproducible with this file.
>>
>> The file in question is only about 50k bytes long, but it contains one
>> line of more than 37k characters, consisting of a mix of ASCII and
>> non-ASCII characters, including properly shaped Arabic script.  The file
>> itself has base paragraph direction LTR.
>>
>> Most of the Arabic words in this file are enclosed in the bidirectional
>> control characters POP DIRECTIONAL FORMATTING (#x202c) and RIGHT-TO-LEFT
>> EMBEDDING (#x202b).  I did not add these characters, but I had
>> copy-&-pasted most of the Arabic from a PDF file I did not create.  I
>> don't know if PDFs of Arabic text normally contain these control
>> characters, but the consequences for Emacs were dramatic.  When I simply
>> visited this file in Emacs (started with -Q) there was an immediate
>> slowdown, and in top I could see Emacs using 100% of a CPU thread.  I
>> ran `M-: (benchmark-run nil (end-of-buffer))' on this file, and the
>> result was:
>>
>> (27.962602113 2 0.0226042269999999977)
>
> This is a crazy file.  UBA, the Unicode Bidirectional Algorithm,
> allows the RLE..PDF embeddings to nest.  The nesting is allowed to be
> up to 125 deep(!), but I have never seen a text file using more than a
> couple of nested embeddings.  This file goes up to 111 nested
> embedding levels!  Moreover, quite a few embeddings are invalid: there
> are 1021 RLE control characters in this file, but only 971 PDF
> controls, so they don't pair as they should.  This causes the
> reordering algorithm to examine extremely long stretches of characters
> each time we need to redisplay even a small portion of the window,
> because reordering must always find where each nested level ends to do
> its job.
>
> My suggestion is to remove all the RLE and PDF controls from the file.
> They are not needed, not in Emacs anyway.  I'm guessing the program
> which created this file uses bidi controls because it wants to be
> compatible with incomplete implementations of the UBA, which don't
> support implicit embedding levels (those cause by bidirectional
> properties of characters, as opposed to explicit bidi controls like
> RLE and PDF).  With full UBA implementations, the bidi controls are
> needed only when the reordering using implicit levels produces wrong
> results, which is quite rare.

Indeed, I had already come to the conclusion that I don't need those
controls before I decided to raise the problem I encountered with them.
I've now checked a number of PDFs I have that contain Arabic script, and
in all of those from which I was able to yank Arabic script from the PDF
as Arabic script into Emacs (with some PDFs that wasn't possible), each
Arabic word was enclosed in the control characters.  So that appears to
be standard or at least common with PDF.  Being now aware of this, I can
take care to remove any control characters from yanked text in future.

In the case of the file I sent you, I may be to blame for the unbalanced
control characters: after yanking the Arabic into Emacs, I did some
editing of it and may well have unintentionally deleted some of the
control characters.  At the time I wasn't even aware of these; only
after (re)reading the section on bidirectional display in the Elisp
manual did I enable glyphless-display-mode and saw the characters, but I
didn't bother to check if they paired up properly.

>> The display of the benchmark result only appeared in the echo area after
>> more than a minute (I timed it with a stopwatch).  At that point the
>> mode line showed the buffer at 4% from the top, and the display remained
>> frozen afterwards.  After several minutes during which Emacs consumed
>> 100% CPU, and I had switched the focus away from the Emacs frame, the
>> CPU consumption stopped, but as soon as I switch focus back to that
>> frame, it went back to 100%.  The display never changed from showing the
>> buffer at 4%, apparently being in some kind of infinite loop.  After
>> about 15 minutes I started gdb, attached the Emacs process and produced
>> a backtrace, which I've attached, in the hope it helps to diagnose the
>> problem.
>
> The extremely deep nesting of embeddings in the file, coupled with the
> fact that the first embedding starts near the beginning of the file,
> but ends very near its end, causes the algorithm that finds where to
> position the cursor to fail, because it cannot cope with the situation
> where, after C-f or C-b, the position of point is very far outside of
> the window.  I guess this causes some infloop (even though I don't see
> it here, I just see that the cursor doesn't move although point does
> move).  It could also be just a very long calculation, not an infloop,
> because finding where to place the window-start point in this case is
> also very expensive.

Ok.  But this is only an issue in conjunction with long lines, right?
Because there is no slowdown or display issue with the file from which
this elisp file was generated: that is the file into which I yanked the
Arabic script from the PDF and subsequently edited, so it contains
unpaired control characters, but only a few of its lines are longer than
80 characters, and I think none longer than 150 or so.

>> Nevertheless, there seems to be something else besides the control
>> characters involved in this issue, because as a further test, I created
>> a buffer consisting of more than 1000 copies of the test string
>> concatenating the Arabic example in etc/HELLO and "Hello" (see bug#69385
>> for more on such test buffers), and manually enclosed each Arabic word
>> in the above control characters, but the benchmark result in this buffer
>> was not significantly different from the result without the control
>> characters (and similar to the above result for the copy of the
>> problematic file without the control characters), and the display did
>> not freeze.
>
> Yes, because you never tried such deeply-nested embeddings, and didn't
> make your embedding levels include so many characters long as this
> file does.

Indeed, I simply wrapped each Arabic word in the paired control
characters, so there's no nesting at all.  Now the difference makes
sense.

> This file is an interesting curiosity, as far as I'm concerned, but I
> doubt whether I will find enough time and motivation to try to speed
> up Emacs when such crazy files are visited.

Given the special circumstances of this file's creation I think there's
no need to spend any more time it, so unless you decide you do want to,
as far as I'm concerned this bug can be closed.  It might be beneficial
to others to document the issue briefly, either in the Elisp manual
under Bidirectional Display or just in etc/PROBLEMS, but maybe this is
such an unusual case that even that isn't worth the effort.

Thanks for looking into this and explaining it.

Steve Berman