]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: proxy: add refcount to proxies
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 13 Feb 2026 10:29:00 +0000 (11:29 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 2 Mar 2026 09:44:59 +0000 (10:44 +0100)
Implement refcount notion into proxy structure. The objective is to be
able to increment refcount on proxy to prevent its deletion temporarily.
This is similar to the server refcount : "del backend" is not blocked
and will remove the targetted instance from the global proxies_list.
However, the final free operation is delayed until the refcount is null.

As stated above, the API is similar to servers. Proxies are initialized
with a refcount of 1. Refcount can be incremented via proxy_take(). When
no longer useful, refcount is decremented via proxy_drop() which
replaces the older free_proxy(). Deinit is only performed once refcount
is null.

This commit also defines flag PR_FL_DELETED. It is set when a proxy
instance has been removed via a "del backend" CLI command. This should
serve as indication to modules which may still have a refcount on the
target proxy so that they can release it as soon as possible.

Note that this new refcount is completely ignored for a default proxy
instance. For them, proxy_take() is pure noop. Free is immediately
performed on first proxy_drop() invokation.

include/haproxy/proxy-t.h
include/haproxy/proxy.h
src/haproxy.c
src/hlua.c
src/http_client.c
src/log.c
src/proxy.c
src/resolvers.c
src/sink.c

index fcf9732bd9301d0f8ab42051885228dca7b30590..c233d2909be2403c749d071ba3d27f16c1b885a5 100644 (file)
@@ -247,6 +247,7 @@ enum PR_SRV_STATE_FILE {
 #define PR_FL_PAUSED             0x00000020  /* The proxy was paused at run time (reversible) */
 #define PR_FL_CHECKED            0x00000040  /* The proxy configuration was fully checked (including postparsing checks) */
 #define PR_FL_BE_UNPUBLISHED     0x00000080  /* The proxy cannot be targetted by content switching rules */
+#define PR_FL_DELETED            0x00000100  /* Proxy has been deleted and must be manipulated with care */
 
 struct stream;
 
@@ -508,6 +509,7 @@ struct proxy {
 
        struct guid_node guid;                  /* GUID global tree node */
        struct mt_list watcher_list;            /* list of elems which currently references this proxy instance (currently only used with backends) */
+       uint refcount;                          /* refcount to keep proxy from being deleted during runtime */
 
        EXTRA_COUNTERS(extra_counters_fe);
        EXTRA_COUNTERS(extra_counters_be);
index fa19924f71d45b4f92b737873afc7ce5c767823a..5b73b25d4015dfd2678b8c3d025429ba20120405 100644 (file)
@@ -58,7 +58,7 @@ void stop_proxy(struct proxy *p);
 int  stream_set_backend(struct stream *s, struct proxy *be);
 
 void deinit_proxy(struct proxy *p);
-void free_proxy(struct proxy *p);
+void proxy_drop(struct proxy *p);
 const char *proxy_cap_str(int cap);
 const char *proxy_mode_str(int mode);
 enum pr_mode str_to_proxy_mode(const char *mode);
@@ -82,6 +82,7 @@ void proxy_unref_defaults(struct proxy *px);
 int setup_new_proxy(struct proxy *px, const char *name, unsigned int cap, char **errmsg);
 struct proxy *alloc_new_proxy(const char *name, unsigned int cap,
                               char **errmsg);
+void proxy_take(struct proxy *px);
 struct proxy *parse_new_proxy(const char *name, unsigned int cap,
                               const char *file, int linenum,
                               const struct proxy *defproxy);
index 49640f6ebb74bb010fb20ab1a533205d2e5db9ff..9b13bd2f39cfdce50ad1d9d04d8248feeb73d7ab 100644 (file)
@@ -2814,7 +2814,7 @@ void deinit(void)
        while (p) {
                p0 = p;
                p = p->next;
-               free_proxy(p0);
+               proxy_drop(p0);
        }/* end while(p) */
 
        /* we don't need to free sink_proxies_list nor cfg_log_forward proxies since
index 52884221a98147ca0d326f96a67413fef7b64b07..764c0fb84a4ce92c2872dd56b8d88f2295088083 100644 (file)
@@ -14726,7 +14726,7 @@ static void hlua_deinit()
                        lua_close(hlua_states[thr]);
        }
 
-       free_proxy(socket_proxy);
+       proxy_drop(socket_proxy);
 }
 
 REGISTER_POST_DEINIT(hlua_deinit);
index 64fbc87073e61cf1fa46c130ecd8a79cbc93c4e2..762fc094680179213b02c6dbe7b7d6b8c4d0cd8d 100644 (file)
@@ -1214,7 +1214,7 @@ err:
        if (err_code & ERR_CODE) {
                ha_alert("httpclient: cannot initialize: %s\n", errmsg);
                free(errmsg);
-               free_proxy(px);
+               proxy_drop(px);
 
                return NULL;
        }
index 9fc0d86aa5d3e578e119e5a8ff79e3a8af9324f7..f26079b22b7934f0eb209bab2835e6592a49bad8 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -3739,7 +3739,7 @@ void deinit_log_forward()
        while (p) {
                p0 = p;
                p = p->next;
-               free_proxy(p0);
+               proxy_drop(p0);
        }
 }
 
index f9241ea1deb1ba171a15ccc59b964e0e44d04732..04982b7055d383dafb52d161a3d99eef2cd9680f 100644 (file)
@@ -456,9 +456,19 @@ void deinit_proxy(struct proxy *p)
        proxy_unref_defaults(p);
 }
 
-/* deinit and free <p> proxy */
-void free_proxy(struct proxy *p)
+/* Decrement <p> refcount and free it if null. For a default proxy instance,
+ * refcount is ignored and free is immediately performed.
+ */
+void proxy_drop(struct proxy *p)
 {
+       if (!p)
+               return;
+
+       if (!(p->cap & PR_CAP_DEF)) {
+               if (HA_ATOMIC_SUB_FETCH(&p->refcount, 1))
+                       return;
+       }
+
        deinit_proxy(p);
        ha_free(&p);
 }
@@ -3211,6 +3221,8 @@ struct proxy *alloc_new_proxy(const char *name, unsigned int cap, char **errmsg)
        if (!setup_new_proxy(curproxy, name, cap, errmsg))
                goto fail;
 
+       proxy_take(curproxy);
+
  done:
        return curproxy;
 
@@ -3225,6 +3237,13 @@ struct proxy *alloc_new_proxy(const char *name, unsigned int cap, char **errmsg)
        return NULL;
 }
 
+/* Increment <px> refcount. Does nothing for a default proxy instance. */
+void proxy_take(struct proxy *px)
+{
+       if (!(px->cap & PR_CAP_DEF))
+               HA_ATOMIC_INC(&px->refcount);
+}
+
 /* post-check for proxies */
 static int proxy_postcheck(struct proxy *px)
 {
@@ -4955,8 +4974,8 @@ static int cli_parse_add_backend(char **args, char *payload, struct appctx *appc
        return 1;
 
  err:
-       /* free_proxy() ensures any potential refcounting on defpx is decremented. */
-       free_proxy(px);
+       /* This ensures any potential refcounting on defpx is decremented. */
+       proxy_drop(px);
        thread_release();
 
        if (msg) {
@@ -5053,6 +5072,8 @@ static int cli_parse_delete_backend(char **args, char *payload, struct appctx *a
                goto out;
        }
 
+       px->flags |= PR_FL_DELETED;
+
        thread_release();
 
        ha_notice("Backend deleted.\n");
index b8306d33df82dda2f723da8333895f89a04feb30..5aa2a595f37f899c650673b0dd8a508bcc4ed983 100644 (file)
@@ -2637,7 +2637,7 @@ static void resolvers_destroy(struct resolvers *resolvers)
                resolv_free_resolution(res);
        }
 
-       free_proxy(resolvers->px);
+       proxy_drop(resolvers->px);
        free(resolvers->id);
        free((char *)resolvers->conf.file);
        task_destroy(resolvers->t);
@@ -3586,7 +3586,7 @@ out:
 err_free_conf_file:
        ha_free((void **)&r->conf.file);
 err_free_p:
-       free_proxy(p);
+       proxy_drop(p);
 err_free_r:
        ha_free(&r);
        return err_code;
index 03f57abd8b327ec6181ca3d6278483dcd3cf8cff..e207db871e58e58391314d3d238207450819b50a 100644 (file)
@@ -818,7 +818,7 @@ static void sink_free(struct sink *sink)
        }
        LIST_DEL_INIT(&sink->sink_list); // remove from parent list
        task_destroy(sink->forward_task);
-       free_proxy(sink->forward_px);
+       proxy_drop(sink->forward_px);
        ha_free(&sink->name);
        ha_free(&sink->desc);
        while (sink->sft) {
@@ -866,7 +866,7 @@ static struct sink *sink_new_ringbuf(const char *id, const char *description,
        return sink;
 
  err:
-       free_proxy(p);
+       proxy_drop(p);
        return NULL;
 }