Files
git-mirror/reftable/system.c
Paulo Casaretto 7798604c84 lockfile: add PID file for debugging stale locks
When a lock file is held, it can be helpful to know which process owns
it, especially when debugging stale locks left behind by crashed
processes. Add an optional feature that creates a companion PID file
alongside each lock file, containing the PID of the lock holder.

For a lock file "foo.lock", the PID file is named "foo.pid.lock". The
file uses a simple key-value format ("pid <value>") following the same
pattern as Git object headers, making it extensible for future metadata.

The PID file is created when a lock is acquired (if enabled), and
automatically cleaned up when the lock is released (via commit or
rollback). The file is registered as a tempfile so it gets cleaned up
by signal and atexit handlers if the process terminates abnormally.

When a lock conflict occurs, the code checks for an existing PID file
and, if found, uses kill(pid, 0) to determine if the process is still
running. This allows providing context-aware error messages:

  Lock is held by process 12345. Wait for it to finish, or remove
  the lock file to continue.

Or for a stale lock:

  Lock was held by process 12345, which is no longer running.
  Remove the stale lock file to continue.

The feature is controlled via core.lockfilePid configuration, which
accepts per-component values similar to core.fsync:

  - none: Disable for all components (default)
  - all: Enable for all components
  - index, config, refs, commit-graph, midx, shallow, gc, other:
    Enable for specific components

Multiple components can be combined with commas:

  git config core.lockfilePid index,config

Each lock file call site specifies which component it belongs to,
allowing users fine-grained control over which locks create PID files.

Existing PID files are always read when displaying lock errors,
regardless of the core.lockfilePid setting. This ensures helpful
diagnostics even when the feature was previously enabled and later
disabled.

Signed-off-by: Paulo Casaretto <pcasaretto@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-12-18 09:23:07 +09:00

134 lines
2.5 KiB
C

#include "../git-compat-util.h"
#include "system.h"
#include "basics.h"
#include "reftable-error.h"
#include "../lockfile.h"
#include "../tempfile.h"
uint32_t reftable_rand(void)
{
return git_rand(CSPRNG_BYTES_INSECURE);
}
int tmpfile_from_pattern(struct reftable_tmpfile *out, const char *pattern)
{
struct tempfile *tempfile;
tempfile = mks_tempfile(pattern);
if (!tempfile)
return REFTABLE_IO_ERROR;
out->path = tempfile->filename.buf;
out->fd = tempfile->fd;
out->priv = tempfile;
return 0;
}
int tmpfile_close(struct reftable_tmpfile *t)
{
struct tempfile *tempfile = t->priv;
int ret = close_tempfile_gently(tempfile);
t->fd = -1;
if (ret < 0)
return REFTABLE_IO_ERROR;
return 0;
}
int tmpfile_delete(struct reftable_tmpfile *t)
{
struct tempfile *tempfile = t->priv;
int ret = delete_tempfile(&tempfile);
*t = REFTABLE_TMPFILE_INIT;
if (ret < 0)
return REFTABLE_IO_ERROR;
return 0;
}
int tmpfile_rename(struct reftable_tmpfile *t, const char *path)
{
struct tempfile *tempfile = t->priv;
int ret = rename_tempfile(&tempfile, path);
*t = REFTABLE_TMPFILE_INIT;
if (ret < 0)
return REFTABLE_IO_ERROR;
return 0;
}
int flock_acquire(struct reftable_flock *l, const char *target_path,
long timeout_ms)
{
struct lock_file *lockfile;
int err;
lockfile = reftable_malloc(sizeof(*lockfile));
if (!lockfile)
return REFTABLE_OUT_OF_MEMORY_ERROR;
err = hold_lock_file_for_update_timeout(lockfile, target_path, LOCK_NO_DEREF,
timeout_ms, LOCKFILE_PID_REFS);
if (err < 0) {
reftable_free(lockfile);
if (errno == EEXIST)
return REFTABLE_LOCK_ERROR;
return REFTABLE_IO_ERROR;
}
l->fd = get_lock_file_fd(lockfile);
l->path = get_lock_file_path(lockfile);
l->priv = lockfile;
return 0;
}
int flock_close(struct reftable_flock *l)
{
struct lock_file *lockfile = l->priv;
int ret;
if (!lockfile)
return REFTABLE_API_ERROR;
ret = close_lock_file_gently(lockfile);
l->fd = -1;
if (ret < 0)
return REFTABLE_IO_ERROR;
return 0;
}
int flock_release(struct reftable_flock *l)
{
struct lock_file *lockfile = l->priv;
int ret;
if (!lockfile)
return 0;
ret = rollback_lock_file(lockfile);
reftable_free(lockfile);
*l = REFTABLE_FLOCK_INIT;
if (ret < 0)
return REFTABLE_IO_ERROR;
return 0;
}
int flock_commit(struct reftable_flock *l)
{
struct lock_file *lockfile = l->priv;
int ret;
if (!lockfile)
return REFTABLE_API_ERROR;
ret = commit_lock_file(lockfile);
reftable_free(lockfile);
*l = REFTABLE_FLOCK_INIT;
if (ret < 0)
return REFTABLE_IO_ERROR;
return 0;
}