branch: elpa/jabber
commit 550d537d1acb8efc13e5f98a83296abf916a11e3
Author: Thanos Apollo <[email protected]>
Commit: Thanos Apollo <[email protected]>
sm: Count drained stanzas once
---
lisp/jabber-core.el | 6 ++----
lisp/jabber-sm.el | 12 +++++-------
lisp/jabber-util.el | 15 ++++++++++-----
tests/jabber-test-sm.el | 43 +++++++++++++++++++++++++++++++------------
4 files changed, 48 insertions(+), 28 deletions(-)
diff --git a/lisp/jabber-core.el b/lisp/jabber-core.el
index ca7c18e4ee..201a1e8195 100644
--- a/lisp/jabber-core.el
+++ b/lisp/jabber-core.el
@@ -850,8 +850,7 @@ override the defaults from `jabber-account-list'."
(jabber-send-sexp--immediate fsm sexp))
;; Drain any stanzas queued before disconnect.
(setq new-state-data
- (jabber-sm--drain-pending
- fsm new-state-data #'jabber-send-sexp--immediate))
+ (jabber-sm--drain-pending fsm new-state-data))
(list :session-established new-state-data)))
((jabber-sm--failed-p stanza)
(message "Stream Management resume failed, falling back to
auth")
@@ -915,8 +914,7 @@ override the defaults from `jabber-account-list'."
(list :session-established state-data :keep))
((jabber-sm--a-p stanza)
(setq state-data (jabber-sm--process-ack state-data
stanza))
- (setq state-data (jabber-sm--drain-pending
- fsm state-data
#'jabber-send-sexp--immediate))
+ (setq state-data (jabber-sm--drain-pending fsm state-data))
(list :session-established state-data :keep))
(t
;; Only message/presence/iq stanzas reach here; <r/>/<a/>
are
diff --git a/lisp/jabber-sm.el b/lisp/jabber-sm.el
index 5dcac0ae0b..89ffaaad42 100644
--- a/lisp/jabber-sm.el
+++ b/lisp/jabber-sm.el
@@ -40,7 +40,7 @@
(require 'jabber-xml)
(require 'fsm)
-(declare-function jabber-send-sexp--immediate "jabber-util.el" (jc sexp))
+(declare-function jabber-send-sexp--raw "jabber-util.el" (jc sexp))
(declare-function jabber-send-string "jabber-util.el" (jc string))
(defvar jabber-debug-log-xml)
@@ -232,11 +232,9 @@ Return updated STATE-DATA."
(nconc (plist-get state-data :sm-pending-queue)
(list (cons (jabber-sm--stanza-priority sexp) sexp)))))
-(defun jabber-sm--drain-pending (jc state-data send-fn)
+(defun jabber-sm--drain-pending (jc state-data)
"Send queued stanzas from pending queue up to the in-flight cap.
-JC is the connection. STATE-DATA is the FSM plist. SEND-FN is
-called with (JC SEXP) for each drained stanza and must bypass
-the back-pressure gate to avoid re-queuing.
+JC is the connection. STATE-DATA is the FSM plist.
The queue is stable-sorted by priority before draining so messages
go first, then IQs, then presence. FIFO order is preserved
within each priority class.
@@ -248,7 +246,7 @@ Return updated STATE-DATA."
(< (jabber-sm--in-flight-count state-data)
jabber-sm-max-in-flight)))
(let ((sexp (cdr (pop queue))))
- (funcall send-fn jc sexp)
+ (jabber-send-sexp--raw jc sexp)
(setq state-data (jabber-sm--count-outbound state-data sexp))))
(plist-put state-data :sm-pending-queue queue)))
@@ -285,7 +283,7 @@ recovery cycle."
(plist-get state-data :sm-outbound-count))
(plist-put state-data :sm-outbound-queue nil)
(plist-put state-data :sm-stall-since nil)
- (jabber-sm--drain-pending jc state-data #'jabber-send-sexp--immediate)))
+ (jabber-sm--drain-pending jc state-data)))
;;; Ack send/receive
diff --git a/lisp/jabber-util.el b/lisp/jabber-util.el
index d2f98d3196..4833c47b96 100644
--- a/lisp/jabber-util.el
+++ b/lisp/jabber-util.el
@@ -980,10 +980,10 @@ DATA is any sexp."
(when jabber-debug-log-xml
(jabber-process-console fsm direction data)))
-(defun jabber-send-sexp--immediate (jc sexp)
- "Send SEXP to JC immediately, bypassing the back-pressure gate.
-Log the XML, send it (with a proactive <r/> for countable stanzas
-when SM is enabled), and update the outbound counter."
+(defun jabber-send-sexp--raw (jc sexp)
+ "Send SEXP to JC without updating Stream Management state.
+Log the XML and append an <r/> request when SM is enabled for a
+countable stanza."
(condition-case e
(jabber-log-xml jc "sending" sexp)
(error
@@ -996,7 +996,12 @@ when SM is enabled), and update the outbound counter."
(jabber-sm--stanza-p sexp))))
(if sm-countable
(jabber-send-string jc (concat xml (jabber-sm--make-request-xml)))
- (jabber-send-string jc xml)))
+ (jabber-send-string jc xml))))
+
+(defun jabber-send-sexp--immediate (jc sexp)
+ "Send SEXP to JC immediately, bypassing the back-pressure gate.
+Update Stream Management state after transmitting the stanza."
+ (jabber-send-sexp--raw jc sexp)
(jabber-sm--count-outbound (fsm-get-state-data jc) sexp))
(defun jabber-send-sexp (jc sexp)
diff --git a/tests/jabber-test-sm.el b/tests/jabber-test-sm.el
index 04a7106922..ded3883909 100644
--- a/tests/jabber-test-sm.el
+++ b/tests/jabber-test-sm.el
@@ -514,9 +514,9 @@
(cons 0 msg2)
(cons 0 msg3))))
(sent nil))
- (setq sd (jabber-sm--drain-pending
- 'fake-jc sd
- (lambda (_jc sexp) (push sexp sent))))
+ (cl-letf (((symbol-function 'jabber-send-sexp--raw)
+ (lambda (_jc sexp) (push sexp sent))))
+ (setq sd (jabber-sm--drain-pending 'fake-jc sd)))
;; Should have sent exactly 2 (the cap)
(should (= (length sent) 2))
;; One remains in pending queue
@@ -525,6 +525,25 @@
;; Outbound count incremented for sent stanzas
(should (= (plist-get sd :sm-outbound-count) 2))))
+(ert-deftest jabber-test-sm-drain-pending-counts-once ()
+ "Draining counts each transmitted stanza exactly once."
+ (let* ((jabber-sm-max-in-flight 2)
+ (msg '(message ((to . "a@b")) (body () "1")))
+ (sd (list :sm-enabled t
+ :sm-outbound-count 10
+ :sm-last-acked 9
+ :sm-outbound-queue nil
+ :sm-pending-queue (list (cons 0 msg))))
+ (sent nil))
+ (cl-letf (((symbol-function 'jabber-send-sexp--raw)
+ (lambda (_jc sexp) (push sexp sent))))
+ (setq sd (jabber-sm--drain-pending 'fake-jc sd)))
+ (should (equal sent (list msg)))
+ (should (= (plist-get sd :sm-outbound-count) 11))
+ (should (= (length (plist-get sd :sm-outbound-queue)) 1))
+ (should (= (caar (plist-get sd :sm-outbound-queue)) 11))
+ (should (null (plist-get sd :sm-pending-queue)))))
+
(ert-deftest jabber-test-sm-drain-pending-empty ()
"Drain with empty queue is a no-op."
(let* ((jabber-sm-max-in-flight 10)
@@ -534,9 +553,9 @@
:sm-outbound-queue nil
:sm-pending-queue nil))
(sent nil))
- (setq sd (jabber-sm--drain-pending
- 'fake-jc sd
- (lambda (_jc sexp) (push sexp sent))))
+ (cl-letf (((symbol-function 'jabber-send-sexp--raw)
+ (lambda (_jc sexp) (push sexp sent))))
+ (setq sd (jabber-sm--drain-pending 'fake-jc sd)))
(should (null sent))
(should (null (plist-get sd :sm-pending-queue)))))
@@ -579,9 +598,9 @@
(cons 1 iq1)
(cons 0 msg2))))
(sent nil))
- (setq sd (jabber-sm--drain-pending
- 'fake-jc sd
- (lambda (_jc sexp) (push sexp sent))))
+ (cl-letf (((symbol-function 'jabber-send-sexp--raw)
+ (lambda (_jc sexp) (push sexp sent))))
+ (setq sd (jabber-sm--drain-pending 'fake-jc sd)))
(setq sent (nreverse sent))
;; Messages first (FIFO), then IQ, then presences (FIFO)
(should (= (length sent) 5))
@@ -612,9 +631,9 @@
(cons 0 msg1)
(cons 2 pres2))))
(sent nil))
- (setq sd (jabber-sm--drain-pending
- 'fake-jc sd
- (lambda (_jc sexp) (push sexp sent))))
+ (cl-letf (((symbol-function 'jabber-send-sexp--raw)
+ (lambda (_jc sexp) (push sexp sent))))
+ (setq sd (jabber-sm--drain-pending 'fake-jc sd)))
(setq sent (nreverse sent))
;; Message sent first, then one presence
(should (= (length sent) 2))