Subject: bug#77942: 31.0.50; replace-region-contents gets stuck

From: Stefan Monnier <[email-masked]>
Date: Mon, 21 Apr 2025 13:23:34 -0400

> To reproduce with emacs -Q, define this function
>
> (defun elb-replace-region-contents-entry ()
>   (with-temp-buffer
>     (let ((step (apply #'concat (make-list 2000 "🙂été👶🏿 "))))
>       (dotimes (_ (/ 10000000 (length step)))
>         (insert step)))
>
>     (dotimes (_ 100)
>       (let* ((a (1+ (random (point-max))))
> 	       (b (1+ (random (point-max))))

[ Side note: This should use (+ (point-min) (random (buffer-size)))  ]

> 	     (beg (min a b))
> 	     (end (max a b)))

And this should not be necessary because `replace-region-contents`
should accept positions in any order, like `delete-region`.

> 	(replace-region-contents beg end "🙂été👶🏿 🙂été👶🏿 ")))))
>
> and evaluate
>
>   (elb-replace-region-contents-entry)
>
> This enters an (infinite?) loop

AFAICT it's not infinite.  At least, it does end for me when the buffer
is smaller (100000) and it does take more time (but still end) with
a buffer of 500000, so I think it would end *eventually*.

> that cannot be interrupted with C-g in the GUI version.

Apparently the MAX-SECS argument doesn't help either.  🙁

> If started with -nw, C-g eventually crashes Emacs,
> without printing anything on stdout or stderr.

That's even worse.

Eli wrote:
> Adding Stefan.

FWIW, this problem also shows up in Emacs-28 with:

    (require 'subr-x)
    
    (defun elb-replace-region-contents-entry ()
      (with-temp-buffer
        (let ((step (apply #'concat (make-list 2000 "🙂été👶🏿 "))))
          (dotimes (_ (/ 10000000 (length step)))
            (insert step)))
    
        (dotimes (_ 100)
          (let* ((a (+ (point-min) (random (buffer-size))))
                 (b (+ (point-min) (random (buffer-size)))))
            (message "replace-region-contents %S %S ..." a b)
            (replace-region-contents a b (lambda () "🙂été👶🏿 🙂été👶🏿 "))
            (message "replace-region-contents...done")))))

- Stefan