Subject: bug#69241: Fixed patch issues

From: Daniel Pettersson <[email-masked]>
Date: Wed, 28 Feb 2024 13:22:50 +0100

The previous patch included an large regression, where "batch" of
messages where handled as an LIFO.  This patch should fix the issue and
also removes the dependency on timer.el internal apis.  Which makes it
instead dependent on undocumented behavior that timers of equal time are
executed in LIFO order.

>From c4d5ddb9ce5cdb8c283928daf6b166e4ce5a430d Mon Sep 17 00:00:00 2001
From: Daniel Pettersson <[email-masked]>
Date: Wed, 28 Feb 2024 13:03:56 +0100
Subject: [PATCH] Jsonrpc: improve performance of process filter function

`run-at-time' keeps `timer-list' list sorted by inserting each timer
based on the timer value.  This means that `timer--time-less-p' needs is
executed ~ N*N/2 times for each N pending messages.  This means that
jsonrpc becomes unusable for connections that generate a lot messages at
the same time.

* lisp/jsonrpc.el (Version): Bump to 1.0.25
(jsonrpc--process-filter): Improve performance
---
 lisp/jsonrpc.el | 28 +++++++++++++++++++---------
 1 file changed, 19 insertions(+), 9 deletions(-)

diff --git a/lisp/jsonrpc.el b/lisp/jsonrpc.el
index 14fe0447008..5037d8c5b2b 100644
--- a/lisp/jsonrpc.el
+++ b/lisp/jsonrpc.el
@@ -4,7 +4,7 @@
 
 ;; Author: João Távora <[email-masked]>
 ;; Keywords: processes, languages, extensions
-;; Version: 1.0.24
+;; Version: 1.0.25
 ;; Package-Requires: ((emacs "25.2"))
 
 ;; This is a GNU ELPA :core package.  Avoid functionality that is not
@@ -760,10 +760,11 @@ jsonrpc--process-filter
                                 (setq message
                                       (plist-put message :jsonrpc-json
                                                  (buffer-string)))
-                                (process-put proc 'jsonrpc-mqueue
-                                             (nconc (process-get proc
-                                                                 'jsonrpc-mqueue)
-                                                    (list message)))))
+                                ;; Put new messages at the front of the queue,
+                                ;; this is correct as the order is reversed
+                                ;; before putting the timers on `timer-list'.
+                                (push message
+                                      (process-get proc 'jsonrpc-mqueue))))
                           (goto-char message-end)
                           (let ((inhibit-read-only t))
                             (delete-region (point-min) (point)))
@@ -782,11 +783,20 @@ jsonrpc--process-filter
           ;; non-locally (typically the reply to a request), so do
           ;; this all this processing in top-level loops timer.
           (cl-loop
+           ;; `timer-activate' orders timers by time, which is an
+           ;; very expensive operation when jsonrpc-mqueue is large,
+           ;; therefore the time object is reused for each timer
+           ;; created.
+           with time = (current-time)
            for msg = (pop (process-get proc 'jsonrpc-mqueue)) while msg
-           do (run-at-time 0 nil
-                           (lambda (m) (with-temp-buffer
-                                         (jsonrpc-connection-receive conn m)))
-                           msg)))))))
+           do (let ((timer (timer-create)))
+                (timer-set-time timer time)
+                (timer-set-function timer
+                                    (lambda (conn msg)
+                                      (with-temp-buffer
+                                        (jsonrpc-connection-receive conn msg)))
+                                    (list conn msg))
+                (timer-activate timer))))))))
 
 (defun jsonrpc--remove (conn id &optional deferred-spec)
   "Cancel CONN's continuations for ID, including its timer, if it exists.
-- 
2.39.3 (Apple Git-145)