Skip to content

Commit ac67ae3

Browse files
committed
Merge branch 'jh/builtin-fsmonitor-part3' into seen
More fsmonitor--daemon. * jh/builtin-fsmonitor-part3: (30 commits) t7527: improve implicit shutdown testing in fsmonitor--daemon fsmonitor--daemon: allow --super-prefix argument t7527: test Unicode NFC/NFD handling on MacOS t/lib-unicode-nfc-nfd: helper prereqs for testing unicode nfc/nfd t/helper/hexdump: add helper to print hexdump of stdin fsmonitor: on macOS also emit NFC spelling for NFD pathname t7527: test FSMonitor on case insensitive+preserving file system fsmonitor: never set CE_FSMONITOR_VALID on submodules t/perf/p7527: add perf test for builtin FSMonitor t7527: FSMonitor tests for directory moves fsmonitor: optimize processing of directory events fsm-listen-darwin: shutdown daemon if worktree root is moved/renamed fsm-health-win32: force shutdown daemon if worktree root moves fsm-health-win32: add polling framework to monitor daemon health fsmonitor--daemon: stub in health thread fsmonitor--daemon: rename listener thread related variables fsmonitor--daemon: prepare for adding health thread fsmonitor--daemon: cd out of worktree root fsm-listen-darwin: ignore FSEvents caused by xattr changes on macOS unpack-trees: initialize fsmonitor_has_run_once in o->result ...
2 parents 9733cc7 + 3294ca6 commit ac67ae3

28 files changed

+2439
-149
lines changed

Makefile

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,8 +477,14 @@ include shared.mak
477477
#
478478
# If your platform supports a built-in fsmonitor backend, set
479479
# FSMONITOR_DAEMON_BACKEND to the "<name>" of the corresponding
480-
# `compat/fsmonitor/fsm-listen-<name>.c` that implements the
481-
# `fsm_listen__*()` routines.
480+
# `compat/fsmonitor/fsm-listen-<name>.c` and
481+
# `compat/fsmonitor/fsm-health-<name>.c` files
482+
# that implement the `fsm_listen__*()` and `fsm_health__*()` routines.
483+
#
484+
# If your platform has OS-specific ways to tell if a repo is incompatible with
485+
# fsmonitor (whether the hook or IPC daemon version), set FSMONITOR_OS_SETTINGS
486+
# to the "<name>" of the corresponding `compat/fsmonitor/fsm-settings-<name>.c`
487+
# that implements the `fsm_os_settings__*()` routines.
482488
#
483489
# Define DEVELOPER to enable more compiler warnings. Compiler version
484490
# and family are auto detected, but could be overridden by defining
@@ -729,6 +735,7 @@ TEST_BUILTINS_OBJS += test-getcwd.o
729735
TEST_BUILTINS_OBJS += test-hash-speed.o
730736
TEST_BUILTINS_OBJS += test-hash.o
731737
TEST_BUILTINS_OBJS += test-hashmap.o
738+
TEST_BUILTINS_OBJS += test-hexdump.o
732739
TEST_BUILTINS_OBJS += test-index-version.o
733740
TEST_BUILTINS_OBJS += test-json-writer.o
734741
TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
@@ -2011,6 +2018,12 @@ endif
20112018
ifdef FSMONITOR_DAEMON_BACKEND
20122019
COMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND
20132020
COMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o
2021+
COMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o
2022+
endif
2023+
2024+
ifdef FSMONITOR_OS_SETTINGS
2025+
COMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS
2026+
COMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o
20142027
endif
20152028

20162029
ifeq ($(TCLTK_PATH),)
@@ -2965,6 +2978,9 @@ GIT-BUILD-OPTIONS: FORCE
29652978
ifdef FSMONITOR_DAEMON_BACKEND
29662979
@echo FSMONITOR_DAEMON_BACKEND=\''$(subst ','\'',$(subst ','\'',$(FSMONITOR_DAEMON_BACKEND)))'\' >>$@+
29672980
endif
2981+
ifdef FSMONITOR_OS_SETTINGS
2982+
@echo FSMONITOR_OS_SETTINGS=\''$(subst ','\'',$(subst ','\'',$(FSMONITOR_OS_SETTINGS)))'\' >>$@+
2983+
endif
29682984
ifdef TEST_OUTPUT_DIRECTORY
29692985
@echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+
29702986
endif

builtin/fsmonitor--daemon.c

Lines changed: 107 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "parse-options.h"
44
#include "fsmonitor.h"
55
#include "fsmonitor-ipc.h"
6+
#include "compat/fsmonitor/fsm-health.h"
67
#include "compat/fsmonitor/fsm-listen.h"
78
#include "fsmonitor--daemon.h"
89
#include "simple-ipc.h"
@@ -1136,6 +1137,18 @@ void fsmonitor_publish(struct fsmonitor_daemon_state *state,
11361137
pthread_mutex_unlock(&state->main_lock);
11371138
}
11381139

