diff options
author | Stefan Metzmacher <metze@samba.org> | 2022-08-28 12:38:24 +0200 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2022-09-20 00:34:35 +0000 |
commit | 63291ea5c5d32e295b3638afd530e805ec59a190 (patch) | |
tree | 627b6ec77dbe656f54bb9f6933291ed571657d0a /source3/lib | |
parent | 6bda68910e2a2d9b986d6d65c9790684b2f15e48 (diff) | |
download | samba-63291ea5c5d32e295b3638afd530e805ec59a190.tar.gz |
s3:g_lock: add callback function to g_lock_trylock()
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15125
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
Diffstat (limited to 'source3/lib')
-rw-r--r-- | source3/lib/g_lock.c | 112 |
1 files changed, 99 insertions, 13 deletions
diff --git a/source3/lib/g_lock.c b/source3/lib/g_lock.c index 351f8ac4cfc..7e27f2ea52a 100644 --- a/source3/lib/g_lock.c +++ b/source3/lib/g_lock.c @@ -577,12 +577,92 @@ void g_lock_lock_cb_wake_watchers(struct g_lock_lock_cb_state *cb_state) cb_state->modified = true; } +static NTSTATUS g_lock_lock_cb_run_and_store(struct g_lock_lock_cb_state *cb_state) +{ + struct g_lock *lck = cb_state->lck; + NTSTATUS success_status = NT_STATUS_OK; + NTSTATUS status; + + if (cb_state->cb_fn != NULL) { + + SMB_ASSERT(lck->num_shared == 0); + SMB_ASSERT(cb_state->new_shared == NULL); + + if (cb_state->ctx->lock_order != DBWRAP_LOCK_ORDER_NONE) { + const char *name = dbwrap_name(cb_state->ctx->db); + dbwrap_lock_order_lock(name, cb_state->ctx->lock_order); + } + + cb_state->ctx->busy = true; + cb_state->cb_fn(cb_state, cb_state->cb_private); + cb_state->ctx->busy = false; + + if (cb_state->ctx->lock_order != DBWRAP_LOCK_ORDER_NONE) { + const char *name = dbwrap_name(cb_state->ctx->db); + dbwrap_lock_order_unlock(name, cb_state->ctx->lock_order); + } + } + + if (cb_state->unlock) { + /* + * Unlocked should wake up watchers. + * + * We no longer need the lock, so + * force a wakeup of the next watchers, + * even if we don't do any update. + */ + dbwrap_watched_watch_reset_alerting(cb_state->rec); + dbwrap_watched_watch_force_alerting(cb_state->rec); + if (!cb_state->modified) { + /* + * The record was not changed at + * all, so we can also avoid + * storing the lck.unique_lock_epoch + * change + */ + return NT_STATUS_WAS_UNLOCKED; + } + lck->exclusive = (struct server_id) { .pid = 0 }; + cb_state->new_shared = NULL; + + if (lck->datalen == 0) { + if (!cb_state->existed) { + return NT_STATUS_WAS_UNLOCKED; + } + + status = dbwrap_record_delete(cb_state->rec); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dbwrap_record_delete() failed: %s\n", + nt_errstr(status)); + return status; + } + return NT_STATUS_WAS_UNLOCKED; + } + + success_status = NT_STATUS_WAS_UNLOCKED; + } + + status = g_lock_store(cb_state->rec, + cb_state->lck, + cb_state->new_shared, + NULL, 0); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("g_lock_store() failed: %s\n", + nt_errstr(status)); + return status; + } + + return success_status; +} + struct g_lock_lock_state { struct tevent_context *ev; struct g_lock_ctx *ctx; TDB_DATA key; enum g_lock_type type; bool retry; + g_lock_lock_cb_fn_t cb_fn; + void *cb_private; }; struct g_lock_lock_fn_state { @@ -607,7 +687,15 @@ static NTSTATUS g_lock_trylock( enum g_lock_type type = req_state->type; bool retry = req_state->retry; struct g_lock lck = { .exclusive.pid = 0 }; - struct server_id *new_shared = NULL; + struct g_lock_lock_cb_state cb_state = { + .ctx = req_state->ctx, + .rec = rec, + .lck = &lck, + .cb_fn = req_state->cb_fn, + .cb_private = req_state->cb_private, + .existed = data.dsize != 0, + .update_mem_ctx = talloc_tos(), + }; struct server_id_buf tmp; NTSTATUS status; bool ok; @@ -831,29 +919,27 @@ noexclusive: do_shared: g_lock_cleanup_shared(&lck); - new_shared = &self; + cb_state.new_shared = &self; goto got_lock; got_lock: /* - * We are going to store us as owner or a reader, - * so we got what we were waiting for. - * - * So we no longer need to monitor the - * record. + * We got the lock we asked for, so we no + * longer need to monitor the record. */ dbwrap_watched_watch_remove_instance(rec, state->watch_instance); - status = g_lock_store(rec, &lck, new_shared, NULL, 0); - if (!NT_STATUS_IS_OK(status)) { - DBG_DEBUG("g_lock_store() failed: %s\n", - nt_errstr(status)); + status = g_lock_lock_cb_run_and_store(&cb_state); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_WAS_UNLOCKED)) + { + DBG_WARNING("g_lock_lock_cb_run_and_store() failed: %s\n", + nt_errstr(status)); return status; } talloc_set_destructor(req_state, NULL); - - return NT_STATUS_OK; + return status; } static void g_lock_lock_fn( |