Commit 7303f286ca for asterisk.org

commit 7303f286ca291e1d7ec028e4e94e00ce5bf11014
Author: phoneben <3232963@gmail.com>
Date:   Mon Oct 26 14:12:15 2026 +0200

    manager.c: Don't block the AMI accept thread on busy session locks.

    manager.c: Don't block the AMI accept thread on busy session locks.

    new AMI connections can stall under load.
    purge_sessions() runs from the AMI accept loop and took every
    session's lock while looking for expired HTTP sessions. A session
    thread holding its lock (for example while writing events to a slow
    client) stalled the accept thread, so new AMI connections queued up
    and could overflow the listen queue.

    Use ao2_trylock() and skip sessions that are currently locked. Expired
    session cleanup is opportunistic; a skipped session is checked again
    on a later pass.

    Assisted-by: Claude (Anthropic AI assistant)

    Resolves: #2248

diff --git a/main/manager.c b/main/manager.c
index 6ffa7e3484..3ea06efd7d 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -7532,7 +7532,17 @@ static int purge_sessions(int n_max)

 	/* The order of operations is significant */
 	while (n_max > 0 && (session = ao2_iterator_next(&i))) {
-		ao2_lock(session);
+		/*
+		 * This runs in the AMI accept thread. Only HTTP sessions have a
+		 * session timeout, so skip non-HTTP sessions (managerid of zero)
+		 * without taking their lock. If an HTTP session is locked by another
+		 * thread, skip it rather than stall new connections; cleanup is
+		 * opportunistic and it will be examined again on a later pass.
+		 */
+		if (!session->managerid || ao2_trylock(session)) {
+			unref_mansession(session);
+			continue;
+		}
 		if (session->sessiontimeout && (now > session->sessiontimeout) && !session->inuse) {
 			if (session->authenticated
 				&& VERBOSITY_ATLEAST(2)