1140+
static void *fsm_health__thread_proc(void *_state)
1141+
{
1142+
struct fsmonitor_daemon_state *state = _state;
1143+
1144+
trace2_thread_start("fsm-health");
1145+
1146+
fsm_health__loop(state);
1147+
1148+
trace2_thread_exit();
1149+
return NULL;
1150+
}
1151+
11391152
static void *fsm_listen__thread_proc(void *_state)
11401153
{
11411154
struct fsmonitor_daemon_state *state = _state;
@@ -1174,18 +1187,21 @@ static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state)
11741187
*/
11751188
.uds_disallow_chdir = 0
11761189
};
1190+
int health_started = 0;
1191+
int listener_started = 0;
1192+
int err = 0;
11771193

11781194
/*
11791195
* Start the IPC thread pool before the we've started the file
11801196
* system event listener thread so that we have the IPC handle
11811197
* before we need it.
11821198
*/
11831199
if (ipc_server_run_async(&state->ipc_server_data,
1184-
fsmonitor_ipc__get_path(), &ipc_opts,
1200+
state->path_ipc.buf, &ipc_opts,
11851201
handle_client, state))
11861202
return error_errno(
11871203
_("could not start IPC thread pool on '%s'"),
1188-
fsmonitor_ipc__get_path());
1204+
state->path_ipc.buf);
11891205

11901206
/*
11911207
* Start the fsmonitor listener thread to collect filesystem
@@ -1194,15 +1210,31 @@ static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state)
11941210
if (pthread_create(&state->listener_thread, NULL,
11951211
fsm_listen__thread_proc, state) < 0) {
11961212
ipc_server_stop_async(state->ipc_server_data);
1197-
ipc_server_await(state->ipc_server_data);
1213+
err = error(_("could not start fsmonitor listener thread"));
1214+
goto cleanup;
1215+
}
1216+
listener_started = 1;
11981217

1199-
return error(_("could not start fsmonitor listener thread"));
1218+
/*
1219+
* Start the health thread to watch over our process.
1220+
*/
1221+
if (pthread_create(&state->health_thread, NULL,
1222+
fsm_health__thread_proc, state) < 0) {
1223+
ipc_server_stop_async(state->ipc_server_data);
1224+
err = error(_("could not start fsmonitor health thread"));
1225+
goto cleanup;
12001226
}
1227+
health_started = 1;
12011228

12021229
/*
12031230
* The daemon is now fully functional in background threads.
1231+
* Our primary thread should now just wait while the threads
1232+
* do all the work.
1233+
*/
1234+
cleanup:
1235+
/*
12041236
* Wait for the IPC thread pool to shutdown (whether by client
1205-
* request or from filesystem activity).
1237+
* request, from filesystem activity, or an error).
12061238
*/
12071239
ipc_server_await(state->ipc_server_data);
12081240

@@ -1211,23 +1243,38 @@ static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state)
12111243
* event from the IPC thread pool, but it doesn't hurt to tell
12121244
* it again. And wait for it to shutdown.
12131245
*/
1214-
fsm_listen__stop_async(state);
1215-
pthread_join(state->listener_thread, NULL);
1246+
if (listener_started) {
1247+
fsm_listen__stop_async(state);
1248+
pthread_join(state->listener_thread, NULL);
1249+
}
12161250

1217-
return state->error_code;
1251+
if (health_started) {
1252+
fsm_health__stop_async(state);
1253+
pthread_join(state->health_thread, NULL);
1254+
}
1255+
1256+
if (err)
1257+
return err;
1258+
if (state->listen_error_code)
1259+
return state->listen_error_code;
1260+
if (state->health_error_code)
1261+
return state->health_error_code;
1262+
return 0;
12181263
}
12191264

