]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: hlua_fcn: add Patref:commit() method
authorAurelien DARRAGON <adarragon@haproxy.com>
Tue, 19 Nov 2024 15:40:20 +0000 (16:40 +0100)
committerAurelien DARRAGON <adarragon@haproxy.com>
Fri, 29 Nov 2024 06:23:08 +0000 (07:23 +0100)
commit() method may be used to commit pending updates on the local patref
object:

hlua_patref flags were added:
 HLUA_PATREF_FL_GEN means the patref object has been updated
 and it is associated to a new revision (curr_gen) in order to prepare
 and commit the pending updates.

upon commit, the pattern API is leveraged with curr_gen as revision to
commit new object items. Once commit is performed, previous (pending)
revisions that are older than the committed one are cleaned up (similar
to what's done with commit on the cli). Also, Patref function APIs now
take into account curr_gen to perform lookups.

doc/lua-api/index.rst
include/haproxy/hlua-t.h
src/hlua_fcn.c

index 9ce713045cad7ff927f56b7bc14bcc43e0eba7a7..f9d629d0f6fe5972e6eef6fcd500e642d6382c3a 100644 (file)
@@ -3456,6 +3456,17 @@ Patref class
   :returns: true if the pattern reference is used to handle maps instead
    of acl, false otherwise.
 
+.. js:function:: Patref.commit(ref)
+
+  Tries to commit pending Patref object updates, that is updates made to the
+  local object will be committed to the underlying patter reference storage
+  in an atomic manner upon success. Upon failure, local pending updates are
+  lost. Upon success, all other pending updates on the pattern reference
+  (e.g.: "prepare" from the cli or from other Patref Lua objects) started
+  before the new one will be pruned.
+
+  :returns: true on success and nil on failure (followed by an error message).
+
 .. _applethttp_class:
 
 AppletHTTP class
index 0d9abd5503e74c72fc5fee8465e86409cd5ebfc4..397f1ccb827d03ec835cf4d7179cc48f191fa8ae 100644 (file)
@@ -234,9 +234,17 @@ struct hlua_server_list_iterator_context {
        struct proxy *px;
 };
 
+#define HLUA_PATREF_FL_NONE    0x00
+#define HLUA_PATREF_FL_GEN     0x01 /* patref update backed by specific subset, check curr_gen */
+
 /* pat_ref struct wrapper for lua */
 struct hlua_patref {
+       /* no need for lock-protecting the struct, it is not meant to
+        * be used by parallel lua contexts
+        */
        struct pat_ref *ptr;
+       uint16_t flags; /* HLUA_PATREF_FL_* */
+       unsigned int curr_gen; /* relevant if HLUA_PATREF_FL_GEN is set */
 };
 
 struct hlua_patref_iterator_context {
index 02b82c5a960e5c602b7c5ee5a7970c29f5d4d902..573f18ec24978bb732e28148cc7189cba72a2fb1 100644 (file)
@@ -2641,6 +2641,59 @@ int hlua_patref_is_map(lua_State *L)
        return 1;
 }
 
+/* full-clear may require yielding between pruning
+ * batches
+ */
+static int _hlua_patref_clear(lua_State *L, int status, lua_KContext ctx)
+{
+       struct hlua_patref *ref = hlua_checkudata(L, 1, class_patref_ref);
+       unsigned int from = lua_tointeger(L, 2);
+       unsigned int to = lua_tointeger(L, 3);
+       int ret;
+
+ loop:
+       HA_RWLOCK_WRLOCK(PATREF_LOCK, &ref->ptr->lock);
+       ret = pat_ref_purge_range(ref->ptr, from, to, 100);
+       HA_RWLOCK_WRUNLOCK(PATREF_LOCK, &ref->ptr->lock);
+       if (!ret) {
+               hlua_yieldk(L, 0, 0, _hlua_patref_clear, TICK_ETERNITY, HLUA_CTRLYIELD); // continue
+               /* never reached, unless if called from body/init state
+                * where yieldk is no-op, thus we can't do anything to prevent
+                * thread contention
+                */
+               goto loop;
+       }
+
+       lua_pushboolean(L, 1);
+       return 1; // end
+}
+
+int hlua_patref_commit(lua_State *L)
+{
+       struct hlua_patref *ref;
+       int ret;
+
+       ref = hlua_checkudata(L, 1, class_patref_ref);
+       BUG_ON(!ref);
+
+       if (!(ref->flags & HLUA_PATREF_FL_GEN))
+               return hlua_error(L, "Nothing to do");
+
+       ref->flags &= ~HLUA_PATREF_FL_GEN;
+       ret = pat_ref_commit(ref->ptr, ref->curr_gen);
+
+       if (ret)
+               return hlua_error(L, "Commit failed");
+
+       /* cleanup: prune previous generations: The range of generations
+        * that get trashed by a commit starts from the opposite of the
+        * current one and ends at the previous one.
+         */
+       lua_pushinteger(L, ref->curr_gen - ((~0U) >> 1)); // from
+       lua_pushinteger(L, ref->curr_gen - 1); // to
+       return _hlua_patref_clear(L, LUA_OK, 0);
+}
+
 void hlua_fcn_new_patref(lua_State *L, struct pat_ref *ref)
 {
        struct hlua_patref *_ref;
@@ -2659,6 +2712,8 @@ void hlua_fcn_new_patref(lua_State *L, struct pat_ref *ref)
                if (!_ref)
                        luaL_error(L, "Lua out of memory error.");
                _ref->ptr = ref;
+               _ref->curr_gen = 0;
+               _ref->flags = HLUA_PATREF_FL_NONE;
                lua_pushlightuserdata(L, _ref);
                lua_rawseti(L, -2, 0);
        }
@@ -2666,6 +2721,7 @@ void hlua_fcn_new_patref(lua_State *L, struct pat_ref *ref)
        /* set public methods */
        hlua_class_function(L, "get_name", hlua_patref_get_name);
        hlua_class_function(L, "is_map", hlua_patref_is_map);
+       hlua_class_function(L, "commit", hlua_patref_commit);
 }
 
 int hlua_patref_gc(lua_State *L)
