Subject: bug#73199: url-retrieve is not thread-safe

From: Michael Albinus <[email-masked]>
Date: Mon, 30 Sep 2024 18:42:55 +0200

Eli Zaretskii <[email-masked]> writes:

>> Comments?
> ENOPATCH

Oops, sorry. I've appended the vc-dir buffer, not the vc-diff ...

diff --git a/lisp/url/url-http.el b/lisp/url/url-http.el
index 184c1278072..019ba2d7e57 100644
--- a/lisp/url/url-http.el
+++ b/lisp/url/url-http.el
@@ -74,7 +74,9 @@ url-http-proxy-basic-auth-storage

 (defvar url-http-open-connections (make-hash-table :test 'equal
 						   :size 17)
-  "A hash table of all open network connections.")
+  "A hash table of all open network connections.
+If Emacs is compiled with thread support, the key is a list `(host port
+thread)'.  Otherwise, it is a cons cell `(host . port)'.")

 (defvar url-http-version "1.1"
   "What version of HTTP we advertise, as a string.
@@ -154,26 +156,33 @@ url-http-debug
   (apply #'url-debug 'http args))

 (defun url-http-mark-connection-as-busy (host port proc)
-  (url-http-debug "Marking connection as busy: %s:%d %S" host port proc)
-  (set-process-query-on-exit-flag proc t)
-  (puthash (cons host port)
-	      (delq proc (gethash (cons host port) url-http-open-connections))
-	      url-http-open-connections)
-  proc)
+  (let ((key (if main-thread
+                 (list host port (funcall 'current-thread)) (cons host port))))
+    (url-http-debug "Marking connection as busy: %s:%d %S" host port proc)
+    (set-process-query-on-exit-flag proc t)
+    (puthash key
+             (delq proc (gethash key url-http-open-connections))
+	     url-http-open-connections)
+    proc))

 (defun url-http-mark-connection-as-free (host port proc)
-  (url-http-debug "Marking connection as free: %s:%d %S" host port proc)
-  (when (memq (process-status proc) '(open run connect))
-    (set-process-buffer proc nil)
-    (set-process-sentinel proc 'url-http-idle-sentinel)
-    (set-process-query-on-exit-flag proc nil)
-    (puthash (cons host port)
-	     (cons proc (gethash (cons host port) url-http-open-connections))
-	     url-http-open-connections))
-  nil)
+  (let ((key (if main-thread
+                 (list host port (funcall 'current-thread)) (cons host port))))
+    (url-http-debug "Marking connection as free: %s:%d %S" host port proc)
+    (when (memq (process-status proc) '(open run connect))
+      (set-process-buffer proc nil)
+      (set-process-sentinel proc 'url-http-idle-sentinel)
+      (set-process-query-on-exit-flag proc nil)
+      (puthash key
+	       (cons proc (gethash key url-http-open-connections))
+	       url-http-open-connections))
+    nil))

 (defun url-http-find-free-connection (host port &optional gateway-method)
-  (let ((conns (gethash (cons host port) url-http-open-connections))
+  (let ((conns (gethash
+                (if main-thread
+                    (list host port (funcall 'current-thread)) (cons host port))
+                url-http-open-connections))
 	(connection nil))
     (while (and conns (not connection))
       (if (not (memq (process-status (car conns)) '(run open connect)))