Git fork

fsmonitor--daemon: stub in health thread

Create another thread to watch over the daemon process and
automatically shut it down if necessary.

This commit creates the basic framework for a "health" thread
to monitor the daemon and/or the file system. Later commits
will add platform-specific code to do the actual work.

The "health" thread is intended to monitor conditions that
would be difficult to track inside the IPC thread pool and/or
the file system listener threads. For example, when there are
file system events outside of the watched worktree root or if
we want to have an idle-timeout auto-shutdown feature.

This commit creates the health thread itself, defines the thread-proc
and sets up the thread's event loop. It integrates this new thread
into the existing IPC and Listener thread models.

This commit defines the API to the platform-specific code where all of
the monitoring will actually happen.

The platform-specific code for MacOS is just stubs. Meaning that the
health thread will immediately exit on MacOS, but that is OK and
expected. Future work can define MacOS-specific monitoring.

The platform-specific code for Windows sets up enough of the
WaitForMultipleObjects() machinery to watch for system and/or custom
events. Currently, the set of wait handles only includes our custom
shutdown event (sent from our other theads). Later commits in this
series will extend the set of wait handles to monitor other
conditions.

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Jeff Hostetler and committed by
Junio C Hamano
d0605550 207534e4

+192 -2
+4 -2
Makefile
··· 472 472 # 473 473 # If your platform supports a built-in fsmonitor backend, set 474 474 # FSMONITOR_DAEMON_BACKEND to the "<name>" of the corresponding 475 - # `compat/fsmonitor/fsm-listen-<name>.c` that implements the 476 - # `fsm_listen__*()` routines. 475 + # `compat/fsmonitor/fsm-listen-<name>.c` and 476 + # `compat/fsmonitor/fsm-health-<name>.c` files 477 + # that implement the `fsm_listen__*()` and `fsm_health__*()` routines. 477 478 # 478 479 # If your platform has OS-specific ways to tell if a repo is incompatible with 479 480 # fsmonitor (whether the hook or IPC daemon version), set FSMONITOR_OS_SETTINGS ··· 1982 1983 ifdef FSMONITOR_DAEMON_BACKEND 1983 1984 COMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND 1984 1985 COMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o 1986 + COMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o 1985 1987 endif 1986 1988 1987 1989 ifdef FSMONITOR_OS_SETTINGS
+39
builtin/fsmonitor--daemon.c
··· 3 3 #include "parse-options.h" 4 4 #include "fsmonitor.h" 5 5 #include "fsmonitor-ipc.h" 6 + #include "compat/fsmonitor/fsm-health.h" 6 7 #include "compat/fsmonitor/fsm-listen.h" 7 8 #include "fsmonitor--daemon.h" 8 9 #include "simple-ipc.h" ··· 1136 1137 pthread_mutex_unlock(&state->main_lock); 1137 1138 } 1138 1139 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 + 1139 1152 static void *fsm_listen__thread_proc(void *_state) 1140 1153 { 1141 1154 struct fsmonitor_daemon_state *state = _state; ··· 1174 1187 */ 1175 1188 .uds_disallow_chdir = 0 1176 1189 }; 1190 + int health_started = 0; 1177 1191 int listener_started = 0; 1178 1192 int err = 0; 1179 1193 ··· 1202 1216 listener_started = 1; 1203 1217 1204 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; 1226 + } 1227 + health_started = 1; 1228 + 1229 + /* 1205 1230 * The daemon is now fully functional in background threads. 1206 1231 * Our primary thread should now just wait while the threads 1207 1232 * do all the work. ··· 1221 1246 if (listener_started) { 1222 1247 fsm_listen__stop_async(state); 1223 1248 pthread_join(state->listener_thread, NULL); 1249 + } 1250 + 1251 + if (health_started) { 1252 + fsm_health__stop_async(state); 1253 + pthread_join(state->health_thread, NULL); 1224 1254 } 1225 1255 1226 1256 if (err) 1227 1257 return err; 1228 1258 if (state->listen_error_code) 1229 1259 return state->listen_error_code; 1260 + if (state->health_error_code) 1261 + return state->health_error_code; 1230 1262 return 0; 1231 1263 } 1232 1264 ··· 1242 1274 pthread_mutex_init(&state.main_lock, NULL); 1243 1275 pthread_cond_init(&state.cookies_cond, NULL); 1244 1276 state.listen_error_code = 0; 1277 + state.health_error_code = 0; 1245 1278 state.current_token_data = fsmonitor_new_token_data(); 1246 1279 1247 1280 /* Prepare to (recursively) watch the <worktree-root> directory. */ ··· 1321 1354 goto done; 1322 1355 } 1323 1356 1357 + if (fsm_health__ctor(&state)) { 1358 + err = error(_("could not initialize health thread")); 1359 + goto done; 1360 + } 1361 + 1324 1362 /* 1325 1363 * CD out of the worktree root directory. 1326 1364 * ··· 1344 1382 pthread_cond_destroy(&state.cookies_cond); 1345 1383 pthread_mutex_destroy(&state.main_lock); 1346 1384 fsm_listen__dtor(&state); 1385 + fsm_health__dtor(&state); 1347 1386 1348 1387 ipc_server_free(state.ipc_server_data); 1349 1388
+24
compat/fsmonitor/fsm-health-darwin.c
··· 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 + }
+72
compat/fsmonitor/fsm-health-win32.c
··· 1 + #include "cache.h" 2 + #include "config.h" 3 + #include "fsmonitor.h" 4 + #include "fsm-health.h" 5 + #include "fsmonitor--daemon.h" 6 + 7 + struct fsm_health_data 8 + { 9 + HANDLE hEventShutdown; 10 + 11 + HANDLE hHandles[1]; /* the array does not own these handles */ 12 + #define HEALTH_SHUTDOWN 0 13 + int nr_handles; /* number of active event handles */ 14 + }; 15 + 16 + int fsm_health__ctor(struct fsmonitor_daemon_state *state) 17 + { 18 + struct fsm_health_data *data; 19 + 20 + CALLOC_ARRAY(data, 1); 21 + 22 + data->hEventShutdown = CreateEvent(NULL, TRUE, FALSE, NULL); 23 + 24 + data->hHandles[HEALTH_SHUTDOWN] = data->hEventShutdown; 25 + data->nr_handles++; 26 + 27 + state->health_data = data; 28 + return 0; 29 + } 30 + 31 + void fsm_health__dtor(struct fsmonitor_daemon_state *state) 32 + { 33 + struct fsm_health_data *data; 34 + 35 + if (!state || !state->health_data) 36 + return; 37 + 38 + data = state->health_data; 39 + 40 + CloseHandle(data->hEventShutdown); 41 + 42 + FREE_AND_NULL(state->health_data); 43 + } 44 + 45 + void fsm_health__loop(struct fsmonitor_daemon_state *state) 46 + { 47 + struct fsm_health_data *data = state->health_data; 48 + 49 + for (;;) { 50 + DWORD dwWait = WaitForMultipleObjects(data->nr_handles, 51 + data->hHandles, 52 + FALSE, INFINITE); 53 + 54 + if (dwWait == WAIT_OBJECT_0 + HEALTH_SHUTDOWN) 55 + goto clean_shutdown; 56 + 57 + error(_("health thread wait failed [GLE %ld]"), 58 + GetLastError()); 59 + goto force_error_stop; 60 + } 61 + 62 + force_error_stop: 63 + state->health_error_code = -1; 64 + ipc_server_stop_async(state->ipc_server_data); 65 + clean_shutdown: 66 + return; 67 + } 68 + 69 + void fsm_health__stop_async(struct fsmonitor_daemon_state *state) 70 + { 71 + SetEvent(state->health_data->hHandles[HEALTH_SHUTDOWN]); 72 + }
+47
compat/fsmonitor/fsm-health.h
··· 1 + #ifndef FSM_HEALTH_H 2 + #define FSM_HEALTH_H 3 + 4 + /* This needs to be implemented by each backend */ 5 + 6 + #ifdef HAVE_FSMONITOR_DAEMON_BACKEND 7 + 8 + struct fsmonitor_daemon_state; 9 + 10 + /* 11 + * Initialize platform-specific data for the fsmonitor health thread. 12 + * This will be called from the main thread PRIOR to staring the 13 + * thread. 14 + * 15 + * Returns 0 if successful. 16 + * Returns -1 otherwise. 17 + */ 18 + int fsm_health__ctor(struct fsmonitor_daemon_state *state); 19 + 20 + /* 21 + * Cleanup platform-specific data for the health thread. 22 + * This will be called from the main thread AFTER joining the thread. 23 + */ 24 + void fsm_health__dtor(struct fsmonitor_daemon_state *state); 25 + 26 + /* 27 + * The main body of the platform-specific event loop to monitor the 28 + * health of the daemon process. This will run in the health thread. 29 + * 30 + * The health thread should call `ipc_server_stop_async()` if it needs 31 + * to cause a shutdown. (It should NOT do so if it receives a shutdown 32 + * shutdown signal.) 33 + * 34 + * It should set `state->health_error_code` to -1 if the daemon should exit 35 + * with an error. 36 + */ 37 + void fsm_health__loop(struct fsmonitor_daemon_state *state); 38 + 39 + /* 40 + * Gently request that the health thread shutdown. 41 + * It does not wait for it to stop. The caller should do a JOIN 42 + * to wait for it. 43 + */ 44 + void fsm_health__stop_async(struct fsmonitor_daemon_state *state); 45 + 46 + #endif /* HAVE_FSMONITOR_DAEMON_BACKEND */ 47 + #endif /* FSM_HEALTH_H */
+2
contrib/buildsystems/CMakeLists.txt
··· 289 289 if(CMAKE_SYSTEM_NAME STREQUAL "Windows") 290 290 add_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND) 291 291 list(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c) 292 + list(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c) 292 293 293 294 add_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS) 294 295 list(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-win32.c) 295 296 elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") 296 297 add_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND) 297 298 list(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c) 299 + list(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c) 298 300 299 301 add_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS) 300 302 list(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-darwin.c)
+4
fsmonitor--daemon.h
··· 34 34 void fsmonitor_batch__add_path(struct fsmonitor_batch *batch, const char *path); 35 35 36 36 struct fsm_listen_data; /* opaque platform-specific data for listener thread */ 37 + struct fsm_health_data; /* opaque platform-specific data for health thread */ 37 38 38 39 struct fsmonitor_daemon_state { 39 40 pthread_t listener_thread; 41 + pthread_t health_thread; 40 42 pthread_mutex_t main_lock; 41 43 42 44 struct strbuf path_worktree_watch; ··· 51 53 struct hashmap cookies; 52 54 53 55 int listen_error_code; 56 + int health_error_code; 54 57 struct fsm_listen_data *listen_data; 58 + struct fsm_health_data *health_data; 55 59 56 60 struct ipc_server_data *ipc_server_data; 57 61 struct strbuf path_ipc;