@@ -2696,7 +2752,11 @@ int hlua_listable_patref_index(lua_State *L)
 
        /* Perform pat ref element lookup by key */
        HA_RWLOCK_WRLOCK(PATREF_LOCK, &ref->ptr->lock);
-       elt = pat_ref_find_elt(ref->ptr, key);
+       if ((ref->flags & HLUA_PATREF_FL_GEN) &&
+           pat_ref_may_commit(ref->ptr, ref->curr_gen))
+               elt = pat_ref_gen_find_elt(ref->ptr, ref->curr_gen, key);
+       else
+               elt = pat_ref_find_elt(ref->ptr, key);
        if (elt == NULL) {
                HA_RWLOCK_WRUNLOCK(PATREF_LOCK, &ref->ptr->lock);
                lua_pushnil(L);
@@ -2718,12 +2778,19 @@ static int _hlua_listable_patref_pairs_iterator(lua_State *L, int status, lua_KC
        struct hlua_patref_iterator_context *hctx;
        struct pat_ref_elt *elt;
        int cnt = 0;
+       unsigned int curr_gen;
 
        context_index = lua_upvalueindex(1);
        hctx = lua_touserdata(L, context_index);
 
        HA_RWLOCK_WRLOCK(PATREF_LOCK, &hctx->ref->ptr->lock);
 
+       if ((hctx->ref->flags & HLUA_PATREF_FL_GEN) &&
+           pat_ref_may_commit(hctx->ref->ptr, hctx->ref->curr_gen))
+               curr_gen = hctx->ref->curr_gen;
+       else
+               curr_gen = hctx->ref->ptr->curr_gen;
+
        if (LIST_ISEMPTY(&hctx->bref.users)) {
                /* first iteration */
                hctx->bref.ref = hctx->ref->ptr->head.n;
@@ -2741,7 +2808,7 @@ static int _hlua_listable_patref_pairs_iterator(lua_State *L, int status, lua_KC
 
        elt = LIST_ELEM(hctx->bref.ref, struct pat_ref_elt *, list);
 
-       if (elt->gen_id != hctx->ref->ptr->curr_gen) {
+       if (elt->gen_id != curr_gen) {
                /* check if we may do something to try to prevent thread contention,
                 * unless we run from body/init state where hlua_yieldk is no-op
                 */