12201265
static int fsmonitor_run_daemon(void)
12211266
{
12221267
struct fsmonitor_daemon_state state;
1268+
const char *home;
12231269
int err;
12241270

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

12271273
hashmap_init(&state.cookies, cookies_cmp, NULL, 0);
12281274
pthread_mutex_init(&state.main_lock, NULL);
12291275
pthread_cond_init(&state.cookies_cond, NULL);
1230-
state.error_code = 0;
1276+
state.listen_error_code = 0;
1277+
state.health_error_code = 0;
12311278
state.current_token_data = fsmonitor_new_token_data();
12321279

12331280
/* Prepare to (recursively) watch the <worktree-root> directory. */
@@ -1289,6 +1336,15 @@ static int fsmonitor_run_daemon(void)
12891336

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

1339+
/*
1340+
* We create a named-pipe or unix domain socket inside of the
1341+
* ".git" directory. (Well, on Windows, we base our named
1342+
* pipe in the NPFS on the absolute path of the git
1343+
* directory.)
1344+
*/
1345+
strbuf_init(&state.path_ipc, 0);
1346+
strbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path()));
1347+
12921348
/*
12931349
* Confirm that we can create platform-specific resources for the
12941350
* filesystem listener before we bother starting all the threads.
@@ -1298,18 +1354,42 @@ static int fsmonitor_run_daemon(void)
12981354
goto done;
12991355
}
13001356

1357+
if (fsm_health__ctor(&state)) {
1358+
err = error(_("could not initialize health thread"));
1359+
goto done;
1360+
}
1361+
1362+
/*
1363+
* CD out of the worktree root directory.
1364+
*
1365+
* The common Git startup mechanism causes our CWD to be the
1366+
* root of the worktree. On Windows, this causes our process
1367+
* to hold a locked handle on the CWD. This prevents the
1368+
* worktree from being moved or deleted while the daemon is
1369+
* running.
1370+
*
1371+
* We assume that our FS and IPC listener threads have either
1372+
* opened all of the handles that they need or will do
1373+
* everything using absolute paths.
1374+
*/
1375+
home = getenv("HOME");
1376+
if (home && *home && chdir(home))
1377+
die_errno(_("could not cd home '%s'"), home);
1378+
13011379
err = fsmonitor_run_daemon_1(&state);
13021380

13031381
done:
13041382
pthread_cond_destroy(&state.cookies_cond);
13051383
pthread_mutex_destroy(&state.main_lock);
13061384
fsm_listen__dtor(&state);
1385+
fsm_health__dtor(&state);
13071386

13081387
ipc_server_free(state.ipc_server_data);
13091388

13101389
strbuf_release(&state.path_worktree_watch);
13111390
strbuf_release(&state.path_gitdir_watch);
13121391
strbuf_release(&state.path_cookie_prefix);
1392+
strbuf_release(&state.path_ipc);
13131393

13141394
return err;
13151395
}
@@ -1423,6 +1503,7 @@ static int try_to_start_background_daemon(void)
14231503
int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
14241504
{
14251505
const char *subcmd;
1506+
enum fsmonitor_reason reason;
14261507
int detach_console = 0;
14271508

14281509
struct option options[] = {
@@ -1449,6 +1530,23 @@ int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
14491530
die(_("invalid 'ipc-threads' value (%d)"),
14501531
fsmonitor__ipc_threads);
14511532

1533+
prepare_repo_settings(the_repository);
1534+
/*
1535+
* If the repo is fsmonitor-compatible, explicitly set IPC-mode
1536+
* (without bothering to load the `core.fsmonitor` config settings).
1537+
*
1538+
* If the repo is not compatible, the repo-settings will be set to
1539+
* incompatible rather than IPC, so we can use one of the __get
1540+
* routines to detect the discrepancy.
1541+
*/
1542+
fsm_settings__set_ipc(the_repository);
1543+
1544+
reason = fsm_settings__get_reason(the_repository);
1545+
if (reason > FSMONITOR_REASON_OK)
1546+
die("%s",
1547+
fsm_settings__get_incompatible_msg(the_repository,
1548+
reason));
1549+
14521550
if (!strcmp(subcmd, "start"))
14531551
return !!try_to_start_background_daemon();
14541552

builtin/update-index.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,6 +1257,22 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
12571257

12581258
if (fsmonitor > 0) {
12591259
enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);
1260+
enum fsmonitor_reason reason = fsm_settings__get_reason(r);
1261+
1262+
/*
1263+
* The user wants to turn on FSMonitor using the command
1264+
* line argument. (We don't know (or care) whether that
1265+
* is the IPC or HOOK version.)
1266+
*
1267+
* Use one of the __get routines to force load the FSMonitor
1268+
* config settings into the repo-settings. That will detect
1269+
* whether the file system is compatible so that we can stop
1270+
* here with a nice error message.
1271+
*/
1272+
if (reason > FSMONITOR_REASON_OK)
1273+
die("%s",
1274+
fsm_settings__get_incompatible_msg(r, reason));
1275+
12601276
if (fsm_mode == FSMONITOR_MODE_DISABLED) {
12611277
warning(_("core.fsmonitor is unset; "
12621278
"set it if you really want to "

compat/fsmonitor/fsm-health-darwin.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include "cache.h"
2+
#include "config.h"
3+
#include "fsmonitor.h"
4+
#include "fsm-health.h"
5+
#include "fsmonitor--daemon.h"
6+
7+
int fsm_health__ctor(struct fsmonitor_daemon_state *state)
8+
{
9+
return 0;
10+
}
11+
12+
void fsm_health__dtor(struct fsmonitor_daemon_state *state)
13+
{
14+
return;
15+
}
16+
17+
void fsm_health__loop(struct fsmonitor_daemon_state *state)
18+
{
19+
return;
20+
}
21+
22+
void fsm_health__stop_async(struct fsmonitor_daemon_state *state)
23+
{
24+
}

0 commit comments

Comments
 (0)