Skip to content

Commit

Permalink
Merge branch 'try-v4-fsmonitor-part4' into try-v4-fsmonitor
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffhostetler authored and dscho committed Feb 15, 2022
2 parents eb7d786 + f5cb87b commit 509aa02
Show file tree
Hide file tree
Showing 21 changed files with 1,542 additions and 128 deletions.
19 changes: 17 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -472,8 +472,14 @@ all::
#
# If your platform supports a built-in fsmonitor backend, set
# FSMONITOR_DAEMON_BACKEND to the "<name>" of the corresponding
# `compat/fsmonitor/fsm-listen-<name>.c` that implements the
# `fsm_listen__*()` routines.
# `compat/fsmonitor/fsm-listen-<name>.c` and
# `compat/fsmonitor/fsm-health-<name>.c` files
# that implement the `fsm_listen__*()` and `fsm_health__*()` routines.
#
# If your platform has os-specific ways to tell if a repo is incompatible with
# fsmonitor (whether the hook or ipc daemon version), set FSMONITOR_OS_SETTINGS
# to the "<name>" of the corresponding `compat/fsmonitor/fsm-settings-<name>.c`
# that implements the `fsm_os_settings__*()` routines.
#
# Define DEVELOPER to enable more compiler warnings. Compiler version
# and family are auto detected, but could be overridden by defining
Expand Down Expand Up @@ -1976,6 +1982,12 @@ endif
ifdef FSMONITOR_DAEMON_BACKEND
COMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND
COMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o
COMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o
endif

ifdef FSMONITOR_OS_SETTINGS
COMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS
COMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o
endif

ifeq ($(TCLTK_PATH),)
Expand Down Expand Up @@ -2907,6 +2919,9 @@ GIT-BUILD-OPTIONS: FORCE
ifdef FSMONITOR_DAEMON_BACKEND
@echo FSMONITOR_DAEMON_BACKEND=\''$(subst ','\'',$(subst ','\'',$(FSMONITOR_DAEMON_BACKEND)))'\' >>$@+
endif
ifdef FSMONITOR_OS_SETTINGS
@echo FSMONITOR_OS_SETTINGS=\''$(subst ','\'',$(subst ','\'',$(FSMONITOR_OS_SETTINGS)))'\' >>$@+
endif
ifdef TEST_OUTPUT_DIRECTORY
@echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+
endif
Expand Down
138 changes: 123 additions & 15 deletions builtin/fsmonitor--daemon.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "parse-options.h"
#include "fsmonitor.h"
#include "fsmonitor-ipc.h"
#include "compat/fsmonitor/fsm-health.h"
#include "compat/fsmonitor/fsm-listen.h"
#include "fsmonitor--daemon.h"
#include "simple-ipc.h"
Expand All @@ -27,6 +28,9 @@ static int fsmonitor__ipc_threads = 8;
#define FSMONITOR__START_TIMEOUT "fsmonitor.starttimeout"
static int fsmonitor__start_timeout_sec = 60;

#define FSMONITOR__ANNOUNCE_STARTUP "fsmonitor.announcestartup"
static int fsmonitor__announce_startup = 0;

static int fsmonitor_config(const char *var, const char *value, void *cb)
{
if (!strcmp(var, FSMONITOR__IPC_THREADS)) {
Expand All @@ -47,6 +51,16 @@ static int fsmonitor_config(const char *var, const char *value, void *cb)
return 0;
}

if (!strcmp(var, FSMONITOR__ANNOUNCE_STARTUP)) {
int is_bool;
int i = git_config_bool_or_int(var, value, &is_bool);
if (i < 0)
return error(_("value of '%s' not bool or int: %d"),
var, i);
fsmonitor__announce_startup = i;
return 0;
}

return git_default_config(var, value, cb);
}

Expand Down Expand Up @@ -1111,6 +1125,18 @@ void fsmonitor_publish(struct fsmonitor_daemon_state *state,
pthread_mutex_unlock(&state->main_lock);
}

