MINOR: hlua_fcn: add Patref:commit() method

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.
This commit is contained in:
Aurelien DARRAGON 2024-11-19 16:40:20 +01:00
parent e769d8f426
commit 8bce7ff854
3 changed files with 88 additions and 2 deletions

View 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

View 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 {

View 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
*/