static void *fsm_health__thread_proc(void *_state)
{
struct fsmonitor_daemon_state *state = _state;

trace2_thread_start("fsm-health");

fsm_health__loop(state);

trace2_thread_exit();
return NULL;
}

static void *fsm_listen__thread_proc(void *_state)
{
struct fsmonitor_daemon_state *state = _state;
Expand Down Expand Up @@ -1149,18 +1175,21 @@ static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state)
*/
.uds_disallow_chdir = 0
};
int health_started = 0;
int listener_started = 0;
int err = 0;

/*
* Start the IPC thread pool before the we've started the file
* system event listener thread so that we have the IPC handle
* before we need it.
*/
if (ipc_server_run_async(&state->ipc_server_data,
fsmonitor_ipc__get_path(), &ipc_opts,
state->path_ipc.buf, &ipc_opts,
handle_client, state))
return error_errno(
_("could not start IPC thread pool on '%s'"),
fsmonitor_ipc__get_path());
state->path_ipc.buf);

/*
* Start the fsmonitor listener thread to collect filesystem
Expand All @@ -1169,15 +1198,31 @@ static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state)
if (pthread_create(&state->listener_thread, NULL,
fsm_listen__thread_proc, state) < 0) {
ipc_server_stop_async(state->ipc_server_data);
ipc_server_await(state->ipc_server_data);
err = error(_("could not start fsmonitor listener thread"));
goto cleanup;
}
listener_started = 1;

return error(_("could not start fsmonitor listener thread"));
/*
* Start the health thread to watch over our process.
*/
if (pthread_create(&state->health_thread, NULL,
fsm_health__thread_proc, state) < 0) {
ipc_server_stop_async(state->ipc_server_data);
err = error(_("could not start fsmonitor health thread"));
goto cleanup;
}
health_started = 1;

/*
* The daemon is now fully functional in background threads.
* Our primary thread should now just wait while the threads
* do all the work.
*/
cleanup:
/*
* Wait for the IPC thread pool to shutdown (whether by client
* request or from filesystem activity).
* request, from filesystem activity, or an error).
*/
ipc_server_await(state->ipc_server_data);

Expand All @@ -1186,23 +1231,38 @@ static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state)
* event from the IPC thread pool, but it doesn't hurt to tell
* it again. And wait for it to shutdown.
*/
fsm_listen__stop_async(state);
pthread_join(state->listener_thread, NULL);
if (listener_started) {
fsm_listen__stop_async(state);
pthread_join(state->listener_thread, NULL);
}

return state->error_code;
if (health_started) {
fsm_health__stop_async(state);
pthread_join(state->health_thread, NULL);
}

if (err)
return err;
if (state->listen_error_code)
return state->listen_error_code;
if (state->health_error_code)
return state->health_error_code;
return 0;
}

static int fsmonitor_run_daemon(void)
{
struct fsmonitor_daemon_state state;
const char *home;
int err;

memset(&state, 0, sizeof(state));

hashmap_init(&state.cookies, cookies_cmp, NULL, 0);
pthread_mutex_init(&state.main_lock, NULL);
pthread_cond_init(&state.cookies_cond, NULL);
state.error_code = 0;
state.listen_error_code = 0;
state.health_error_code = 0;
state.current_token_data = fsmonitor_new_token_data();

/* Prepare to (recursively) watch the <worktree-root> directory. */
Expand Down Expand Up @@ -1264,6 +1324,15 @@ static int fsmonitor_run_daemon(void)

strbuf_addch(&state.path_cookie_prefix, '/');

/*
* We create a named-pipe or unix domain socket inside of the
* ".git" directory. (Well, on Windows, we base our named
* pipe in the NPFS on the absolute path of the git
* directory.)
*/
strbuf_init(&state.path_ipc, 0);
strbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path()));

/*
* Confirm that we can create platform-specific resources for the
* filesystem listener before we bother starting all the threads.
Expand All @@ -1273,18 +1342,42 @@ static int fsmonitor_run_daemon(void)
goto done;
}

if (fsm_health__ctor(&state)) {
err = error(_("could not initialize health thread"));
goto done;
}

/*
* CD out of the worktree root directory.
*
* The common Git startup mechanism causes our CWD to be the
* root of the worktree. On Windows, this causes our process
* to hold a locked handle on the CWD. This prevents the
* worktree from being moved or deleted while the daemon is
* running.
*
* We assume that our FS and IPC listener threads have either
* opened all of the handles that they need or will do
* everything using absolute paths.
*/
home = getenv("HOME");
if (home && *home && chdir(home))
die_errno("could not cd home '%s'", home);

err = fsmonitor_run_daemon_1(&state);

done:
pthread_cond_destroy(&state.cookies_cond);
pthread_mutex_destroy(&state.main_lock);
fsm_listen__dtor(&state);
fsm_health__dtor(&state);

ipc_server_free(state.ipc_server_data);

strbuf_release(&state.path_worktree_watch);
strbuf_release(&state.path_gitdir_watch);
strbuf_release(&state.path_cookie_prefix);
strbuf_release(&state.path_ipc);

/*
* NEEDSWORK: Consider "rm -rf <gitdir>/<fsmonitor-dir>"
Expand All @@ -1307,9 +1400,11 @@ static int try_to_run_foreground_daemon(int free_console)
die("fsmonitor--daemon is already running '%s'",
the_repository->worktree);

printf(_("running fsmonitor-daemon in '%s'\n"),
the_repository->worktree);
fflush(stdout);
if (fsmonitor__announce_startup) {
fprintf(stderr, _("running fsmonitor-daemon in '%s'\n"),
the_repository->worktree);
fflush(stderr);
}

#ifdef GIT_WINDOWS_NATIVE
if (free_console)
Expand Down Expand Up @@ -1360,9 +1455,11 @@ static int try_to_start_background_daemon(void)
die("fsmonitor--daemon is already running '%s'",
the_repository->worktree);

printf(_("starting fsmonitor-daemon in '%s'\n"),
the_repository->worktree);
fflush(stdout);
if (fsmonitor__announce_startup) {
fprintf(stderr, _("starting fsmonitor-daemon in '%s'\n"),
the_repository->worktree);
fflush(stderr);
}

cp.git_cmd = 1;

Expand Down Expand Up @@ -1424,6 +1521,17 @@ int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
die(_("invalid 'ipc-threads' value (%d)"),
fsmonitor__ipc_threads);

prepare_repo_settings(the_repository);
fsm_settings__set_ipc(the_repository);

if (fsm_settings__get_mode(the_repository) == FSMONITOR_MODE_INCOMPATIBLE) {
struct strbuf buf_reason = STRBUF_INIT;
fsm_settings__get_reason(the_repository, &buf_reason);
error("%s '%s'", buf_reason.buf, xgetcwd());
strbuf_release(&buf_reason);
return -1;
}

if (!strcmp(subcmd, "start"))
return !!try_to_start_background_daemon();

Expand Down
9 changes: 9 additions & 0 deletions builtin/update-index.c
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,15 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)

if (fsmonitor > 0) {
enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);

if (fsm_mode == FSMONITOR_MODE_INCOMPATIBLE) {
struct strbuf buf_reason = STRBUF_INIT;
fsm_settings__get_reason(r, &buf_reason);
error("%s", buf_reason.buf);
strbuf_release(&buf_reason);
return -1;
}

if (fsm_mode == FSMONITOR_MODE_DISABLED) {
advise(_("core.fsmonitor is unset; "
"set it if you really want to "
Expand Down
24 changes: 24 additions & 0 deletions compat/fsmonitor/fsm-health-darwin.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include "cache.h"
#include "config.h"
#include "fsmonitor.h"
#include "fsm-health.h"
#include "fsmonitor--daemon.h"

int fsm_health__ctor(struct fsmonitor_daemon_state *state)
{
return 0;
}

void fsm_health__dtor(struct fsmonitor_daemon_state *state)
{
return;
}

void fsm_health__loop(struct fsmonitor_daemon_state *state)
{
return;
}

void fsm_health__stop_async(struct fsmonitor_daemon_state *state)
{
}
Loading

0 comments on commit 509aa02

Please sign in to comment.