Builtin FSMonitor Feature
Subsystems (2)
PATCH v3 31/34 fsmonitor: force update index after large responses
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Set the FSMONITOR_CHANGED bit on istate->cache_changed when FSMonitor returns a very large repsonse to ensure that the index is written to disk.
Normally, when the FSMonitor response includes a tracked file, the index is always updated. Similarly, the index might be updated when the response alters the untracked-cache (when enabled). However, in cases where neither of those cause the index to be considered changed, the FSMonitor response is wasted. Subsequent Git commands will make requests with the same token and receive the same response.
If that response is very large, performance may suffer. It would be more efficient to force update the index now (and the token in the index extension) in order to reduce the size of the response received by future commands.
This was observed on Windows after a large checkout. On Windows, the kernel emits events for the files that are changed as they are changed. However, it might delay events for the containing directories until the system is more idle (or someone scans the directory (so it seems)). The first status following a checkout would get the list of files. The subsequent status commands would get the list of directories as the events trickled out. But they would never catch up because the token was not advanced because the index wasn't updated.
This list of directories caused wt_status_collect_untracked() to unnecessarily spend time actually scanning them during each command.
--- fsmonitor.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-)
fsmonitor.c +49 -1
@@ -236,6 +236,45 @@ static void fsmonitor_refresh_callback(struct index_state *istate, char *name)untracked_cache_invalidate_path (istate, name,0 ); } +/* +* The number of pathnames that we need to receive from FSMonitor +* before we force the index to be updated. +* +* Note that any pathname within the set of received paths MAY cause +* cache-entry or istate flag bits to be updated and thus cause the +* index to be updated on disk. +* +* However, the response may contain many paths (such as ignored +* paths) that will not update any flag bits. And thus not force the +* index to be updated. (This is fine and normal.) It also means +* that the token will not be updated in the FSMonitor index +* extension. So the next Git command will find the same token in the +* index, make the same token-relative request, and receive the same +* response (plus any newly changed paths). If this response is large +* (and continues to grow), performance could be impacted. +* +* For example, if the user runs a build and it writes 100K object +* files but doesn't modify any source files, the index would not need +* to be updated. The FSMonitor response (after the build and +* relative to a pre-build token) might be 5MB. Each subsequent Git +* command will receive that same 100K/5MB response until something +* causes the index to be updated. And `refresh_fsmonitor()` will +* have to iterate over those 100K paths each time. +* +* Performance could be improved if we optionally force update the +* index after a very large response and get an updated token into +* the FSMonitor index extension. This should allow subsequent +* commands to get smaller and more current responses. +* +* The value chosen here does not need to be precise. The index +* will be updated automatically the first time the user touches +* a tracked file and causes a command like `git status` to +* update an mtime to be updated and/or set a flag bit. +* +* NEEDSWORK: Does this need to be a config value? +*/ +static int fsmonitor_force_update_threshold =100 ; +void refresh_fsmonitor (struct index_state *istate) {struct strbuf query_result =STRBUF_INIT ;
@@ -379,19 +418,28 @@ apply_results:* * This updates both the cache-entries and the untracked-cache. */ +int count =0 ; + buf = query_result.buf;for (i = bol; i < query_result.len; i++) {if (buf[i] !='\0' )continue ;fsmonitor_refresh_callback (istate, buf + bol); bol = i +1 ; + count++; } -if (bol < query_result.len) +if (bol < query_result.len) {fsmonitor_refresh_callback (istate, buf + bol); + count++; + }/* Now mark the untracked cache for fsmonitor usage */ if (istate->untracked) istate->untracked->use_fsmonitor =1 ; + +if (count > fsmonitor_force_update_threshold) + istate->cache_changed |=FSMONITOR_CHANGED ; + }else {/* * We received a trivial response, so invalidate everything.
Signature · 1 line
gitgitgadget
PATCH v3 33/34 fsmonitor: handle shortname for .git
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
On Windows, teach FSMonitor to recognize the shortname of ".git" as an alias for ".git".
Sometimes we receive FS events using the shortname, such as when a CMD shell runs "RENAME GIT~1 FOO" or "RMDIR GIT~1". The FS notification arrives using whatever combination of long and shortnames used by the other process. (Shortnames do seem to be case normalized, however.)
NEEDSWORK: This only addresses the case of removing or renaming the ".git" directory using the shortname alias, so that the daemon properly shuts down. I'm leaving it a task for later to handle the general case of shortnames and report them to the fsmonitor client process. This would include tracked and untracked paths that just happen to have a shortname alias.
--- compat/fsmonitor/fsmonitor-fs-listen-win32.c | 192 +++++++++++++++---- t/t7527-builtin-fsmonitor.sh | 65 +++++++ 2 files changed, 217 insertions(+), 40 deletions(-)
compat/fsmonitor/fsmonitor-fs-listen-win32.c +152 -40
@@ -48,6 +48,8 @@ struct fsmonitor_daemon_backend_data#define LISTENER_HAVE_DATA_WORKTREE 1#define LISTENER_HAVE_DATA_GITDIR 2int nr_listener_handles; + +struct strbuf dot_git_shortname; };/*
@@ -250,6 +252,62 @@ static void cancel_rdcw_watch(struct one_watch *watch) watch->is_active = FALSE; } +/* +* Process a single relative pathname event. +* Return 1 if we should shutdown. +*/ +static int process_1_worktree_event ( +FILE_NOTIFY_INFORMATION *info, +struct string_list *cookie_list, +struct fsmonitor_batch **batch, +const struct strbuf *path, +enum fsmonitor_path_type t) +{ +const char *slash; + +switch (t) { +case IS_INSIDE_DOT_GIT_WITH_COOKIE_PREFIX : +/* special case cookie files within .git */ + +/* Use just the filename of the cookie file. */ + slash =find_last_dir_sep (path->buf); +string_list_append (cookie_list, + slash ? slash +1 : path->buf); +break ; + +case IS_INSIDE_DOT_GIT : +/* ignore everything inside of "<worktree>/.git/" */ +break ; + +case IS_DOT_GIT : +/* "<worktree>/.git" was deleted (or renamed away) */ +if ((info->Action ==FILE_ACTION_REMOVED ) || + (info->Action ==FILE_ACTION_RENAMED_OLD_NAME )) { +trace2_data_string ("fsmonitor" ,NULL , +"fsm-listen/dotgit" , +"removed" ); +return 1 ; + } +break ; + +case IS_WORKDIR_PATH : +/* queue normal pathname */ +if (!*batch) + *batch =fsmonitor_batch__new (); +fsmonitor_batch__add_path (*batch, path->buf); +break ; + +case IS_GITDIR : +case IS_INSIDE_GITDIR : +case IS_INSIDE_GITDIR_WITH_COOKIE_PREFIX : +default : +BUG ("unexpected path classification '%d' for '%s'" , + t, path->buf); + } + +return 0 ; +} +/* * Process filesystem events that happen anywhere (recursively) under the * <worktree> root directory. For a normal working directory, this includes
@@ -294,7 +352,6 @@ static int process_worktree_events(struct fsmonitor_daemon_state *state)*/ for (;;) {FILE_NOTIFY_INFORMATION *info = (void *)p; -const char *slash;enum fsmonitor_path_type t;strbuf_reset (&path);
@@ -303,45 +360,45 @@ static int process_worktree_events(struct fsmonitor_daemon_state *state) t =fsmonitor_classify_path_workdir_relative (path.buf); -switch (t) { -case IS_INSIDE_DOT_GIT_WITH_COOKIE_PREFIX : -/* special case cookie files within .git */ - -/* Use just the filename of the cookie file. */ - slash =find_last_dir_sep (path.buf); -string_list_append (&cookie_list, - slash ? slash +1 : path.buf); -break ; - -case IS_INSIDE_DOT_GIT : -/* ignore everything inside of "<worktree>/.git/" */ -break ; - -case IS_DOT_GIT : -/* "<worktree>/.git" was deleted (or renamed away) */ -if ((info->Action ==FILE_ACTION_REMOVED ) || - (info->Action ==FILE_ACTION_RENAMED_OLD_NAME )) { -trace2_data_string ("fsmonitor" ,NULL , -"fsm-listen/dotgit" , -"removed" ); - goto force_shutdown; - } -break ; - -case IS_WORKDIR_PATH : -/* queue normal pathname */ -if (!batch) - batch =fsmonitor_batch__new (); -fsmonitor_batch__add_path (batch, path.buf); -break ; - -case IS_GITDIR : -case IS_INSIDE_GITDIR : -case IS_INSIDE_GITDIR_WITH_COOKIE_PREFIX : -default : -BUG ("unexpected path classification '%d' for '%s'" , - t, path.buf); - } +if (process_1_worktree_event (info, &cookie_list, &batch, + &path, t)) + goto force_shutdown; + +/* +* NEEDSWORK: If `path` contains a shortname (that is, +* if any component within it is a shortname), we +* should expand it to a longname (See +* `GetLongPathNameW()`) and re-normalize, classify, +* and process it because our client is probably +* expecting "normal" paths. +* +* HOWEVER, if our process has called `chdir()` to get +* us out of the root of the worktree (so that the +* root directory is not busy), then we have to be +* careful to convert the paths in the INFO array +* (which are relative to the directory of the RDCW +* watch and not the CWD) into absolute paths before +* calling GetLongPathNameW() and then convert the +* computed value back to a RDCW-relative pathname +* (which is what we and the client expect). +* +* FOR NOW, just handle case (1) exactly so that we +* shutdown properly when ".git" is deleted via the +* shortname alias. +* +* We might see case (2) events for cookie files, but +* we can ignore them. +* +* FOR LATER, handle case (3) where the worktree +* events contain shortnames. We should convert +* them to longnames to avoid confusing the client. +*/ +if (data->dot_git_shortname.len && + !strcmp (path.buf, data->dot_git_shortname.buf) && +process_1_worktree_event (info, &cookie_list, &batch, + &data->dot_git_shortname, +IS_DOT_GIT )) + goto force_shutdown; skip_this_path:if (!info->NextEntryOffset)
@@ -415,6 +472,14 @@ static int process_gitdir_events(struct fsmonitor_daemon_state *state) t, path.buf); } +/* +* WRT shortnames, this external gitdir will not see +* case (1) nor case (3) events. +* +* We might see case (2) events for cookie files, but +* we can ignore them. +*/ + skip_this_path:if (!info->NextEntryOffset)break ;
@@ -493,6 +558,7 @@ clean_shutdown:int fsmonitor_fs_listen__ctor (struct fsmonitor_daemon_state *state) {struct fsmonitor_daemon_backend_data *data; +char shortname[16 ];/* a padded 8.3 buffer */ CALLOC_ARRAY (data,1 );
@@ -523,6 +589,52 @@ int fsmonitor_fs_listen__ctor(struct fsmonitor_daemon_state *state) data->nr_listener_handles++; } +/* +* NEEDSWORK: Properly handle 8.3 shortnames. RDCW events can +* contain a shortname (if another application uses a +* shortname in a system call). We care about aliasing and +* the use of shortnames for: +* +* (1) ".git", +* -- if an external process deletes ".git" using "GIT~1", +* we need to catch that and shutdown. +* +* (2) our cookie files, +* -- if an external process deletes one of our cookie +* files using a shortname, we will get a shortname +* event for it. However, we should have already +* gotten a longname event for it when we created the +* cookie, so we can safely discard the shortname +* events for cookie files. +* +* (3) the spelling of modified files that we report to clients. +* -- we need to report the longname to the client because +* that is what they are expecting. Presumably, the +* client is going to lookup the paths that we report +* in their index and untracked-cache, so we should +* normalize the data for them. (Technically, they +* could adapt, so we could relax this maybe.) +* +* FOR NOW, while our CWD is at the root of the worktree we +* can easily get the spelling of the shortname of ".git" (if +* the volume has shortnames enabled). For most worktrees +* this value will be "GIT~1", but we don't want to assume +* that. +* +* Capture this so that we can handle (1). +* +* We leave (3) for a future effort. +*/ +strbuf_init (&data->dot_git_shortname,0 ); +GetShortPathNameA (".git" , shortname,sizeof (shortname)); +if (!strcmp (".git" , shortname)) +trace_printf_key (&trace_fsmonitor,"No shortname for '.git'" ); +else { +trace_printf_key (&trace_fsmonitor, +"Shortname of '.git' is '%s'" , shortname); +strbuf_addstr (&data->dot_git_shortname, shortname); + } + state->backend_data = data;return 0 ;
t/t7527-builtin-fsmonitor.sh +65 -0
71 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH v3 34/34 t7527: test FS event reporing on MacOS WRT case and Unicode
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Confirm that MacOS FS events are reported with a normalized spelling.
APFS (and/or HFS+) is case-insensitive. This means that case-independent lookups ( [ -d .git ] and [ -d .GIT ] ) should both succeed. But that doesn't tell us how FS events are reported if we try "rm -rf .git" versus "rm -rf .GIT". Are the events reported using the on-disk spelling of the pathname or in the spelling used by the command.
NEEDSWORK: I was only able to test case. It would be nice to add tests that use different Unicode spellings/normalizations and understand the differences between APFS and HFS+ in this area. We should confirm that the spelling of the workdir paths that the daemon sends to clients are always properly normalized.
--- t/t7527-builtin-fsmonitor.sh | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+)
t/t7527-builtin-fsmonitor.sh +30 -0
@@ -178,6 +178,36 @@ test_expect_success MINGW,SHORTNAMES 'implicit daemon stop (rename GIT~2)' ' test_must_fail git -C test_implicit_1s2 fsmonitor--daemon status ' +# Confirm that MacOS hides all of the Unicode normalization and/or +# case folding from the FS events. That is, are the pathnames in the +# FS events reported using the spelling on the disk or in the spelling +# used by the other process. +# +# Note that we assume that the filesystem is set to case insensitive. +# +# NEEDSWORK: APFS handles Unicode and Unicode normalization +# differently than HFS+. I only have an APFS partition, so +# more testing here would be helpful. +# + +# Rename .git using alternate spelling and confirm that the daemon +# sees the event using the correct spelling and shutdown. +test_expect_success UTF8_NFD_TO_NFC 'MacOS event spelling (rename .GIT)' ' + test_when_finished "stop_daemon_delete_repo test_apfs" && + + git init test_apfs && + start_daemon test_apfs && + + [ -d test_apfs/.git ] && + [ -d test_apfs/.GIT ] && + + mv test_apfs/.GIT test_apfs/.FOO && + sleep 1 && + mv test_apfs/.FOO test_apfs/.git && + + test_must_fail git -C test_apfs fsmonitor--daemon status +' + test_expect_success 'cannot start multiple daemons' ' test_when_finished "stop_daemon_delete_repo test_multiple" &&
Signature · 1 line
gitgitgadget
Re: [PATCH v3 06/34] fsmonitor: config settings are repository-specific
In reply to Jeff Hostetler via GitGitGadgetOn Thu, Jul 01 2021, Jeff Hostetler via GitGitGadget wrote:
In a reference to a discussion[1] about an earlier version of this patch you said:
I'm going to ignore all of the thread responses to this patch dealing with how we acquire config settings and macros and etc. Those issues are completely independent of FSMonitor (which is already way too big).
Since then the changes to repo-settings.c have become a lot larger, so let's take a look...
1. https://lore.kernel.org/git/[email protected]/
2. https://lore.kernel.org/git/[email protected]/
Quoted 11 lines
repo-settings.c +3
-0@@ -5,10 +5,42 @@#define UPDATE_DEFAULT_BOOL (s,v) do { if (s == -1) { s = v; } } while(0) +/* +* Return 1 if the repo/workdir is incompatible with FSMonitor. +*/
repo-settings.c line 20
Quoted 10 lines
repo-settings.c +10
-0+static int is_repo_incompatible_with_fsmonitor (struct repository *r) +{ +const char *const_strval; + +/* +* Bare repositories don't have a working directory and +* therefore, nothing to watch. +*/ +if (!r->worktree) +return 1 ;
Looking ahead in this series you end up using FSMONITOR_MODE_INCOMPATIBLE in two places in the codebase. In builtin/update-index.c to throw a "repository is incompatible with fsmonitor" error.
Can't that case just be replaced with setup_work_tree()? Other sub-modes of update-index already die implicitly on that, e.g.:
$ git update-index test fatal: this operation must be run in a work tree
The other case is:
+ prepare_repo_settings(the_repository);
+ if (!the_repository->worktree)
+ return error(_("fsmonitor-daemon does not support bare repos '%s'"),
+ xgetcwd());
+ if (the_repository->settings.fsmonitor_mode == FSMONITOR_MODE_INCOMPATIBLE)
+ return error(_("fsmonitor-daemon is incompatible with this repo '%s'"),
+ the_repository->worktree);
I.e. we just checked the_repository->worktree, but it's not that, but....
Quoted 4 lines
repo-settings.c +4
-0+ +/* +* GVFS (aka VFS for Git) is incompatible with FSMonitor. +*
repo-settings.c line 34
Quoted 10 lines
repo-settings.c +10
-0+* Granted, core Git does not know anything about GVFS and +* we shouldn't make assumptions about a downstream feature, +* but users can install both versions. And this can lead +* to incorrect results from core Git commands. So, without +* bringing in any of the GVFS code, do a simple config test +* for a published config setting. (We do not look at the +* various *_TEST_* environment variables.) +*/ +if (!repo_config_get_value (r,"core.virtualfilesystem" , &const_strval)) +return 1 ;
I'm skeptical of us hardcoding a third-party software config variable. Can't GitVFS handle this somehow on its end?
But just in terms of implementation it seems the end result of that is to emit a very confusing error to the user. Sinc we already checked for bare repos we run into this and instead of sayingwhen we should really say "hey, maybe disable your core.virtualFileSystem setting", we say "your repo is incompatible".
repo-settings.c line 43
Quoted 9 lines
repo-settings.c +5
-0+ +return 0 ; +} +void prepare_repo_settings (struct repository *r) {int value;char *strval; +const char *const_strval;
Can be declared in the "else" below.
repo-settings.c void prepare_repo_settings(struct repository *r) · line 62
Quoted 9 lines
repo-settings.c +2
-0if (r->settings.initialized)return ;@@ -26,6 +58,22 @@ void prepare_repo_settings(struct repository *r)UPDATE_DEFAULT_BOOL (r->settings.commit_graph_read_changed_paths,1 );UPDATE_DEFAULT_BOOL (r->settings.gc_write_commit_graph,1 ); + r->settings.fsmonitor_hook_path =NULL ; + r->settings.fsmonitor_mode =FSMONITOR_MODE_DISABLED ;
With the memset earlier (b.t.w. I've got a patch to fix all this bizarre behavior in repo-settings.c, but have been waiting on this series we implicitly set it to FSMONITOR_MODE_UNSET (-1) with the memset, but then never use that ever.
Your code in update-index.c then for a check against "FSMONITOR_MODE_DISABLED" says "core.useBuiltinFSMonitor is unset;".
repo-settings.c void prepare_repo_settings(struct repository *r) · line 64
Quoted 2 lines
repo-settings.c +2
-0+if (is_repo_incompatible_with_fsmonitor (r)) + r->settings.fsmonitor_mode =FSMONITOR_MODE_INCOMPATIBLE ;
Style: should have {} braces on all arms.
repo-settings.c void prepare_repo_settings(struct repository *r) · line 67
Quoted 3 lines
repo-settings.c +3
-0+else if (!repo_config_get_bool (r,"core.usebuiltinfsmonitor" , &value) + && value) + r->settings.fsmonitor_mode =FSMONITOR_MODE_IPC ;
Here you're conflating false with whether the variable is set at all. I guess that works out here since if it's false we want to fall through to...
repo-settings.c void prepare_repo_settings(struct repository *r) · line 68
Quoted 1 line
repo-settings.c +1
-0+else {
...ignoring it and looing at core.fsmonitor instead.
repo-settings.c void prepare_repo_settings(struct repository *r) · line 70
Quoted 2 lines
repo-settings.c +2
-0+if (repo_config_get_pathname (r,"core.fsmonitor" , &const_strval)) + const_strval =getenv ("GIT_TEST_FSMONITOR" );
If it's not set we pay attention to GIT_TEST_FSMONITOR, so the behavior from the old git_config_get_fsmonitor(). So even if the env variable is set we want to take the config variable over it, correct?
repo-settings.c void prepare_repo_settings(struct repository *r) · line 72
Quoted 2 lines
repo-settings.c +2
-0+if (const_strval && *const_strval) { + r->settings.fsmonitor_hook_path =strdup (const_strval);
We had a strbuf_detach()'d string in the case of repo_config_get_pathname(), but here we strdup() it again in case we were in the getenv() codepath. This code probably leaks memory now anyway, but perhaps it's better to split up the two so we make it easier to deal with who owns/frees what in the future.
Quoted 10 lines · Jeff Hostetler via GitGitGadget
Here is V3 of my patch series to add a builtin FSMonitor daemon to Git. I rebased this series onto v2.32.0.
V3 addresses most of the previous review comments and things we've learned from our experimental testing of V2. (A version of V2 was shipped as an experimental feature in the v2.32.0-based releases of Git for Windows and VFS for Git.)
There are still a few items that I need to address, but that list is getting very short.
...
Quoted 2 lines
fsmonitor-fs-listen-win32: stub in backend for Windows
fsmonitor-fs-listen-macos: stub in backend for MacOS
I left some light comments on the repo-settings.c part of this to follow up from a previous round.
Any other testing of it is stalled by there being no linux backend for it as part of this series. I see from spelunking repos that Johannes had a WIP compat/fsmonitor/linux.c which looks like it could/should mostly work, but the API names all changed since then, and after a short try I gave up on trying to rebase it.
I'd really prefer for git not to have features that place free platforms at a disadvantage against proprietary platforms if it can be avoided, and in this case the lack of a Linux backend also means much less widespread testing of the feature among the development community / CI.
Quoted 14 lines · Ævar Arnfjörð Bjarmason
Quoted 10 lines · Jeff Hostetler via GitGitGadget
Here is V3 of my patch series to add a builtin FSMonitor daemon to Git. I rebased this series onto v2.32.0.
V3 addresses most of the previous review comments and things we've learned from our experimental testing of V2. (A version of V2 was shipped as an experimental feature in the v2.32.0-based releases of Git for Windows and VFS for Git.)
There are still a few items that I need to address, but that list is getting very short.
Quoted 6 lines
...
Quoted 2 lines
fsmonitor-fs-listen-win32: stub in backend for Windows
fsmonitor-fs-listen-macos: stub in backend for MacOSI left some light comments on the
repo-settings.cpart of this to follow up from a previous round.
Thanks.
Quoted 6 lines
Any other testing of it is stalled by there being no linux backend for it as part of this series. I see from spelunking repos that Johannes had a WIP
compat/fsmonitor/linux.cwhich looks like it could/should mostly work, but the API names all changed since then, and after a short try I gave up on trying to rebase it.
The early Linux version was dropped because inotify does not give recursive coverage -- only the requested directory. Using inotify requires adding a watch to each subdirectory (recursively) in the worktree. There's a system limit on the maximum number of watched directories (defaults to 8K IIRC) and that limit is system-wide.
Since the whole point was to support large very large repos, using inotify was a non-starter, so I removed the Linux version from our patch series. For example, the first repo I tried it on (outside of the test suite) had 25K subdirectories.
I'm told there is a new fanotify API in recent Linux kernels that is a better fit for what we need, but we haven't had time to investigate it yet.
Quoted 6 lines
I'd really prefer for git not to have features that place free platforms at a disadvantage against proprietary platforms if it can be avoided, and in this case the lack of a Linux backend also means much less widespread testing of the feature among the development community / CI.
This feature is always going to have platform-specific components, so the lack of one platform or another should not stop us from discussing it for the platforms that can be supported.
And given the size and complexity of the platform-specific code, we should not assume that "just test it on Linux" is sufficient. Yes, there are some common/shared routines/data structures in the daemon, but hard/tricky parts are in the platform layer.
Jeff
Re: [PATCH v3 23/34] t/helper/test-touch: add helper to touch a series of files
In reply to Jeff Hostetler via GitGitGadgetQuoted 9 lines · Jeff Hostetler via GitGitGadget
t/helper/test-touch.c new file +3
-0@@ -0,0 +1,126 @@ +/* +* test-touch.c: variation on /usr/bin/touch to speed up tests +* with a large number of files (primarily on Windows where child
t/helper/test-touch.c line 13
Quoted 10 lines
t/helper/test-touch.c new file +10
-0+* process are very, very expensive). +*/ + +#include "test-tool.h" +#include "cache.h" +#include "parse-options.h" + +char *seq_pattern; +int seq_start =1 ; +int seq_count =1 ;
With this in, "make sparse" dies like this:
SP t/helper/test-touch.ct/helper/test-touch.c:11:6: error: symbol 'seq_pattern' was not declared. Should it be static? t/helper/test-touch.c:12:5: error: symbol 'seq_start' was not declared. Should it be static? t/helper/test-touch.c:13:5: error: symbol 'seq_count' was not declared. Should it be static?
Quoted 35 lines · Jeff Hostetler
Quoted 10 lines · Ævar Arnfjörð Bjarmason
Quoted 8 lines · Jeff Hostetler via GitGitGadget
Here is V3 of my patch series to add a builtin FSMonitor daemon to Git. I rebased this series onto v2.32.0.
V3 addresses most of the previous review comments and things we've learned from our experimental testing of V2. (A version of V2 was shipped as an experimental feature in the v2.32.0-based releases of Git for Windows and VFS for Git.)
Quoted 8 lines
Quoted 2 lines
There are still a few items that I need to address, but that list is getting very short.
...
Quoted 2 lines
fsmonitor-fs-listen-win32: stub in backend for Windows
fsmonitor-fs-listen-macos: stub in backend for MacOSI left some light comments on the
repo-settings.cpart of this to follow
up from a previous round.Thanks.
Quoted 6 lines
Any other testing of it is stalled by there being no linux backend for
it as part of this series. I see from spelunking repos that Johannes had a WIPcompat/fsmonitor/linux.cwhich looks like it could/should mostly work, but the API names all changed since then, and after a short try I gave up on trying to rebase it.The early Linux version was dropped because inotify does not give recursive coverage -- only the requested directory. Using inotify requires adding a watch to each subdirectory (recursively) in the worktree. There's a system limit on the maximum number of watched directories (defaults to 8K IIRC) and that limit is system-wide.
Quoted 8 lines
Since the whole point was to support large very large repos, using inotify was a non-starter, so I removed the Linux version from our patch series. For example, the first repo I tried it on (outside of the test suite) had 25K subdirectories.
I'm told there is a new fanotify API in recent Linux kernels that is a better fit for what we need, but we haven't had time to investigate it yet.
That default limit is a bit annoying, but I don't see how it's a blocker in any way.
You simply adjust the limit. E.g. I deployed and tested the hook version of inotify (using watchman) in a sizable development environment, and written my own code using the API. This was all before fanotify(7) existed. IIRC I set most of the limits to 2^24 or 2^20. I've used it with really large Git repos, including with David Turner's 2015-04-03-1M-git for testing (git ls-files | wc -l on that is around a quarter-million).
If you have a humongous repository and don't have root on your own machine you're out of luck. But I think most people who'd use this are either using their own laptop, or e.g. in a corporate setting where administrators would tweak the sysctl limits given the performance advantages (as I did).
Once you adjust the limits Linux deals with large trees just fine, it just has overly conservative limits for most things in sysctl. The API is a bit annoying, your event loop needs to run around and add watches.
AFAICT you need Linux 5.1 for fanotify(7) to be useful, e.g. Debian stable, RHEL etc. aren't using anything that new. So having an inotify backend as well as possibly a fanotify one would be very useful.
And linux.git's docs suggest that the default limit was bumped from 8192 to 1M in v5.10, a really recent kernel, so if you've got that you've also got fanotify.
In any case, even if Linux's inotify limit was something hardcoded and impossible to change you could still use such an API to test & debug the basics of this feature on that platform.
Quoted 10 lines
Quoted 6 lines
I'd really prefer for git not to have features that place free platforms
at a disadvantage against proprietary platforms if it can be avoided, and in this case the lack of a Linux backend also means much less widespread testing of the feature among the development community / CI.This feature is always going to have platform-specific components, so the lack of one platform or another should not stop us from discussing it for the platforms that can be supported.
(I think per the above that's s/can be/are/)
Quoted 4 lines
And given the size and complexity of the platform-specific code, we should not assume that "just test it on Linux" is sufficient. Yes, there are some common/shared routines/data structures in the daemon, but hard/tricky parts are in the platform layer.
I think we're talking past each other a bit here. I'm not saying that you can get full or meaningful testing for it on Windows if you test it on Linux, or the other way around.
Of course there's platform-specific stuff, although there's also a lot of non-platform-specific stuff, so even a very basic implementation of inotify would make reviwing this easier / give access to more reviewers.
I'm saying that I prefer that Git as a free software project not be in the situation of saying the best use-case for a given size/shape of repo is to use Git in combination with proprietary operating systems over freely licensed ones.
IOW what the FSF has a policy for GNU projects. Now, I think the FSF probably goes too far in that (famously removing rather obscure font rendering features from Emacs on OSX), but "manage lots of data" is a core feature of git.
Re: [PATCH v3 14/34] fsmonitor--daemon: implement 'start' command
In reply to Jeff Hostetler via GitGitGadgetQuoted 68 lines · Jeff Hostetler via GitGitGadget
builtin/fsmonitor--daemon.c +68
-0+#ifdef GIT_WINDOWS_NATIVE +/* +* Create a background process to run the daemon. It should be completely +* disassociated from the terminal. +* +* Conceptually like `daemonize()` but different because Windows does not +* have `fork(2)`. Spawn a normal Windows child process but without the +* limitations of `start_command()` and `finish_command()`. +* +* The child process execs the "git fsmonitor--daemon run" command. +* +* The current process returns so that the caller can wait for the child +* to startup before exiting. +*/ +static int spawn_background_fsmonitor_daemon (pid_t *pid) +{ +char git_exe[MAX_PATH ]; +struct strvec args =STRVEC_INIT ; +int in, out; + +GetModuleFileNameA (NULL , git_exe,MAX_PATH ); + + in =open ("/dev/null" ,O_RDONLY ); + out =open ("/dev/null" ,O_WRONLY ); + +strvec_push (&args, git_exe); +strvec_push (&args,"fsmonitor--daemon" ); +strvec_push (&args,"run" ); +strvec_pushf (&args,"--ipc-threads=%d" , fsmonitor__ipc_threads); + + *pid =mingw_spawnvpe (args.v[0 ], args.v,NULL ,NULL , in, out, out); +close (in); +close (out); + +strvec_clear (&args); + +if (*pid <0 ) +return error (_ ("could not spawn fsmonitor--daemon in the background" )); + +return 0 ; +} +#else +/* +* Create a background process to run the daemon. It should be completely +* disassociated from the terminal. +* +* This is adapted from `daemonize()`. Use `fork()` to directly +* create and run the daemon in the child process. +* +* The fork-child can just call the run code; it does not need to exec +* it. +* +* The fork-parent returns the child PID so that we can wait for the +* child to startup before exiting. +*/ +static int spawn_background_fsmonitor_daemon (pid_t *pid) +{ + *pid =fork (); + +switch (*pid) { +case 0 : +if (setsid () ==-1 ) +error_errno (_ ("setsid failed" )); +close (0 ); +close (1 ); +close (2 ); +sanitize_stdfds (); +
builtin/fsmonitor--daemon.c static int try_to_run_foreground_daemon(void) · line 349
Quoted 10 lines
builtin/fsmonitor--daemon.c +10
-0+return !!fsmonitor_run_daemon (); + +case -1 : +return error_errno (_ ("could not spawn fsmonitor--daemon in the background" )); + +default : + return 0; + } +} +#endif
The spawn_background_fsmonitor_daemon() function here is almost the same as daemonize(). I wonder if this & the Windows-specific one you have here can't be refactored into an API from what's now in setup.c.
Then we could make builtin/gc.c and daemon.c use that, so Windows could have background GC, and we'd have a more battle-tested central codepath for this tricky bit.
It seems to me like the only limitations on it are to have this return slightly more general things (e.g. not set its own errors, return structured data), and maybe some callback for what to do in the child/parent.
Quoted 59 lines
builtin/fsmonitor--daemon.c +59
-0+/* +* This is adapted from `wait_or_whine()`. Watch the child process and +* let it get started and begin listening for requests on the socket +* before reporting our success. +*/ +static int wait_for_background_startup (pid_t pid_child) +{ +int status; +pid_t pid_seen; +enum ipc_active_state s; +time_t time_limit, now; + +time (&time_limit); + time_limit += fsmonitor__start_timeout_sec; + +for (;;) { + pid_seen =waitpid (pid_child, &status,WNOHANG ); + +if (pid_seen ==-1 ) +return error_errno (_ ("waitpid failed" )); +else if (pid_seen ==0 ) { +/* +* The child is still running (this should be +* the normal case). Try to connect to it on +* the socket and see if it is ready for +* business. +* +* If there is another daemon already running, +* our child will fail to start (possibly +* after a timeout on the lock), but we don't +* care (who responds) if the socket is live. +*/ + s =fsmonitor_ipc__get_state (); +if (s ==IPC_STATE__LISTENING ) +return 0 ; + +time (&now); +if (now > time_limit) +return error (_ ("fsmonitor--daemon not online yet" )); + }else if (pid_seen == pid_child) { +/* +* The new child daemon process shutdown while +* it was starting up, so it is not listening +* on the socket. +* +* Try to ping the socket in the odd chance +* that another daemon started (or was already +* running) while our child was starting. +* +* Again, we don't care who services the socket. +*/ + s =fsmonitor_ipc__get_state (); +if (s ==IPC_STATE__LISTENING ) +return 0 ; + +/* +* We don't care about the WEXITSTATUS() nor +* any of the WIF*(status) values because +* `cmd_fsmonitor__daemon()` does the `!!result`
builtin/fsmonitor--daemon.c static int try_to_run_foreground_daemon(void) · line 419
Quoted 10 lines
builtin/fsmonitor--daemon.c +10
-0+* trick on all function return values. +* +* So it is sufficient to just report the +* early shutdown as an error. +*/ +return error (_ ("fsmonitor--daemon failed to start" )); + }else +return error (_ ("waitpid is confused" )); + } +}
Ditto this. could we extend the wait_or_whine() function (or some extended version thereof) to do what you need with callbacks?
It seems the main difference is just being able to pass down a flag for waitpid(), and the loop needing to check EINTR or not depending on whether WNOHANG is passed.
For e.g. the "We don't care about the WEXITSTATUS()" you'd get that behavior with an adjusted wait_or_whine(). Wouldn't it be better to report what exit status it exits with e.g. if the top-level process is signalled? We do so in trace2 for other things we spawn...
Re: [PATCH v3 02/34] fsmonitor--daemon: man page
In reply to Jeff Hostetler via GitGitGadgetQuoted 81 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Create a manual page describing the
git fsmonitor--daemonfeature.Signed-off-by Jeff Hostetler <[email protected]>--- Documentation/git-fsmonitor--daemon.txt | 75 +++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 Documentation/git-fsmonitor--daemon.txtDocumentation/git-fsmonitor--daemon.txt new file +65
-0@@ -0,0 +1,75 @@ +git-fsmonitor--daemon(1) +======================== + +NAME +---- +git-fsmonitor--daemon - A Built-in File System Monitor + +SYNOPSIS +-------- +[verse] +'git fsmonitor--daemon' start +'git fsmonitor--daemon' run +'git fsmonitor--daemon' stop +'git fsmonitor--daemon' status + +DESCRIPTION +----------- + +A daemon to watch the working directory for file and directory +changes using platform-specific file system notification facilities. + +This daemon communicates directly with commands like `git status` +using the link:technical/api-simple-ipc.html[simple IPC] interface +instead of the slower linkgit:githooks[5] interface. + +This daemon is built into Git so that no third-party tools are +required. + +OPTIONS +------- + +start:: + Starts a daemon in the background. + +run:: + Runs a daemon in the foreground. + +stop:: + Stops the daemon running in the current working + directory, if present. + +status:: + Exits with zero status if a daemon is watching the + current working directory. + +REMARKS +------- + +This daemon is a long running process used to watch a single working +directory and maintain a list of the recently changed files and +directories. Performance of commands such as `git status` can be +increased if they just ask for a summary of changes to the working +directory and can avoid scanning the disk. + +When `core.useBuiltinFSMonitor` is set to `true` (see +linkgit:git-config[1]) commands, such as `git status`, will ask the +daemon for changes and automatically start it (if necessary). + +For more information see the "File System Monitor" section in +linkgit:git-update-index[1]. + +CAVEATS +------- + +The fsmonitor daemon does not currently know about submodules and does
Documentation/git-fsmonitor--daemon.txt line 75
Quoted 10 lines
Documentation/git-fsmonitor--daemon.txt new file +10
-0+not know to filter out file system events that happen within a +submodule. If fsmonitor daemon is watching a super repo and a file is +modified within the working directory of a submodule, it will report +the change (as happening against the super repo). However, the client +will properly ignore these extra events, so performance may be affected +but it will not cause an incorrect result. + +GIT +--- +Part of the linkgit:git[1] suite
Later in the series we incrementally add features to the daemon, so this is describing a state that doesn't exist yet at this point.
I think it would be better to start with a stup here and add documentation as we add features, e.g. the patch tha adds "start" should add that to the synopsis + options etc.
See the outstanding ab/config-based-hooks-base for a small example of that.
Re: [PATCH v3 03/34] fsmonitor--daemon: update fsmonitor documentation
In reply to Jeff Hostetler via GitGitGadgetQuoted 131 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Update references to
core.fsmonitorandcore.fsmonitorHookVersionand pointers toWatchmanto mention the new built-infsmonitor--daemon.Signed-off-by Jeff Hostetler <[email protected]>--- Documentation/config/core.txt | 56 ++++++++++++++++++++++-------- Documentation/git-update-index.txt | 27 +++++++------- Documentation/githooks.txt | 3 +- 3 files changed, 59 insertions(+), 27 deletions(-)Documentation/config/core.txt +42
-14@@ -62,22 +62,50 @@ core.protectNTFS:: Defaults to `true` on Windows, and `false` elsewhere. core.fsmonitor:: - If set, the value of this variable is used as a command which - will identify all files that may have changed since the - requested date/time. This information is used to speed up git by - avoiding unnecessary processing of files that have not changed. - See the "fsmonitor-watchman" section of linkgit:githooks[5]. + If set, this variable contains the pathname of the "fsmonitor" + hook command. ++ +This hook command is used to identify all files that may have changed +since the requested date/time. This information is used to speed up +git by avoiding unnecessary scanning of files that have not changed. ++ +See the "fsmonitor-watchman" section of linkgit:githooks[5]. ++ +Note: The value of this config setting is ignored if the +built-in file system monitor is enabled (see `core.useBuiltinFSMonitor`). core.fsmonitorHookVersion:: - Sets the version of hook that is to be used when calling fsmonitor. - There are currently versions 1 and 2. When this is not set, - version 2 will be tried first and if it fails then version 1 - will be tried. Version 1 uses a timestamp as input to determine - which files have changes since that time but some monitors - like watchman have race conditions when used with a timestamp. - Version 2 uses an opaque string so that the monitor can return - something that can be used to determine what files have changed - without race conditions. + Sets the protocol version to be used when invoking the + "fsmonitor" hook. ++ +There are currently versions 1 and 2. When this is not set, +version 2 will be tried first and if it fails then version 1 +will be tried. Version 1 uses a timestamp as input to determine +which files have changes since that time but some monitors +like Watchman have race conditions when used with a timestamp. +Version 2 uses an opaque string so that the monitor can return +something that can be used to determine what files have changed +without race conditions. ++ +Note: The value of this config setting is ignored if the +built-in file system monitor is enabled (see `core.useBuiltinFSMonitor`). + +core.useBuiltinFSMonitor:: + If set to true, enable the built-in file system monitor + daemon for this working directory (linkgit:git-fsmonitor--daemon[1]). ++ +Like hook-based file system monitors, the built-in file system monitor +can speed up Git commands that need to refresh the Git index +(e.g. `git status`) in a working directory with many files. The +built-in monitor eliminates the need to install and maintain an +external third-party tool. ++ +The built-in file system monitor is currently available only on a +limited set of supported platforms. Currently, this includes Windows +and MacOS. ++ +Note: if this config setting is set to `true`, the values of +`core.fsmonitor` and `core.fsmonitorHookVersion` are ignored. core.trustctime:: If false, the ctime differences between the index and theDocumentation/git-update-index.txt +15
-12@@ -498,7 +498,9 @@ FILE SYSTEM MONITOR This feature is intended to speed up git operations for repos that have large working directories. -It enables git to work together with a file system monitor (see the +It enables git to work together with a file system monitor (see +linkgit:git-fsmonitor--daemon[1] +and the "fsmonitor-watchman" section of linkgit:githooks[5]) that can inform it as to what files have been modified. This enables git to avoid having to lstat() every file to find modified files.@@ -508,17 +510,18 @@ performance by avoiding the cost of scanning the entire working directory looking for new files. If you want to enable (or disable) this feature, it is easier to use -the `core.fsmonitor` configuration variable (see -linkgit:git-config[1]) than using the `--fsmonitor` option to -`git update-index` in each repository, especially if you want to do so -across all repositories you use, because you can set the configuration -variable in your `$HOME/.gitconfig` just once and have it affect all -repositories you touch. - -When the `core.fsmonitor` configuration variable is changed, the -file system monitor is added to or removed from the index the next time -a command reads the index. When `--[no-]fsmonitor` are used, the file -system monitor is immediately added to or removed from the index. +the `core.fsmonitor` or `core.useBuiltinFSMonitor` configuration +variable (see linkgit:git-config[1]) than using the `--fsmonitor` +option to `git update-index` in each repository, especially if you +want to do so across all repositories you use, because you can set the +configuration variable in your `$HOME/.gitconfig` just once and have +it affect all repositories you touch. + +When the `core.fsmonitor` or `core.useBuiltinFSMonitor` configuration +variable is changed, the file system monitor is added to or removed +from the index the next time a command reads the index. When +`--[no-]fsmonitor` are used, the file system monitor is immediately +added to or removed from the index. CONFIGURATION -------------Documentation/githooks.txt +0
-0
Documentation/githooks.txt fsmonitor-watchman · line 600
Quoted 9 lines
Documentation/githooks.txt +2
-1@@ -593,7 +593,8 @@ fsmonitor-watchman This hook is invoked when the configuration option `core.fsmonitor` is set to `.git/hooks/fsmonitor-watchman` or `.git/hooks/fsmonitor-watchmanv2` -depending on the version of the hook to use. +depending on the version of the hook to use, unless overridden via +`core.useBuiltinFSMonitor` (see linkgit:git-config[1]). Version 1 takes two arguments, a version (1) and the time in elapsed nanoseconds since midnight, January 1, 1970.
Ditto the comment on 02/34, mostly docs for things that don't exist until in later patches.
Re: [PATCH v3 08/34] fsmonitor--daemon: add a built-in fsmonitor daemon
In reply to Jeff Hostetler via GitGitGadgetOn Thu, Jul 01 2021, Jeff Hostetler via GitGitGadget wrote:
A general comment on this series (including previous patches). We've usually tried to bend over backwards in git's codebase not to have big ifdef blocks, so we compile most code the same everywhere. We waste a bit of object code, but that's fine.
See 9c897c5c2ad (pack-objects: remove #ifdef NO_PTHREADS, 2018-11-03) for a good exmaple of bad code being turned to good.
E.g. in this case:
Quoted 31 lines
builtin/fsmonitor--daemon.c new file +31
-0+#ifdef HAVE_FSMONITOR_DAEMON_BACKEND + +int cmd_fsmonitor__daemon (int argc,const char **argv,const char *prefix) +{ +const char *subcmd; + +struct option options[] = { +OPT_END () + }; + +if (argc <2 ) +usage_with_options (builtin_fsmonitor__daemon_usage, options); + +if (argc ==2 && !strcmp (argv[1 ],"-h" )) +usage_with_options (builtin_fsmonitor__daemon_usage, options); + +git_config (git_default_config,NULL ); + + subcmd = argv[1 ]; + argv--; + argc++; + + argc =parse_options (argc, argv, prefix, options, + builtin_fsmonitor__daemon_usage,0 ); + +die (_ ("Unhandled subcommand '%s'" ), subcmd); +} + +#else +int cmd_fsmonitor__daemon (int argc,const char **argv,const char *prefix) +{
builtin/fsmonitor--daemon.c line 53
Quoted 10 lines
builtin/fsmonitor--daemon.c new file +10
-0+struct option options[] = { +OPT_END () + }; + +if (argc ==2 && !strcmp (argv[1 ],"-h" )) +usage_with_options (builtin_fsmonitor__daemon_usage, options); + + die(_("fsmonitor--daemon not supported on this platform")); +} +#endif
This whole thing could really just be a -DHAVE_FSMONITOR_DAEMON_BACKEND=1 or -DHAVE_FSMONITOR_DAEMON_BACKEND=0 somewhere (depending), and then somewhere in the middle of the first function:
if (!HAVE_FSMONITOR_DAEMON_BACKEND)
die(_("fsmonitor--daemon not supported on this platform"));Re: [PATCH v3 10/34] t/helper/fsmonitor-client: create IPC client to talk to FSMonitor Daemon
In reply to Jeff Hostetler via GitGitGadgetQuoted 37 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Create an IPC client to send query and flush commands to the daemon.
Signed-off-by Jeff Hostetler <[email protected]>--- Makefile | 1 + t/helper/test-fsmonitor-client.c | 121 +++++++++++++++++++++++++++++++ t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + 4 files changed, 124 insertions(+) create mode 100644 t/helper/test-fsmonitor-client.cMakefile +1
-0@@ -709,6 +709,7 @@ TEST_BUILTINS_OBJS += test-dump-split-index.o TEST_BUILTINS_OBJS += test-dump-untracked-cache.o TEST_BUILTINS_OBJS += test-example-decorate.o TEST_BUILTINS_OBJS += test-fast-rebase.o +TEST_BUILTINS_OBJS += test-fsmonitor-client.o TEST_BUILTINS_OBJS += test-genrandom.o TEST_BUILTINS_OBJS += test-genzeros.o TEST_BUILTINS_OBJS += test-hash-speed.ot/helper/test-fsmonitor-client.c new file +6
-0@@ -0,0 +1,121 @@ +/* +* test-fsmonitor-client.c: client code to send commands/requests to +* a `git fsmonitor--daemon` daemon. +*/ + +#include "test-tool.h"
t/helper/test-fsmonitor-client.c line 16
Quoted 10 lines
t/helper/test-fsmonitor-client.c new file +10
-0+#include "cache.h" +#include "parse-options.h" +#include "fsmonitor-ipc.h" + +#ifndef HAVE_FSMONITOR_DAEMON_BACKEND +int cmd__fsmonitor_client (int argc,const char **argv) +{ +die ("fsmonitor--daemon not available on this platform" ); +} +#else
Re my earlier comments on excessive ifdefs: In this case don't we just want to not compile test-fsmonitor-client at all unless HAVE_FSMONITOR_DAEMON_BACKEND is true.
You'll get the same error as though you ran "helper/test-tool does-not-exist", but the tests check for the prerequisite earlier anyway, so why get this far on an unsupported platform for a pure test helper?
Re: [PATCH v3 11/34] fsmonitor-fs-listen-win32: stub in backend for Windows
In reply to Jeff Hostetler via GitGitGadgetQuoted 44 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Stub in empty backend for fsmonitor--daemon on Windows.
Signed-off-by Jeff Hostetler <[email protected]>--- Makefile | 13 ++++++ compat/fsmonitor/fsmonitor-fs-listen-win32.c | 21 +++++++++ compat/fsmonitor/fsmonitor-fs-listen.h | 49 ++++++++++++++++++++ config.mak.uname | 2 + contrib/buildsystems/CMakeLists.txt | 5 ++ 5 files changed, 90 insertions(+) create mode 100644 compat/fsmonitor/fsmonitor-fs-listen-win32.c create mode 100644 compat/fsmonitor/fsmonitor-fs-listen.hMakefile +10
-0@@ -467,6 +467,11 @@ all:: # directory, and the JSON compilation database 'compile_commands.json' will be # created at the root of the repository. # +# If your platform supports a built-in fsmonitor backend, set +# FSMONITOR_DAEMON_BACKEND to the "<name>" of the corresponding +# `compat/fsmonitor/fsmonitor-fs-listen-<name>.c` that implements the +# `fsmonitor_fs_listen__*()` routines. +# # Define DEVELOPER to enable more compiler warnings. Compiler version # and family are auto detected, but could be overridden by defining # COMPILER_FEATURES (see config.mak.dev). You can still set@@ -1929,6 +1934,11 @@ ifdef NEED_ACCESS_ROOT_HANDLER COMPAT_OBJS += compat/access.o endif +ifdef FSMONITOR_DAEMON_BACKEND + COMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND + COMPAT_OBJS += compat/fsmonitor/fsmonitor-fs-listen-$(FSMONITOR_DAEMON_BACKEND).o +endif + ifeq ($(TCLTK_PATH),) NO_TCLTK = NoThanks endif
Makefile GIT-BUILD-OPTIONS: FORCE · line 2808
Quoted 6 lines
Makefile +3
-0@@ -2793,6 +2803,9 @@ GIT-BUILD-OPTIONS: FORCE @echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+ @echo DC_SHA1=\''$(subst ','\'',$(subst ','\'',$(DC_SHA1)))'\' >>$@+ @echo X=\'$(X)\' >>$@+ +ifdef FSMONITOR_DAEMON_BACKEND + @echo FSMONITOR_DAEMON_BACKEND=\''$(subst ','\'',$(subst ','\'',$(FSMONITOR_DAEMON_BACKEND)))'\' >>$@+ +endif
Why put this in an ifdef?
In 342e9ef2d9e (Introduce a performance testing framework, 2012-02-17) we started doing that for some perf/test options (which b.t.w., I don't really see the reason for, maybe it's some subtlety in how test-lib.sh picks those up).
But for all the other compile-time stuff we don't ifdef it, we just define it, and then you get an empty value or not.
This would AFAICT be the first build-time-for-the-C-program option we ifdef for writing a line to GIT-BUILD-OPTIONS.
Re: [PATCH v3 12/34] fsmonitor-fs-listen-macos: stub in backend for MacOS
In reply to Jeff Hostetler via GitGitGadgetQuoted 44 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Stub in empty implementation of fsmonitor--daemon
backend for MacOS.Signed-off-by Jeff Hostetler <[email protected]>--- compat/fsmonitor/fsmonitor-fs-listen-macos.c | 20 ++++++++++++++++++++ config.mak.uname | 2 ++ contrib/buildsystems/CMakeLists.txt | 3 +++ 3 files changed, 25 insertions(+) create mode 100644 compat/fsmonitor/fsmonitor-fs-listen-macos.ccompat/fsmonitor/fsmonitor-fs-listen-macos.c new file +20
-0@@ -0,0 +1,20 @@ +#include "cache.h" +#include "fsmonitor.h" +#include "fsmonitor-fs-listen.h" + +int fsmonitor_fs_listen__ctor (struct fsmonitor_daemon_state *state) +{ +return -1 ; +} + +void fsmonitor_fs_listen__dtor (struct fsmonitor_daemon_state *state) +{ +} + +void fsmonitor_fs_listen__stop_async (struct fsmonitor_daemon_state *state) +{ +} + +void fsmonitor_fs_listen__loop (struct fsmonitor_daemon_state *state) +{ +}config.mak.uname +0
-0
config.mak.uname ifeq ($(uname_S),Darwin) · line 150
Quoted 4 lines
config.mak.uname +1
-0@@ -147,6 +147,8 @@ ifeq ($(uname_S),Darwin) MSGFMT = /usr/local/opt/gettext/bin/msgfmt endif endif + FSMONITOR_DAEMON_BACKEND = macos
A rather trivial point, but can't we pick one of "macos" or "darwin" (I'd think going with the existing uname is better) and name the file after the uname (or lower-case thereof)?
Makes these make rules more consistent too, we could just set this to "YesPlease" here, and then lower case the uname for the file compilation/include.
Re: [PATCH v3 15/34] fsmonitor: do not try to operate on bare repos
In reply to Jeff Hostetler via GitGitGadgetQuoted 17 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Bare repos do not have a working directory, so there is no directory for the daemon to register a watch upon. And therefore there are no files within the directory for it to actually watch.
Signed-off-by Jeff Hostetler <[email protected]>--- builtin/fsmonitor--daemon.c | 8 ++++++++ t/t7519-status-fsmonitor.sh | 16 ++++++++++++++++ 2 files changed, 24 insertions(+)builtin/fsmonitor--daemon.c +0
-0
builtin/fsmonitor--daemon.c int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix) · line 499
Quoted 10 lines
builtin/fsmonitor--daemon.c +7
-0@@ -490,6 +490,14 @@ 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); +if (!the_repository->worktree) +return error (_ ("fsmonitor-daemon does not support bare repos '%s'" ), +xgetcwd ()); +if (the_repository->settings.fsmonitor_mode ==FSMONITOR_MODE_INCOMPATIBLE ) +return error (_ ("fsmonitor-daemon is incompatible with this repo '%s'" ), + the_repository->worktree);
I commented on another patch that that second condition seems like it's only hit under core.virtualfilesystem=true....
builtin/fsmonitor--daemon.c int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
Quoted 6 lines
builtin/fsmonitor--daemon.c +6
-0+test_expect_success FSMONITOR_DAEMON 'try running fsmonitor-daemon in bare repo' ' + test_when_finished"rm -rf ./bare-clone" && + git clone --bare . ./bare-clone && + test_must_fail git -C ./bare-clone fsmonitor--daemon run 2>actual && + grep"fsmonitor-daemon does not support bare repos" actual +'
Isn't just:
git init --bare bare.git
test_must_fail git -C bare.git [...]
Enough, or does the repository need content to get to that error.
builtin/fsmonitor--daemon.c
Quoted 9 lines
builtin/fsmonitor--daemon.c +9
-0+test_expect_success FSMONITOR_DAEMON 'try running fsmonitor-daemon in virtual repo' ' + test_when_finished"rm -rf ./fake-virtual-clone" && + git clone . ./fake-virtual-clone && + test_must_fail git -C ./fake-virtual-clone \ + -c core.virtualfilesystem=true \ + fsmonitor--daemon run 2>actual && + grep"fsmonitor-daemon is incompatible with this repo" actual +' +
Ditto.
Re: [PATCH v3 17/34] fsmonitor--daemon: define token-ids
In reply to Jeff Hostetler via GitGitGadgetQuoted 6 lines · Jeff Hostetler via GitGitGadget
builtin/fsmonitor--daemon.c +6
-0+if (!test_env_value) { +struct timeval tv; +struct tm tm; +time_t secs; + +gettimeofday (&tv,NULL );
builtin/fsmonitor--daemon.c static int do_as_client__status(void) · line 202
Quoted 10 lines
builtin/fsmonitor--daemon.c +10
-0+ secs = tv.tv_sec; +gmtime_r (&secs, &tm); + +strbuf_addf (&token->token_id, +"%" PRIu64".%d.%4d%02d%02dT%02d%02d%02d.%06ldZ" , + flush_count++, +getpid (), + tm.tm_year +1900 , tm.tm_mon +1 , tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, + (long )tv.tv_usec);
Just bikeshedding, but can we have tokens that mostly sort numeric-wise by time order? So time at the start, not the flush_count/getpid.
Maybe I'm missing something, but couldn't we just re-use the trace2 SID
+ a more trivial trailer? It would have the nice property that you could find the trace2 SID whenever you looked at such a token (could e.g. split them by "/" too), and add the tv_usec, flush_count+whatever else is needed to make it unique after the "/", no?
Re: [PATCH v3 19/34] fsmonitor-fs-listen-win32: implement FSMonitor backend on Windows
In reply to Jeff Hostetler via GitGitGadgetQuoted 10 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Teach the win32 backend to register a watch on the working tree root directory (recursively). Also watch the <gitdir> if it is not inside the working tree. And to collect path change notifications into batches and publish.
Signed-off-by Jeff Hostetler <[email protected]>--- compat/fsmonitor/fsmonitor-fs-listen-win32.c | 530 +++++++++++++++++++
<bikeshed mode> Spying on the early history of this (looking for the Linux backend) I saw that at some point we had just compat/fsmonitor/linux.c, and presumably some of compat/fsmonitor/{windows,win32,macos,darwin}.c.
At some point those filenames became much much longer.
I've noticed you tend to prefer really long file and function names, e.g. your borrowed daemonize() became spawn_background_fsmonitor_daemon(), I think aiming for shorter filenames & function names helps, e.g. these long names widen diffstats, and many people who hack on the code stick religiously to 80 character width terminals.
Re: [PATCH v3 24/34] t/perf/p7519: speed up test using "test-tool touch"
In reply to Jeff Hostetler via GitGitGadgetQuoted 40 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Change p7519 to use a single "test-tool touch" command to update the mtime on a series of (thousands) files instead of invoking thousands of commands to update a single file.
This is primarily for Windows where process creation is so very slow and reduces the test run time by minutes.
Signed-off-by Jeff Hostetler <[email protected]>--- t/perf/p7519-fsmonitor.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)t/perf/p7519-fsmonitor.sh +5
-6@@ -119,10 +119,11 @@ test_expect_success "one time repo setup" ' fi && mkdir 1_file 10_files 100_files 1000_files 10000_files && - for i in $(test_seq 1 10); do touch 10_files/$i; done && - for i in $(test_seq 1 100); do touch 100_files/$i; done && - for i in $(test_seq 1 1000); do touch 1000_files/$i; done && - for i in $(test_seq 1 10000); do touch 10000_files/$i; done && + test-tool touch sequence --pattern="10_files/%d" --start=1 --count=10 && + test-tool touch sequence --pattern="100_files/%d" --start=1 --count=100 && + test-tool touch sequence --pattern="1000_files/%d" --start=1 --count=1000 && + test-tool touch sequence --pattern="10000_files/%d" --start=1 --count=10000 && + git add 1_file 10_files 100_files 1000_files 10000_files && git commit -qm "Add files" &&@@ -200,15 +201,12 @@ test_fsmonitor_suite() { # Update the mtimes on upto 100k files to make status think # that they are dirty. For simplicity, omit any files with # LFs (i.e. anything that ls-files thinks it needs to dquote). - # Then fully backslash-quote the paths to capture any - # whitespace so that they pass thru xargs properly.
t/perf/p7519-fsmonitor.sh test_fsmonitor_suite() { · line 211
Quoted 10 lines
t/perf/p7519-fsmonitor.sh +1
-2# test_perf_w_drop_caches "status (dirty) ($DESC)" ' git ls-files | \ head -100000 | \ grep -v \" | \ - sed '\''s/\(.\)/\\\1/g'\'' | \ - xargs test-tool chmtime -300 && + test-tool touch stdin && git status '
Did you try to replace this with some variant of:
test_seq 1 10000 | xargs touch
Which (depending on your xargs version) would invoke "touch" commands with however many argv items it thinks you can handle.
Re: [PATCH v3 25/34] t/perf: avoid copying builtin fsmonitor files into test repo
In reply to Jeff Hostetler via GitGitGadgetQuoted 4 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Do not try to copy a fsmonitor--daemon socket from the current development directory into the test trash directory.
Okey, the */fsmonitor--daemon* rule covers that...
Quoted 7 lines
When we run the perf suite without an explicit source repo set, we copy of the current $GIT_DIR into the test trash directory. Unix domain sockets cannot be copied in that manner, so the test setup fails.
Additionally, omit any other fsmonitor--daemon temp files inside the $GIT_DIR directory.
So is the "any other" also matched by that rule? Not knowing the files part of this is just phrasing, would be less confusing (if that's true, and you didn't just forget to add a match for them) as:
The */fsmonitor--daemon* glob will also match temporary files the daemon creates, but that's OK. We'd like to ignore these too.
Re: [PATCH v3 27/34] t7527: create test for fsmonitor--daemon
In reply to Jeff Hostetler via GitGitGadgetQuoted 6 lines · Jeff Hostetler via GitGitGadget
t/t7527-builtin-fsmonitor.sh new file +6
-0+ grep \"event\":\"start\".*\"fsmonitor--daemon\" .git/trace_implicit_1 +' + +test_expect_success 'status implicitly starts daemon' ' + test_when_finished redundant_stop_daemon && +
t/t7527-builtin-fsmonitor.sh line 198
Quoted 10 lines
t/t7527-builtin-fsmonitor.sh new file +10
-0+ test_must_fail git fsmonitor--daemon status && + + GIT_TRACE2_EVENT="$(pwd)/.git/trace_implicit_2" \ + git status >actual && + + git fsmonitor--daemon status && + test_might_fail git fsmonitor--daemon stop && + + grep \"event\":\"start\".*\"fsmonitor--daemon\" .git/trace_implicit_2 +'
Seems like this and test_region could eventually be factored into some common function that would be more flexible about grabbing trace2 data.
Re: [PATCH v3 29/34] fsmonitor--daemon: use a cookie file to sync with file system
In reply to Jeff Hostetler via GitGitGadgetQuoted 10 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Teach fsmonitor--daemon client threads to create a cookie file inside the .git directory and then wait until FS events for the cookie are observed by the FS listener thread.
This helps address the racy nature of file system events by blocking the client response until the kernel has drained any event backlog.
Quoted 8 lines
This is especially important on MacOS where kernel events are only issued with a limited frequency. See the
latencyargument ofFSeventStreamCreate(). The kernel only signals everylatencyseconds, but does not guarantee that the kernel queue is completely drained, so we may have to wait more than one interval. If we increase the frequency, the system is more likely to drop events. We avoid these issues by having each client thread create a unique cookie file and then wait until it is seen in the event stream.
Is this a guaranteed property of any API fsmonitor might need to work with (linux, darwin, Windows) that if I perform a bunch of FS operations on my working tree, that if I finish up by touching this cookie file that that'll happen last?
I'd think that wouldn't be the case, i.e. on POSIX filesystems unless you run around fsyncing both files and directories you're not guaranteed that they're on disk, and even then the kernel might decide to sync your cookie earlier, won't it?
E.g. on Linux you can even have cross-FS watches, and mix & match different FS types. I'd expect to get events in whatever implementation-defined order the VFS layer + FS decided to sync them to disk in & get to firing off an event for me.
Or do these APIs all guarantee that a linear view of the world is presented to the API consumer?
Quoted 26 lines
Co-authored-by Kevin Willford <[email protected]>Co-authored-by Johannes Schindelin <[email protected]>Signed-off-by Jeff Hostetler <[email protected]>--- builtin/fsmonitor--daemon.c | 228 +++++++++++++++++++++++++++++++++++- fsmonitor--daemon.h | 5 + 2 files changed, 232 insertions(+), 1 deletion(-)builtin/fsmonitor--daemon.c +10
-0@@ -94,6 +94,149 @@ static int do_as_client__status(void) } } +enum fsmonitor_cookie_item_result { +FCIR_ERROR =-1 ,/* could not create cookie file ? */ +FCIR_INIT =0 , +FCIR_SEEN , +FCIR_ABORT , +}; + +struct fsmonitor_cookie_item { +struct hashmap_entry entry; +const char *name;
builtin/fsmonitor--daemon.c static int do_as_client__status(void) · line 116
Quoted 10 lines
builtin/fsmonitor--daemon.c +10
-0+enum fsmonitor_cookie_item_result result; +}; + +static int cookies_cmp (const void *data,const struct hashmap_entry *he1, +const struct hashmap_entry *he2,const void *keydata) +{ +const struct fsmonitor_cookie_item *a = + container_of(he1, const struct fsmonitor_cookie_item, entry); +const struct fsmonitor_cookie_item *b = + container_of(he2, const struct fsmonitor_cookie_item, entry);
Re earlier comments about verbose names, these are just enums in a builtin/*.c file, so a name like "cookie_item" is fine, and then the whole thing might even fit on one line.. :)
Quoted 28 lines
[...]
builtin/fsmonitor--daemon.c +27
-0+/* +* We will write filesystem syncing cookie files into +* <gitdir>/<fsmonitor-dir>/<cookie-dir>/<pid>-<seq>. +* +* The extra layers of subdirectories here keep us from +* changing the mtime on ".git/" or ".git/foo/" when we create +* or delete cookie files. +* +* There have been problems with some IDEs that do a +* non-recursive watch of the ".git/" directory and run a +* series of commands any time something happens. +* +* For example, if we place our cookie files directly in +* ".git/" or ".git/foo/" then a `git status` (or similar +* command) from the IDE will cause a cookie file to be +* created in one of those dirs. This causes the mtime of +* those dirs to change. This triggers the IDE's watch +* notification. This triggers the IDE to run those commands +* again. And the process repeats and the machine never goes +* idle. +* +* Adding the extra layers of subdirectories prevents the +* mtime of ".git/" and ".git/foo" from changing when a +* cookie file is created. +*/ +strbuf_init (&state.path_cookie_prefix,0 ); +strbuf_addbuf (&state.path_cookie_prefix, &state.path_gitdir_watch);
builtin/fsmonitor--daemon.c static int fsmonitor_run_daemon(void) · line 1259
Quoted 10 lines
builtin/fsmonitor--daemon.c +10
-0+ +strbuf_addch (&state.path_cookie_prefix,'/' ); +strbuf_addstr (&state.path_cookie_prefix,FSMONITOR_DIR ); +mkdir (state.path_cookie_prefix.buf,0777 ); + +strbuf_addch (&state.path_cookie_prefix,'/' ); +strbuf_addstr (&state.path_cookie_prefix,FSMONITOR_COOKIE_DIR ); +mkdir (state.path_cookie_prefix.buf,0777 ); + +strbuf_addch (&state.path_cookie_prefix,'/' );
So, on some stupid IDEs (would be nice to have specifics in the commit message, which ones/versions?) this avoids causing infinite activity, but on slightly more industrious stupid IDEs that would do their own recursive watch we'll have the same problem?
Perhaps we should just consider creating it at the top-level and those IDEs will eventually sort out their bugs, sooner than later if this feature ships...
builtin/fsmonitor--daemon.c done: · line 1285
Quoted 5 lines
builtin/fsmonitor--daemon.c +5
-0+strbuf_release (&state.path_cookie_prefix); + +/* +* NEEDSWORK: Consider "rm -rf <gitdir>/<fsmonitor-dir>" +*/
That would also make this trivial, presumably it's a "needswork" since you have this recursive structure, but if it's at the top-level we already did the unlink() above, so NEEDSWORK solved then?
Re: [PATCH v3 34/34] t7527: test FS event reporing on MacOS WRT case and Unicode
In reply to Jeff Hostetler via GitGitGadgett/t7527-builtin-fsmonitor.sh test_expect_success MINGW,SHORTNAMES 'implicit daemon stop (rename GIT~2)' ' · line 202
Quoted 2 lines · Jeff Hostetler via GitGitGadget
t/t7527-builtin-fsmonitor.sh +2
-0+ [ -d test_apfs/.git ] && + [ -d test_apfs/.GIT ] &&
Better as "test_path_is_dir".
Quoted 72 lines · Ævar Arnfjörð Bjarmason
Quoted 35 lines · Jeff Hostetler
Quoted 10 lines · Ævar Arnfjörð Bjarmason
Quoted 8 lines · Jeff Hostetler via GitGitGadget
Here is V3 of my patch series to add a builtin FSMonitor daemon to Git. I rebased this series onto v2.32.0.
V3 addresses most of the previous review comments and things we've learned from our experimental testing of V2. (A version of V2 was shipped as an experimental feature in the v2.32.0-based releases of Git for Windows and VFS for Git.)
Quoted 8 lines
Quoted 2 lines
There are still a few items that I need to address, but that list is getting very short.
...
Quoted 2 lines
fsmonitor-fs-listen-win32: stub in backend for Windows
fsmonitor-fs-listen-macos: stub in backend for MacOSI left some light comments on the
repo-settings.cpart of this to follow
up from a previous round.Thanks.
Quoted 6 lines
Any other testing of it is stalled by there being no linux backend for
it as part of this series. I see from spelunking repos that Johannes had a WIPcompat/fsmonitor/linux.cwhich looks like it could/should mostly work, but the API names all changed since then, and after a short try I gave up on trying to rebase it.The early Linux version was dropped because inotify does not give recursive coverage -- only the requested directory. Using inotify requires adding a watch to each subdirectory (recursively) in the worktree. There's a system limit on the maximum number of watched directories (defaults to 8K IIRC) and that limit is system-wide.
Quoted 8 lines
Since the whole point was to support large very large repos, using inotify was a non-starter, so I removed the Linux version from our patch series. For example, the first repo I tried it on (outside of the test suite) had 25K subdirectories.
I'm told there is a new fanotify API in recent Linux kernels that is a better fit for what we need, but we haven't had time to investigate it yet.
That default limit is a bit annoying, but I don't see how it's a blocker in any way.
You simply adjust the limit. E.g. I deployed and tested the hook version of inotify (using watchman) in a sizable development environment, and written my own code using the API. This was all before fanotify(7) existed. IIRC I set most of the limits to 2^24 or 2^20. I've used it with really large Git repos, including with David Turner's 2015-04-03-1M-git for testing (
git ls-files | wc -lon that is around a quarter-million).If you have a humongous repository and don't have root on your own machine you're out of luck. But I think most people who'd use this are either using their own laptop, or e.g. in a corporate setting where administrators would tweak the sysctl limits given the performance advantages (as I did).
Once you adjust the limits Linux deals with large trees just fine, it just has overly conservative limits for most things in sysctl. The API is a bit annoying, your event loop needs to run around and add watches.
AFAICT you need Linux 5.1 for fanotify(7) to be useful, e.g. Debian stable, RHEL etc. aren't using anything that new. So having an inotify backend as well as possibly a fanotify one would be very useful.
Quoted 7 lines
And linux.git's docs suggest that the default limit was bumped from 8192 to 1M in v5.10, a really recent kernel, so if you've got that you've also got fanotify.
In any case, even if Linux's inotify limit was something hardcoded and impossible to change you could still use such an API to test & debug the basics of this feature on that platform.
Good points. If the inotify limits can be increased into the millions then we can revisit supporting it. I do worry about possible race conditions as we have to scan the worktree and add/delete a watch for each directory, but we don't need to worry about that right now.
I do want to have Linux support eventually, but I was saving it for a later effort (and/or looking for volunteers). My IPC-based builtin daemon complements the existing hook-based fsmonitor support that Ben Peart and Kevin Willford added a few years ago. That model (and PERL hook script and Watchman integration) work fine for Linux, so the advantages of a builtin monitor aren't as compelling.
For example, on Linux, hook process creation is fast, PERL is fast, and it is easy to just apt-get a tool like Watchman. But on Windows, process creation is slow, NTFS is slow, PERL is available as part of the Git-for-Windows distribution, and installing third-party tools like Watchman onto a collection of enterprise users' machines is a chore, so it made sense of us to pick platforms where the existing hook model has issues and add other platforms later.
Besides, this patch series is already at 34 commits. And some of them are quite large. Adding another platform would just make it even larger.
Right now I'm more interested in the larger question of whether we WANT to have a builtin fsmonitor and do we like the overall design of what I have here? Picking up a new platform, whether it is Linux, or AIX, or Solaris, or Nonstop, or whatever, should nicely fit into one platform-specific file in compat/fsmonitor and not take that long.
Quoted 23 lines
Quoted 11 lines
Quoted 7 lines
I'd really prefer for git not to have features that place free platforms
at a disadvantage against proprietary platforms if it can be avoided, and in this case the lack of a Linux backend also means much less widespread testing of the feature among the development community / CI.This feature is always going to have platform-specific components, so the lack of one platform or another should not stop us from discussing it for the platforms that can be supported.
(I think per the above that's s/can be/are/)
Quoted 4 lines
And given the size and complexity of the platform-specific code, we should not assume that "just test it on Linux" is sufficient. Yes, there are some common/shared routines/data structures in the daemon, but hard/tricky parts are in the platform layer.
I think we're talking past each other a bit here. I'm not saying that you can get full or meaningful testing for it on Windows if you test it on Linux, or the other way around.
Quoted 8 lines
Of course there's platform-specific stuff, although there's also a lot of non-platform-specific stuff, so even a very basic implementation of inotify would make reviwing this easier / give access to more reviewers.
I'm saying that I prefer that Git as a free software project not be in the situation of saying the best use-case for a given size/shape of repo is to use Git in combination with proprietary operating systems over freely licensed ones.
I wouldn't worry about that. Even without Watchman integration, Linux runs things so much faster than anything else it's not funny.
If anything, we need things like fsmonitor on Windows to help keep up with Linux.
Quoted 6 lines
IOW what the FSF has a policy for GNU projects. Now, I think the FSF probably goes too far in that (famously removing rather obscure font rendering features from Emacs on OSX), but "manage lots of data" is a core feature of git.
Jeff
Hi Ævar,
Quoted 9 lines · Ævar Arnfjörð Bjarmason
Quoted 7 lines · Jeff Hostetler
Quoted 5 lines · Ævar Arnfjörð Bjarmason
Any other testing of it is stalled by there being no linux backend for it as part of this series. I see from spelunking repos that Johannes had a WIP
compat/fsmonitor/linux.cwhich looks like it could/should mostly work, but the API names all changed since then, and after a short try I gave up on trying to rebase it.
I am a bit surprised that you gave up so easily, it cannot be that hard if you use git rebase -i smartly.
But I think there is an even bigger obstacle lurking than just the challenge of rebasing that experimental backend.
Quoted 11 lines
Quoted 11 lines
The early Linux version was dropped because inotify does not give recursive coverage -- only the requested directory. Using inotify requires adding a watch to each subdirectory (recursively) in the worktree. There's a system limit on the maximum number of watched directories (defaults to 8K IIRC) and that limit is system-wide.
Since the whole point was to support large very large repos, using inotify was a non-starter, so I removed the Linux version from our patch series. For example, the first repo I tried it on (outside of the test suite) had 25K subdirectories.
Quoted 6 lines
Quoted 3 lines
I'm told there is a new fanotify API in recent Linux kernels that is a better fit for what we need, but we haven't had time to investigate it yet.
That default limit is a bit annoying, but I don't see how it's a blocker in any way.
Let me help you to see it.
So let's assume that you start FSMonitor-enabled Git, with the default values. What is going to happen if you have any decently-sized worktree? You run out of handles. What then? Throw your hands in the air? Stop working? Report incorrect results?
Those are real design challenges, and together with the race problems Jeff mentioned, they pose a much bigger obstacle than the rebasing you mentioned above.
Quoted 8 lines
You simply adjust the limit. E.g. I deployed and tested the hook version of inotify (using watchman) in a sizable development environment, and written my own code using the API. This was all before fanotify(7) existed. IIRC I set most of the limits to 2^24 or 2^20. I've used it with really large Git repos, including with David Turner's 2015-04-03-1M-git for testing (
git ls-files | wc -lon that is around a quarter-million).
Quoted 5 lines
If you have a humongous repository and don't have root on your own machine you're out of luck. But I think most people who'd use this are either using their own laptop, or e.g. in a corporate setting where administrators would tweak the sysctl limits given the performance advantages (as I did).
This conjecture that most people who'd use this are using their own laptop or have a corporate setting where administrators would tweak the sysctl limits according to engineers' wishes strikes me as totally made up from thin air, nothing else.
In other words, I find it an incredibly unconvincing argument.
I prefer not to address the rest of your mail, as I found it not only a lengthy tangent (basically trying to talk Jeff into adding Linux support in what could have been a much shorter mail), but actively distracting from the already long patch series Jeff presented. It is so long, in fact, that we had to put in an exemption in GitGitGadget because it is already longer than a usually-unreasonable 30 patches. Also, at this point, insisting on Linux support (in so many words) is unhelpful.
Let me summarize why I think this is unhelpful: In Git, it is our tradition to develop incrementally, for better or worse. Jeff's effort brought us to a point where we already have Windows and macOS support, i.e. support for the most prevalent development platforms (see e.g.
https://insights.stackoverflow.com/survey/2020#technology-developers-primary-operating-systems).
We already established multiple obstacles for Linux support, therefore demanding Linux support to be included Right Now would increase the patch series even further, making it even less reviewable, being even less incremental, hold up the current known-to-work-well state, force Jeff to work on something he probably cannot work on right now, and therefore delaying the entire effort even further.
Ciao,
Johannes
Re: [PATCH v3 14/34] fsmonitor--daemon: implement 'start' command
In reply to Ævar Arnfjörð BjarmasonHi Ævar,
Quoted 75 lines · Ævar Arnfjörð Bjarmason
Quoted 73 lines · Jeff Hostetler via GitGitGadget
builtin/fsmonitor--daemon.c +73
-0+#ifdef GIT_WINDOWS_NATIVE +/* +* Create a background process to run the daemon. It should be completely +* disassociated from the terminal. +* +* Conceptually like `daemonize()` but different because Windows does not +* have `fork(2)`. Spawn a normal Windows child process but without the +* limitations of `start_command()` and `finish_command()`. +* +* The child process execs the "git fsmonitor--daemon run" command. +* +* The current process returns so that the caller can wait for the child +* to startup before exiting. +*/ +static int spawn_background_fsmonitor_daemon (pid_t *pid) +{ +char git_exe[MAX_PATH ]; +struct strvec args =STRVEC_INIT ; +int in, out; + +GetModuleFileNameA (NULL , git_exe,MAX_PATH ); + + in =open ("/dev/null" ,O_RDONLY ); + out =open ("/dev/null" ,O_WRONLY ); + +strvec_push (&args, git_exe); +strvec_push (&args,"fsmonitor--daemon" ); +strvec_push (&args,"run" ); +strvec_pushf (&args,"--ipc-threads=%d" , fsmonitor__ipc_threads); + + *pid =mingw_spawnvpe (args.v[0 ], args.v,NULL ,NULL , in, out, out); +close (in); +close (out); + +strvec_clear (&args); + +if (*pid <0 ) +return error (_ ("could not spawn fsmonitor--daemon in the background" )); + +return 0 ; +} +#else +/* +* Create a background process to run the daemon. It should be completely +* disassociated from the terminal. +* +* This is adapted from `daemonize()`. Use `fork()` to directly +* create and run the daemon in the child process. +* +* The fork-child can just call the run code; it does not need to exec +* it. +* +* The fork-parent returns the child PID so that we can wait for the +* child to startup before exiting. +*/ +static int spawn_background_fsmonitor_daemon (pid_t *pid) +{ + *pid =fork (); + +switch (*pid) { +case 0 : +if (setsid () ==-1 ) +error_errno (_ ("setsid failed" )); +close (0 ); +close (1 ); +close (2 ); +sanitize_stdfds (); + +return !!fsmonitor_run_daemon (); + +case -1 : +return error_errno (_ ("could not spawn fsmonitor--daemon in the background" )); +
Quoted 8 lines
builtin/fsmonitor--daemon.c static int try_to_run_foreground_daemon(void) · line 349
Quoted 5 lines
builtin/fsmonitor--daemon.c +5
-0+default : + return 0; + } +} +#endif The
spawn_background_fsmonitor_daemon()function here is almost the same asdaemonize().
Yes, the code comment above that function even says that it was adapted from daemonize().
And above that, of course, is a _completely different_ implementation that works on Windows (you will notice that this is in stark contrast of Windows, where the daemonize() function will simply fail with ENOSYS).
Quoted 2 lines
I wonder if this & the Windows-specific one you have here can't be refactored into an API from what's now in
setup.c.
Given that there is no fork() on Windows (which has been the subject of many a message to this mailing list), I think the answer to this question of yours is a resounding "No".
Quoted 3 lines
Then we could make
builtin/gc.canddaemon.cuse that, so Windows could have background GC, and we'd have a more battle-tested central codepath for this tricky bit.
Please. Not _more_ sidetracks.
The issue of getting git gc --auto to daemonize on Windows is a rather complicated one. I won't bore this list with the details, but link to
https://github.com/git-for-windows/git/issues/2221#issuecomment-542589590
(a ~950 word analysis of the problem).
Quoted 4 lines
It seems to me like the only limitations on it are to have this return slightly more general things (e.g. not set its own errors, return structured data), and maybe some callback for what to do in the child/parent.
And one version doesn't die(). Nor does it call exit(0) in the parent process. But it calls fsmonitor_listen() in the child process. And if you wanted to refactor daemonize() to do all that, it would have to be renamed (because it does no longer _necessarily_ daemonize), and it would have to have a gentle flag, and it would somehow have to indicate in its return value whether 0 means that the parent process returned successfully or the client process. And soon we'll end up with a function that is both longer and more unreadable than what we have right now.
Quoted 74 lines
Quoted 60 lines
builtin/fsmonitor--daemon.c +59
-0+/* +* This is adapted from `wait_or_whine()`. Watch the child process and +* let it get started and begin listening for requests on the socket +* before reporting our success. +*/ +static int wait_for_background_startup (pid_t pid_child) +{ +int status; +pid_t pid_seen; +enum ipc_active_state s; +time_t time_limit, now; + +time (&time_limit); + time_limit += fsmonitor__start_timeout_sec; + +for (;;) { + pid_seen =waitpid (pid_child, &status,WNOHANG ); + +if (pid_seen ==-1 ) +return error_errno (_ ("waitpid failed" )); +else if (pid_seen ==0 ) { +/* +* The child is still running (this should be +* the normal case). Try to connect to it on +* the socket and see if it is ready for +* business. +* +* If there is another daemon already running, +* our child will fail to start (possibly +* after a timeout on the lock), but we don't +* care (who responds) if the socket is live. +*/ + s =fsmonitor_ipc__get_state (); +if (s ==IPC_STATE__LISTENING ) +return 0 ; + +time (&now); +if (now > time_limit) +return error (_ ("fsmonitor--daemon not online yet" )); + }else if (pid_seen == pid_child) { +/* +* The new child daemon process shutdown while +* it was starting up, so it is not listening +* on the socket. +* +* Try to ping the socket in the odd chance +* that another daemon started (or was already +* running) while our child was starting. +* +* Again, we don't care who services the socket. +*/ + s =fsmonitor_ipc__get_state (); +if (s ==IPC_STATE__LISTENING ) +return 0 ; + +/* +* We don't care about the WEXITSTATUS() nor +* any of the WIF*(status) values because +* `cmd_fsmonitor__daemon()` does the `!!result` builtin/fsmonitor--daemon.c static int try_to_run_foreground_daemon(void) · line 419
Quoted 10 lines
builtin/fsmonitor--daemon.c +10
-0+* trick on all function return values. +* +* So it is sufficient to just report the +* early shutdown as an error. +*/ +return error (_ ("fsmonitor--daemon failed to start" )); + }else +return error (_ ("waitpid is confused" )); + } +}Ditto this. could we extend the
wait_or_whine()function (or some extended version thereof) to do what you need with callbacks?
Quoted 3 lines
It seems the main difference is just being able to pass down a flag for
waitpid(), and the loop needing to check EINTR or not depending on whether WNOHANG is passed.
Given that over half of wait_or_whine() is concerned with signals, which the wait_for_background_startup() function is not at all concerned with, I see another main difference.
Quoted 4 lines
For e.g. the "We don't care about the
WEXITSTATUS()" you'd get that behavior with an adjustedwait_or_whine(). Wouldn't it be better to report what exit status it exits with e.g. if the top-level process is signalled? We do so in trace2 for other things we spawn...
The wait_or_whine() function also adjusts atexit() behavior, which we would not want here.
Therefore, just like the suggestion about daemonize() above, it appears to me as if the suggested refactoring would make the code dramatically more complex and less readable.
In other words, it would be a refactoring for refactoring's sake. Definitely not something I would suggest.
Ciao,
Johannes
Re: [PATCH v3 02/34] fsmonitor--daemon: man page
In reply to Ævar Arnfjörð BjarmasonHi Ævar,
Quoted 97 lines · Ævar Arnfjörð Bjarmason
Quoted 81 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Create a manual page describing the
git fsmonitor--daemonfeature.Signed-off-by Jeff Hostetler <[email protected]>--- Documentation/git-fsmonitor--daemon.txt | 75 +++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 Documentation/git-fsmonitor--daemon.txtDocumentation/git-fsmonitor--daemon.txt new file +65
-0@@ -0,0 +1,75 @@ +git-fsmonitor--daemon(1) +======================== + +NAME +---- +git-fsmonitor--daemon - A Built-in File System Monitor + +SYNOPSIS +-------- +[verse] +'git fsmonitor--daemon' start +'git fsmonitor--daemon' run +'git fsmonitor--daemon' stop +'git fsmonitor--daemon' status + +DESCRIPTION +----------- + +A daemon to watch the working directory for file and directory +changes using platform-specific file system notification facilities. + +This daemon communicates directly with commands like `git status` +using the link:technical/api-simple-ipc.html[simple IPC] interface +instead of the slower linkgit:githooks[5] interface. + +This daemon is built into Git so that no third-party tools are +required. + +OPTIONS +------- + +start:: + Starts a daemon in the background. + +run:: + Runs a daemon in the foreground. + +stop:: + Stops the daemon running in the current working + directory, if present. + +status:: + Exits with zero status if a daemon is watching the + current working directory. + +REMARKS +------- + +This daemon is a long running process used to watch a single working +directory and maintain a list of the recently changed files and +directories. Performance of commands such as `git status` can be +increased if they just ask for a summary of changes to the working +directory and can avoid scanning the disk. + +When `core.useBuiltinFSMonitor` is set to `true` (see +linkgit:git-config[1]) commands, such as `git status`, will ask the +daemon for changes and automatically start it (if necessary). + +For more information see the "File System Monitor" section in +linkgit:git-update-index[1]. + +CAVEATS +------- + +The fsmonitor daemon does not currently know about submodules and doesDocumentation/git-fsmonitor--daemon.txt line 75
Quoted 10 lines
Documentation/git-fsmonitor--daemon.txt new file +10
-0+not know to filter out file system events that happen within a +submodule. If fsmonitor daemon is watching a super repo and a file is +modified within the working directory of a submodule, it will report +the change (as happening against the super repo). However, the client +will properly ignore these extra events, so performance may be affected +but it will not cause an incorrect result. + +GIT +--- +Part of the linkgit:git[1] suiteLater in the series we incrementally add features to the daemon, so this is describing a state that doesn't exist yet at this point.
Quoted 3 lines
I think it would be better to start with a stup here and add documentation as we add features, e.g. the patch tha adds "start" should add that to the synopsis + options etc.
Incidentally, I had structured the patch series that way until Jeff took over from me last year. And it was definitely not more reviewable because it was not clear from the get-go what I intended this command to do.
Therefore, I object to your suggestion.
Ciao,
Johannes
P.S.: I will not address your other reviews in this patch series, mostly because I am technically off from work this week.
Quoted 26 lines · Johannes Schindelin
Quoted 3 lines · Ævar Arnfjörð Bjarmason
Quoted 1 line · Jeff Hostetler
On 7/1/21 1:40 PM, Ævar Arnfjörð Bjarmason wrote:
[...]
Quoted 11 lines
Quoted 11 lines
The early Linux version was dropped because inotify does not give recursive coverage -- only the requested directory. Using inotify requires adding a watch to each subdirectory (recursively) in the worktree. There's a system limit on the maximum number of watched directories (defaults to 8K IIRC) and that limit is system-wide.
Since the whole point was to support large very large repos, using inotify was a non-starter, so I removed the Linux version from our patch series. For example, the first repo I tried it on (outside of the test suite) had 25K subdirectories.
Quoted 6 lines
Quoted 3 lines
I'm told there is a new fanotify API in recent Linux kernels that is a better fit for what we need, but we haven't had time to investigate it yet.
That default limit is a bit annoying, but I don't see how it's a blocker in any way.
Let me help you to see it.
Quoted 8 lines
So let's assume that you start FSMonitor-enabled Git, with the default values. What is going to happen if you have any decently-sized worktree? You run out of handles. What then? Throw your hands in the air? Stop working? Report incorrect results?
Those are real design challenges, and together with the race problems Jeff mentioned, they pose a much bigger obstacle than the rebasing you mentioned above.
You report an error and tell the user to raise the limit, and cover this in your install docs. It's what watchman does:
https://github.com/facebook/watchman/blob/master/watchman/error_category.cpp#L28-L45
https://facebook.github.io/watchman/docs/install.html#system-specific-preparation
Quoted 14 lines
Quoted 8 lines
You simply adjust the limit. E.g. I deployed and tested the hook version of inotify (using watchman) in a sizable development environment, and written my own code using the API. This was all before fanotify(7) existed. IIRC I set most of the limits to 2^24 or 2^20. I've used it with really large Git repos, including with David Turner's 2015-04-03-1M-git for testing (
git ls-files | wc -lon that is around a quarter-million).Quoted 5 lines
If you have a humongous repository and don't have root on your own machine you're out of luck. But I think most people who'd use this are either using their own laptop, or e.g. in a corporate setting where administrators would tweak the sysctl limits given the performance advantages (as I did).
Quoted 6 lines
This conjecture that most people who'd use this are using their own laptop or have a corporate setting where administrators would tweak the sysctl limits according to engineers' wishes strikes me as totally made up from thin air, nothing else.
In other words, I find it an incredibly unconvincing argument.
It's from a sample size of one experience of deploying this in a BigCorp setting.
But sure, perhaps things are done differently where you work/have worked. My experience is that even if you're dealing with some BOFHs and e.g. are using shared racked development servers it's generally not an insurmountable task to get some useful software installed, or some system configuration tweaked.
In this case we're talking about ~40MB of kernel memory for 1 million dirs IIRC, that coupled with the target audience that benefits most from this probably being deployments that are *painfully* aware of their "git status" slowness...
Quoted 7 lines
I prefer not to address the rest of your mail, as I found it not only a lengthy tangent (basically trying to talk Jeff into adding Linux support in what could have been a much shorter mail), but actively distracting from the already long patch series Jeff presented. It is so long, in fact, that we had to put in an exemption in GitGitGadget because it is already longer than a usually-unreasonable 30 patches. Also, at this point, insisting on Linux support (in so many words) is unhelpful.
This part of the tread started because Jeff H. claimed upthread:
[...]inotify was a non-starter, so I removed the Linux version from our patch series
But after I noted that it works just fine, you just need to change some sysctl limits.
It seems at this point we're debating whether some installations of Linux have BOFH-y enough administrators that they won't tweak sysctl limits for you. OK, but given that I've run this thing in a production setting it's clearly not a "non-starter". I think it could be useful for a lot of users.
I'll reply with more (and hopefully helpful) specifics to Jeff's mail.
Quoted 11 lines
Let me summarize why I think this is unhelpful: In Git, it is our tradition to develop incrementally, for better or worse. Jeff's effort brought us to a point where we already have Windows and macOS support, i.e. support for the most prevalent development platforms (see e.g.
https://insights.stackoverflow.com/survey/2020#technology-developers-primary-operating-systems).
We already established multiple obstacles for Linux support, therefore demanding Linux support to be included Right Now would increase the patch series even further, making it even less reviewable, being even less incremental, hold up the current known-to-work-well state, force Jeff to work on something he probably cannot work on right now, and therefore delaying the entire effort even further.
I think we just disagree. I wouldn't call my opinion "unhelpful" any more than yours.
I don't think Git's ever had anything like a major feature (built in, config settings, etc. etc.) contributed by a propriterary OS vendor that works on that vendor's OS, as well as another vendor's propriterary OS, but not comparable free systems
Is that less incremental and perhaps less practical? Sure. It's not an entirely practical viewpoint. I work on free software partly for idealistic reasons. I'd prefer if the project I'm working on doesn't give users a carrot to pick proprietary systems over free ones.
But ultimately it's not my call, but Junio's.
Quoted 78 lines · Jeff Hostetler
Quoted 68 lines · Ævar Arnfjörð Bjarmason
Quoted 35 lines · Jeff Hostetler
Quoted 10 lines · Ævar Arnfjörð Bjarmason
Quoted 8 lines · Jeff Hostetler via GitGitGadget
Here is V3 of my patch series to add a builtin FSMonitor daemon to Git. I rebased this series onto v2.32.0.
V3 addresses most of the previous review comments and things we've learned from our experimental testing of V2. (A version of V2 was shipped as an experimental feature in the v2.32.0-based releases of Git for Windows and VFS for Git.)
Quoted 8 lines
Quoted 2 lines
There are still a few items that I need to address, but that list is getting very short.
...
Quoted 2 lines
fsmonitor-fs-listen-win32: stub in backend for Windows
fsmonitor-fs-listen-macos: stub in backend for MacOSI left some light comments on the
repo-settings.cpart of this to follow
up from a previous round.Thanks.
Quoted 6 lines
Any other testing of it is stalled by there being no linux backend for
it as part of this series. I see from spelunking repos that Johannes had a WIPcompat/fsmonitor/linux.cwhich looks like it could/should mostly work, but the API names all changed since then, and after a short try I gave up on trying to rebase it.The early Linux version was dropped because inotify does not give recursive coverage -- only the requested directory. Using inotify requires adding a watch to each subdirectory (recursively) in the worktree. There's a system limit on the maximum number of watched directories (defaults to 8K IIRC) and that limit is system-wide.
Quoted 8 lines
Since the whole point was to support large very large repos, using inotify was a non-starter, so I removed the Linux version from our patch series. For example, the first repo I tried it on (outside of the test suite) had 25K subdirectories.
I'm told there is a new fanotify API in recent Linux kernels that is a better fit for what we need, but we haven't had time to investigate it yet.
That default limit is a bit annoying, but I don't see how it's a blocker
in any way.
You simply adjust the limit. E.g. I deployed and tested the hook version
of inotify (using watchman) in a sizable development environment, and written my own code using the API. This was all before fanotify(7) existed. IIRC I set most of the limits to 2^24 or 2^20. I've used it with really large Git repos, including with David Turner's 2015-04-03-1M-git for testing (git ls-files | wc -lon that is around a quarter-million).
If you have a humongous repository and don't have root on your own machine you're out of luck. But I think most people who'd use this are either using their own laptop, or e.g. in a corporate setting where administrators would tweak the sysctl limits given the performance advantages (as I did).
Once you adjust the limits Linux deals with large trees just fine, it
just has overly conservative limits for most things in sysctl. The API is a bit annoying, your event loop needs to run around and add watches. AFAICT you need Linux 5.1 for fanotify(7) to be useful, e.g. Debian stable, RHEL etc. aren't using anything that new. So having an inotify backend as well as possibly a fanotify one would be very useful.Quoted 8 lines
And linux.git's docs suggest that the default limit was bumped from 8192
to 1M in v5.10, a really recent kernel, so if you've got that you've also got fanotify.
In any case, even if Linux's inotify limit was something hardcoded and
impossible to change you could still use such an API to test & debug the basics of this feature on that platform.
Quoted 2 lines
Good points. If the inotify limits can be increased into the millions then we can revisit supporting it.
I also replied in the side-thread on this topic:
https://lore.kernel.org/git/[email protected]/
Quoted 3 lines
[...] I do worry about possible race
conditions as we have to scan the worktree and add/delete a watch for each directory, but we don't need to worry about that right now.
In the watchman code there's a scheduleRecrawl() in such cases, but it's not just something that happens on Linux.
On Windows it's if ReadDirectoryChangesW() returns ERROR_NOTIFY_ENUM_DIR. This builtin version doesn't check that at all. Here's someone who seems to work at MSFT talking about it:
https://devblogs.microsoft.com/oldnewthing/20110812-00/?p=9913
So isn't this an issue on Windows as well?
Quoted 2 lines
I do want to have Linux support eventually, but I was saving it for a later effort (and/or looking for volunteers).
OK, upthread you said it was a "non-starter" because of the adjustable limits we've been discussing...
Quoted 7 lines
My IPC-based builtin daemon complements the existing hook-based fsmonitor support that Ben Peart and Kevin Willford added a few years ago. That model (and PERL hook script and Watchman integration) work fine for Linux, so the advantages of a builtin monitor aren't as compelling.
For example, on Linux, hook process creation is fast, PERL is fast,[...]
Did you or Ben ever try to reproduce what I noted at:
https://lore.kernel.org/git/CACBZZX5e58bWuf3NdDYTxu2KyZj29hHONzN=rp-7vXd8nURyWQ@mail.gmail.com/
And more recently with reference to that at:
https://lore.kernel.org/git/[email protected]/
I.e. when I tested it back then the issue wasn't Perl's performance, it was something between watchman and the user, happening in git. I.e. watchman would return in the single-digit milliseconds, git would take hundreds or thousands of ms.
Maybe I missed some intervening analysis, but this didn't have to do with process overhead back then.
Also, watchman has a JSON network interface. So for a "native" solution I also wonder if you or Ben tried a solution that involved just talking to that over the local network. The hook/perl script was just a trivial aid to make doing that easier. Wouldn't that be just as fast (or near enough) on Windows?
Ben mentioned in the linked thread that there was some talks between Microsoft (or well, the "Git team") and the watchman people. So again, I missed some in-between discussions. I'm just wondering about the design choices...
Quoted 6 lines
and it is easy to just apt-get a tool like Watchman. But on Windows, process creation is slow, NTFS is slow, PERL is available as part of the Git-for-Windows distribution, and installing third-party tools like Watchman onto a collection of enterprise users' machines is a chore, so it made sense of us to pick platforms where the existing hook model has issues and add other platforms later.
If we're trying to do an end-run around package systems being a pain would including the relevant part of watchman in contrib + talking to it over its network interface be a replacement for some/most/all of this series?
Quoted 10 lines
Besides, this patch series is already at 34 commits. And some of them are quite large. Adding another platform would just make it even larger.
Right now I'm more interested in the larger question of whether we WANT to have a builtin fsmonitor and do we like the overall design of what I have here? Picking up a new platform, whether it is Linux, or AIX, or Solaris, or Nonstop, or whatever, should nicely fit into one platform-specific file in compat/fsmonitor and not take that long.
Sure, FWIW I'm not opposed to this being a built-in per-se, or us re-implementing parts of watchman here, and not being able to test this series meaningfully a lot of these questions are probably easy to answer on a supported OS...
Quoted 30 lines
Quoted 16 lines
Quoted 10 lines
Quoted 6 lines
I'd really prefer for git not to have features that place free platforms
at a disadvantage against proprietary platforms if it can be avoided, and in this case the lack of a Linux backend also means much less widespread testing of the feature among the development community / CI.This feature is always going to have platform-specific components, so the lack of one platform or another should not stop us from discussing it for the platforms that can be supported.
(I think per the above that's s/can be/are/)
Quoted 4 lines
And given the size and complexity of the platform-specific code, we should not assume that "just test it on Linux" is sufficient. Yes, there are some common/shared routines/data structures in the daemon, but hard/tricky parts are in the platform layer.
Quoted 13 lines
I think we're talking past each other a bit here. I'm not saying that
you can get full or meaningful testing for it on Windows if you test it on Linux, or the other way around.
Of course there's platform-specific stuff, although there's also a lot
of non-platform-specific stuff, so even a very basic implementation of inotify would make reviwing this easier / give access to more reviewers. I'm saying that I prefer that Git as a free software project not be in
the situation of saying the best use-case for a given size/shape of repo is to use Git in combination with proprietary operating systems over freely licensed ones.
Quoted 5 lines
I wouldn't worry about that. Even without Watchman integration, Linux runs things so much faster than anything else it's not funny.
If anything, we need things like fsmonitor on Windows to help keep up with Linux.
Even on Linux if you do a "git status" on a humongous repository it can take 1-2 seconds (see old but still valid linked numbers above), whereas if you've been logging fs events and ask a daemon "what changed since time xyz" it can take <5ms.
But that's just been on repositories I've tested. I'd assumed that this part of GVFS wasn't something that could have been fairly easily replaced by having the relevant developers run a Linux distro in vmware or whatever, adn that's been consistent with my own testing on still-big-but-smaller repos than that.
Hi Ævar,
Quoted 2 lines · Ævar Arnfjörð Bjarmason
I think we just disagree. I wouldn't call my opinion "unhelpful" any more than yours.
You still misunderstand. This is not about any "opinion" of yours, it is about your delay tactics to make it deliberately difficult to finish this patch series, by raising the bar beyond what is reasonable for a single patch series.
And you keep doing it. I would appreciate if you just stopped with all those tangents and long and many replies that do not seem designed to help the patch series stabilize, but do the opposite.
Ciao,
Johannes
Re: [PATCH v3 19/34] fsmonitor-fs-listen-win32: implement FSMonitor backend on Windows
In reply to Jeff Hostetler via GitGitGadgetHi Jeff,
On Thu, 1 Jul 2021, Jeff Hostetler via GitGitGadget wrote:
Jeff Hostetler <[email protected]>
the win32 backend to register a watch on the working tree irectory (recursively). Also watch the <gitdir> if it is side the working tree. And to collect path change notifications atches and publish.
-off-by: Jeff Hostetler <[email protected]>
t/fsmonitor/fsmonitor-fs-listen-win32.c | 530 +++++++++++++++++++ e changed, 530 insertions(+)
Quoted 10 lines
compat/fsmonitor/fsmonitor-fs-listen-win32.c +5
-0@@ -2,20 +2,550 @@ + [...] + +static struct one_watch *create_watch (struct fsmonitor_daemon_state *state, +const char *path) +{
compat/fsmonitor/fsmonitor-fs-listen-win32.c line 16
Quoted 10 lines
compat/fsmonitor/fsmonitor-fs-listen-win32.c +10
-0+struct one_watch *watch =NULL ; +DWORD desired_access =FILE_LIST_DIRECTORY ; +DWORD share_mode = +FILE_SHARE_WRITE |FILE_SHARE_READ |FILE_SHARE_DELETE ; +HANDLE hDir; + + hDir =CreateFileA (path, + desired_access, share_mode,NULL ,OPEN_EXISTING , +FILE_FLAG_BACKUP_SEMANTICS |FILE_FLAG_OVERLAPPED , +NULL );
The *A() family of Win32 API functions disagree with Git in one very interesting aspect: Git always assumes UTF-8, while e.g. CreateFileA() will use the current Win32 locale to internally transform to wide characters and then call CreateFileW().
This poses no problem when your locale is US American and your paths contain no non-ASCII characters.
In the Git for Windows bug tracker, it was reported that it _does_ cause problems when venturing outside such a cozy scenario (for full details, see https://github.com/git-for-windows/git/issues/3262)
I need this (and merged it before starting the process to release Git for Windows v2.32.0(2)) to fix that (could I ask you to integrate this in case a re-roll will become necessary?):
-- snipsnap --
From: Johannes Schindelin <[email protected]>
Date: Mon, 5 Jul 2021 13:51:05 +0200
Subject: [PATCH] fixup! fsmonitor-fs-listen-win32: implement FSMonitor backend
on Windows
Let's keep avoiding the *A() family of Win32 API functions because they are susceptible to incoherent encoding problems. In Git for Windows, we always assume paths to be UTF-8 encoded. Let's use the dedicated helper to convert such a path to the wide character version, and then use the *W() function instead.
--- compat/fsmonitor/fsmonitor-fs-listen-win32.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
compat/fsmonitor/fsmonitor-fs-listen-win32.c +7 -1
@@ -111,8 +111,14 @@ static struct one_watch *create_watch(struct fsmonitor_daemon_state *state,DWORD share_mode =FILE_SHARE_WRITE |FILE_SHARE_READ |FILE_SHARE_DELETE ;HANDLE hDir; +wchar_t wpath[MAX_PATH ]; - hDir =CreateFileA (path, +if (xutftowcs_path (wpath, path) <0 ) { +error (_ ("could not convert to wide characters: '%s'" ), path); +return NULL ; + } + + hDir =CreateFileW (wpath, desired_access, share_mode,NULL ,OPEN_EXISTING ,FILE_FLAG_BACKUP_SEMANTICS |FILE_FLAG_OVERLAPPED ,NULL );
--
2.32.0.windows.1.15.gf1590a75e2d
Quoted 2 lines · Johannes Schindelin
In Git, it is our tradition to develop incrementally, for better or worse.
We develop incrementally, but the first version of a big feature has to have as many eyes on it as possible in order to track potential issues in the future.
It is very common to look back at an API and say "oh, if only wad thought of that back then". Therefore we should do our best to think about it now.
We want to avoid future aw-shucks.
Quoted 10 lines
Jeff's effort
brought us to a point where we already have Windows and macOS support, i.e. support for the most prevalent development platforms (see e.g.
https://insights.stackoverflow.com/survey/2020#technology-developers-primary-operating-systems).
We already established multiple obstacles for Linux support, therefore demanding Linux support to be included Right Now would increase the patch series even further, making it even less reviewable, being even less incremental, hold up the current known-to-work-well state, force Jeff to work on something he probably cannot work on right now, and therefore delaying the entire effort even further.
This is a red herring.
You don't need to send the Linux support as part of the patch series, you can simply provide a branch for the people that want to give it a try.
Even if it's not ready for wide use, even if it needs a specific version of Linux, even if it's a proof of concept, you can provide it.
The real reason it's not provided is laziness (this is not an attack, laziness is good trait in a programmer, although not always).
Cheers.
Signature · 1 line
Felipe Contreras
Quoted 8 lines · Johannes Schindelin
Quoted 2 lines · Ævar Arnfjörð Bjarmason
I think we just disagree. I wouldn't call my opinion "unhelpful" any more than yours.
You still misunderstand. This is not about any "opinion" of yours, it is about your delay tactics to make it deliberately difficult to finish this patch series,
The whole job of a reviewer is to *delay* the inclusion of the patch series being reviewed until it has passed his/her personal standards.
Ævar is just doing his job (metaphorically).
Quoted 2 lines
by raising the bar beyond what is reasonable for a single patch series.
It is not up to you to determine what is reasonable for a patch series, and given that you have a vested interest you are also not an objective observer.
I have hundreds of patches pending review, and I would love for anyone to try find issues with them, even if I ultimately disagreed with their assessment.
The Git project doesn't owe your patches any preferential treatment. The review process will take as long as the review process takes. You can't put deadlines on open source projects.
Signature · 1 line
Felipe Contreras
Re: [PATCH v3 02/34] fsmonitor--daemon: man page
In reply to Ævar Arnfjörð BjarmasonQuoted 98 lines · Ævar Arnfjörð Bjarmason
Quoted 81 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Create a manual page describing the
git fsmonitor--daemonfeature.Signed-off-by Jeff Hostetler <[email protected]>--- Documentation/git-fsmonitor--daemon.txt | 75 +++++++++++++++++++++++++ 1 file changed, 75 insertions(+)create mode 100644
Documentation/git-fsmonitor--daemon.txtDocumentation/git-fsmonitor--daemon.txt new file +65
-0@@ -0,0 +1,75 @@ +git-fsmonitor--daemon(1) +======================== + +NAME +---- +git-fsmonitor--daemon - A Built-in File System Monitor + +SYNOPSIS +-------- +[verse] +'git fsmonitor--daemon' start +'git fsmonitor--daemon' run +'git fsmonitor--daemon' stop +'git fsmonitor--daemon' status + +DESCRIPTION +----------- + +A daemon to watch the working directory for file and directory +changes using platform-specific file system notification facilities. + +This daemon communicates directly with commands like `git status` +using the link:technical/api-simple-ipc.html[simple IPC] interface +instead of the slower linkgit:githooks[5] interface. + +This daemon is built into Git so that no third-party tools are +required. + +OPTIONS +------- + +start:: + Starts a daemon in the background. + +run:: + Runs a daemon in the foreground. + +stop:: + Stops the daemon running in the current working + directory, if present. + +status:: + Exits with zero status if a daemon is watching the + current working directory. + +REMARKS +------- + +This daemon is a long running process used to watch a single working +directory and maintain a list of the recently changed files and +directories. Performance of commands such as `git status` can be +increased if they just ask for a summary of changes to the working +directory and can avoid scanning the disk. + +When `core.useBuiltinFSMonitor` is set to `true` (see +linkgit:git-config[1]) commands, such as `git status`, will ask the +daemon for changes and automatically start it (if necessary). + +For more information see the "File System Monitor" section in +linkgit:git-update-index[1]. + +CAVEATS +------- + +The fsmonitor daemon does not currently know about submodules and doesDocumentation/git-fsmonitor--daemon.txt line 75
Quoted 10 lines
Documentation/git-fsmonitor--daemon.txt new file +10
-0+not know to filter out file system events that happen within a +submodule. If fsmonitor daemon is watching a super repo and a file is +modified within the working directory of a submodule, it will report +the change (as happening against the super repo). However, the client +will properly ignore these extra events, so performance may be affected +but it will not cause an incorrect result. + +GIT +--- +Part of the linkgit:git[1] suiteLater in the series we incrementally add features to the daemon, so this is describing a state that doesn't exist yet at this point.
Quoted 7 lines
I think it would be better to start with a stup here and add documentation as we add features, e.g. the patch tha adds "start" should add that to the synopsis + options etc.
See the outstanding ab/config-based-hooks-base for a small example of that.
I like to lead the series with the documentation that summarizes the purpose of the entire feature or patch series. This gives the reviewer the context for the complete series that follows. In the past, we've had discussions on the list about how hard it is to review a series when the foo.c comes (alphabetically) before foo.h in the patch and all the documentation is attached to the prototypes in the .h file so the reviewer needs to bounce around in the patch or series to read the intent and then go back to the beginning to read the code. In that spirit, I think that having the complete man page come first provides necessary context and is helpful.
The argument that the man-page should grow as the feature grows presumes that there is a meaningful cut-point mid-series where you would adopt the first portion and delay the second to a later release or something. That division would not be useful/usable.
And it just clutters up later commits in the series with man-page deltas.
So I'd like to keep it as it unless there are further objections.
Jeff
Re: [PATCH v3 14/34] fsmonitor--daemon: implement 'start' command
In reply to Ævar Arnfjörð BjarmasonMy response here is in addition to Dscho's remarks on this topic. He makes excellent points that I'll just #include here. I do want to add my own $0.02 here.
Quoted 86 lines · Ævar Arnfjörð Bjarmason
Quoted 68 lines · Jeff Hostetler via GitGitGadget
builtin/fsmonitor--daemon.c +68
-0+#ifdef GIT_WINDOWS_NATIVE +/* +* Create a background process to run the daemon. It should be completely +* disassociated from the terminal. +* +* Conceptually like `daemonize()` but different because Windows does not +* have `fork(2)`. Spawn a normal Windows child process but without the +* limitations of `start_command()` and `finish_command()`. +* +* The child process execs the "git fsmonitor--daemon run" command. +* +* The current process returns so that the caller can wait for the child +* to startup before exiting. +*/ +static int spawn_background_fsmonitor_daemon (pid_t *pid) +{ +char git_exe[MAX_PATH ]; +struct strvec args =STRVEC_INIT ; +int in, out; + +GetModuleFileNameA (NULL , git_exe,MAX_PATH ); + + in =open ("/dev/null" ,O_RDONLY ); + out =open ("/dev/null" ,O_WRONLY ); + +strvec_push (&args, git_exe); +strvec_push (&args,"fsmonitor--daemon" ); +strvec_push (&args,"run" ); +strvec_pushf (&args,"--ipc-threads=%d" , fsmonitor__ipc_threads); + + *pid =mingw_spawnvpe (args.v[0 ], args.v,NULL ,NULL , in, out, out); +close (in); +close (out); + +strvec_clear (&args); + +if (*pid <0 ) +return error (_ ("could not spawn fsmonitor--daemon in the background" )); + +return 0 ; +} +#else +/* +* Create a background process to run the daemon. It should be completely +* disassociated from the terminal. +* +* This is adapted from `daemonize()`. Use `fork()` to directly +* create and run the daemon in the child process. +* +* The fork-child can just call the run code; it does not need to exec +* it. +* +* The fork-parent returns the child PID so that we can wait for the +* child to startup before exiting. +*/ +static int spawn_background_fsmonitor_daemon (pid_t *pid) +{ + *pid =fork (); + +switch (*pid) { +case 0 : +if (setsid () ==-1 ) +error_errno (_ ("setsid failed" )); +close (0 ); +close (1 ); +close (2 ); +sanitize_stdfds (); +builtin/fsmonitor--daemon.c static int try_to_run_foreground_daemon(void) · line 349
Quoted 10 lines
builtin/fsmonitor--daemon.c +10
-0+return !!fsmonitor_run_daemon (); + +case -1 : +return error_errno (_ ("could not spawn fsmonitor--daemon in the background" )); + +default : + return 0; + } +} +#endif The
spawn_background_fsmonitor_daemon()function here is almost the same asdaemonize(). I wonder if this & the Windows-specific one you have here can't be refactored into an API from what's now insetup.c.
Quoted 4 lines
Then we could make
builtin/gc.canddaemon.cuse that, so Windows could have background GC, and we'd have a more battle-tested central codepath for this tricky bit.
I'd rather not refactor all of this and add unnecessary generality and complexity just to save duplicating some of the code in daemonize().
And I'd rather not destabilize existing commands like gc and daemon by changing the daemonize() layer on them. If those commands need help, let's have a separate conversation _later_ about what help they need and if it makes sense to combine them.
Quoted 4 lines
It seems to me like the only limitations on it are to have this return slightly more general things (e.g. not set its own errors, return structured data), and maybe some callback for what to do in the child/parent.
There are several issues here when trying to start a background process and we're already on the edge of the behavioral differences between Windows and Unix -- let's not make things more confusing with multiple callbacks, returning structures, custom errors, and etc.
Also, since Windows doesn't do fork(), we don't have child/parent branches in the call, so this whole "just pretend it's all Unix" model doesn't work.
Even if we did pretend I'd still need ifdef'd callback routines to either call fsmonitor_run_daemon() or build a command line (or have blocks of functions that "just happen to never be called on one platform or the other").
What I have here is an API that the primary (read: parent) calls and gets back a 0 or -1 (with error message). And that's it. The primary can then wait for the child (whether from fork or CreateProcess) to become responsive or fail to start. And then the primary can exit (with or without error).
So I think we're good. Yes, there is an ifdef here, but I think it is worth it.
Quoted 74 lines
Quoted 60 lines
builtin/fsmonitor--daemon.c +59
-0+/* +* This is adapted from `wait_or_whine()`. Watch the child process and +* let it get started and begin listening for requests on the socket +* before reporting our success. +*/ +static int wait_for_background_startup (pid_t pid_child) +{ +int status; +pid_t pid_seen; +enum ipc_active_state s; +time_t time_limit, now; + +time (&time_limit); + time_limit += fsmonitor__start_timeout_sec; + +for (;;) { + pid_seen =waitpid (pid_child, &status,WNOHANG ); + +if (pid_seen ==-1 ) +return error_errno (_ ("waitpid failed" )); +else if (pid_seen ==0 ) { +/* +* The child is still running (this should be +* the normal case). Try to connect to it on +* the socket and see if it is ready for +* business. +* +* If there is another daemon already running, +* our child will fail to start (possibly +* after a timeout on the lock), but we don't +* care (who responds) if the socket is live. +*/ + s =fsmonitor_ipc__get_state (); +if (s ==IPC_STATE__LISTENING ) +return 0 ; + +time (&now); +if (now > time_limit) +return error (_ ("fsmonitor--daemon not online yet" )); + }else if (pid_seen == pid_child) { +/* +* The new child daemon process shutdown while +* it was starting up, so it is not listening +* on the socket. +* +* Try to ping the socket in the odd chance +* that another daemon started (or was already +* running) while our child was starting. +* +* Again, we don't care who services the socket. +*/ + s =fsmonitor_ipc__get_state (); +if (s ==IPC_STATE__LISTENING ) +return 0 ; + +/* +* We don't care about the WEXITSTATUS() nor +* any of the WIF*(status) values because +* `cmd_fsmonitor__daemon()` does the `!!result` builtin/fsmonitor--daemon.c static int try_to_run_foreground_daemon(void) · line 419
Quoted 10 lines
builtin/fsmonitor--daemon.c +10
-0+* trick on all function return values. +* +* So it is sufficient to just report the +* early shutdown as an error. +*/ +return error (_ ("fsmonitor--daemon failed to start" )); + }else +return error (_ ("waitpid is confused" )); + } +}Ditto this. could we extend the
wait_or_whine()function (or some extended version thereof) to do what you need with callbacks?
Quoted 9 lines
It seems the main difference is just being able to pass down a flag for
waitpid(), and the loop needing to check EINTR or not depending on whether WNOHANG is passed.For e.g. the "We don't care about the
WEXITSTATUS()" you'd get that behavior with an adjustedwait_or_whine(). Wouldn't it be better to report what exit status it exits with e.g. if the top-level process is signalled? We do so in trace2 for other things we spawn...
Again, I don't want to mix my usage here with the existing code and destabilize all existing callers. Here we are spinning to give the child a chance to *start* and confirm that it is in a listening state and ready for connections. We do not wait for the child to exit (unless it dies quickly without becoming ready).
We want to end our wait as soon as we confirm that the child is ready and return. All I really need from the system is waitpid().
Also, since we started the child in my spawn_background...(), it is not in the children_to_clean list, so there is no need to mess with that.
So I'd like to leave this as is.
Jeff
Re: [PATCH v3 17/34] fsmonitor--daemon: define token-ids
In reply to Ævar Arnfjörð BjarmasonQuoted 14 lines · Ævar Arnfjörð Bjarmason
Quoted 11 lines · Jeff Hostetler via GitGitGadget
builtin/fsmonitor--daemon.c +11
-0+if (!test_env_value) { +struct timeval tv; +struct tm tm; +time_t secs; + +gettimeofday (&tv,NULL ); + secs = tv.tv_sec; +gmtime_r (&secs, &tm); + + strbuf_addf(&token->token_id, + "%"PRIu64".%d.%4d%02d%02dT%02d%02d%02d.%06ldZ",
Quoted 8 lines
builtin/fsmonitor--daemon.c static int do_as_client__status(void) · line 202
Quoted 5 lines
builtin/fsmonitor--daemon.c +5
-0+ flush_count++, +getpid (), + tm.tm_year +1900 , tm.tm_mon +1 , tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, + (long)tv.tv_usec);Just bikeshedding, but can we have tokens that mostly sort numeric-wise by time order? So time at the start, not the flush_count/getpid.
As I described in a rather large comment in the code, tokens are opaque strings -- without a less-than / greater-than relationship -- just a random string that the daemon can use (along with a sequence number) to ensure that a later request is well-defined.
Here I'm using a counter, pid, and date-stamp. I'd prefer using a GUID or UUID just to drive that home, but I didn't want to add a new .lib or .a to the build if not necessary.
Perhaps I should compute this portion as hex(hash(time())) to remove the temptation to look inside my opaque token ??
Quoted 7 lines
Maybe I'm missing something, but couldn't we just re-use the trace2 SID
+ a more trivial trailer? It would have the nice property that you could find the trace2 SID whenever you looked at such a token (could e.g. split them by "/" too), and add thetv_usec, flush_count+whatever else is needed to make it unique after the "/", no?
I would rather keep Trace2 out of this. The SID is another opaque string and I don't want to reach inside it.
Jeff
Re: [PATCH v3 19/34] fsmonitor-fs-listen-win32: implement FSMonitor backend on Windows
In reply to Johannes SchindelinQuoted 92 lines · Johannes Schindelin
Hi Jeff,
On Thu, 1 Jul 2021, Jeff Hostetler via GitGitGadget wrote:
Jeff Hostetler <[email protected]>
the win32 backend to register a watch on the working tree irectory (recursively). Also watch the <gitdir> if it is side the working tree. And to collect path change notifications atches and publish.
-off-by: Jeff Hostetler <[email protected]>
t/fsmonitor/fsmonitor-fs-listen-win32.c| 530 +++++++++++++++++++ e changed, 530 insertions(+)Quoted 10 lines
compat/fsmonitor/fsmonitor-fs-listen-win32.c +5
-0@@ -2,20 +2,550 @@ + [...] + +static struct one_watch *create_watch (struct fsmonitor_daemon_state *state, +const char *path) +{compat/fsmonitor/fsmonitor-fs-listen-win32.c line 16
Quoted 10 lines
compat/fsmonitor/fsmonitor-fs-listen-win32.c +10
-0+struct one_watch *watch =NULL ; +DWORD desired_access =FILE_LIST_DIRECTORY ; +DWORD share_mode = +FILE_SHARE_WRITE |FILE_SHARE_READ |FILE_SHARE_DELETE ; +HANDLE hDir; + + hDir =CreateFileA (path, + desired_access, share_mode,NULL ,OPEN_EXISTING , +FILE_FLAG_BACKUP_SEMANTICS |FILE_FLAG_OVERLAPPED , +NULL );The
*A()family of Win32 API functions disagree with Git in one very interesting aspect: Git always assumes UTF-8, while e.g.CreateFileA()will use the current Win32 locale to internally transform to wide characters and then callCreateFileW().This poses no problem when your locale is US American and your paths contain no non-ASCII characters.
In the Git for Windows bug tracker, it was reported that it _does_ cause problems when venturing outside such a cozy scenario (for full details, see https://github.com/git-for-windows/git/issues/3262)
I need this (and merged it before starting the process to release Git for Windows v2.32.0(2)) to fix that (could I ask you to integrate this in case a re-roll will become necessary?):
-- snipsnap --
From: Johannes Schindelin <[email protected]>
Date: Mon, 5 Jul 2021 13:51:05 +0200
Subject: [PATCH] fixup! fsmonitor-fs-listen-win32: implement FSMonitor backend
on WindowsLet's keep avoiding the
*A()family of Win32 API functions because they are susceptible to incoherent encoding problems. In Git for Windows, we always assume paths to be UTF-8 encoded. Let's use the dedicated helper to convert such a path to the wide character version, and then use the*W()function instead.Signed-off-by Johannes Schindelin <[email protected]>--- compat/fsmonitor/fsmonitor-fs-listen-win32.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)compat/fsmonitor/fsmonitor-fs-listen-win32.c +7
-2@@ -111,8 +111,14 @@ static struct one_watch *create_watch(struct fsmonitor_daemon_state *state,DWORD share_mode =FILE_SHARE_WRITE |FILE_SHARE_READ |FILE_SHARE_DELETE ;HANDLE hDir; +wchar_t wpath[MAX_PATH ]; - hDir =CreateFileA (path, +if (xutftowcs_path (wpath, path) <0 ) { +error (_ ("could not convert to wide characters: '%s'" ), path); +return NULL ; + } + + hDir =CreateFileW (wpath, desired_access, share_mode,NULL ,OPEN_EXISTING ,FILE_FLAG_BACKUP_SEMANTICS |FILE_FLAG_OVERLAPPED ,NULL ); --
Quoted 2 lines
2.32.0.windows.1.15.gf1590a75e2d
Thanks for the heads up. I'll pull this into my next release. Jeff
Re: [PATCH v3 19/34] fsmonitor-fs-listen-win32: implement FSMonitor backend on Windows
In reply to Ævar Arnfjörð BjarmasonQuoted 14 lines · Ævar Arnfjörð Bjarmason
Quoted 10 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Teach the win32 backend to register a watch on the working tree root directory (recursively). Also watch the <gitdir> if it is not inside the working tree. And to collect path change notifications into batches and publish.
Signed-off-by Jeff Hostetler <[email protected]>--- compat/fsmonitor/fsmonitor-fs-listen-win32.c | 530 +++++++++++++++++++
Quoted 7 lines
<bikeshed mode> Spying on the early history of this (looking for the Linux backend) I saw that at some point we had just
compat/fsmonitor/linux.c, and presumably some ofcompat/fsmonitor/{windows,win32,macos,darwin}.c.At some point those filenames became much much longer.
Once upon a time having "foo/bar/win32.c" and "abc/def/win32.c" would cause confusion in the debugger (I've long since forgotten which). Breaking at win32.c:30 was no longer unique.
Also, if the Makefile sends all .o's to the root directory or a unified OBJS directory rather than to the subdir containing the .c, then we have another issue during linking...
So, having been burned too many times, I prefer to make source filenames unique when possible.
Quoted 7 lines
I've noticed you tend to prefer really long file and function names, e.g. your borrowed
daemonize()becamespawn_background_fsmonitor_daemon(), I think aiming for shorter filenames & function names helps, e.g. these long names widen diffstats, and many people who hack on the code stick religiously to 80 character width terminals.
I prefer self-documenting code.
Jeff
Re: [PATCH v3 23/34] t/helper/test-touch: add helper to touch a series of files
In reply to Junio C HamanoQuoted 24 lines · Junio C Hamano
Quoted 9 lines · Jeff Hostetler via GitGitGadget
t/helper/test-touch.c new file +3
-0@@ -0,0 +1,126 @@ +/* +* test-touch.c: variation on /usr/bin/touch to speed up tests +* with a large number of files (primarily on Windows where child t/helper/test-touch.c line 13
Quoted 10 lines
t/helper/test-touch.c new file +10
-0+* process are very, very expensive). +*/ + +#include "test-tool.h" +#include "cache.h" +#include "parse-options.h" + +char *seq_pattern; +int seq_start =1 ; +int seq_count =1 ;With this in, "make sparse" dies like this:
Quoted 5 lines
SP
t/helper/test-touch.ct/helper/test-touch.c:11:6: error: symbol 'seq_pattern' was not declared. Should it be static?t/helper/test-touch.c:12:5: error: symbol 'seq_start' was not declared. Should it be static?t/helper/test-touch.c:13:5: error: symbol 'seq_count' was not declared. Should it be static?
I'll fix. Thanks!
Jeff
Re: [PATCH v3 24/34] t/perf/p7519: speed up test using "test-tool touch"
In reply to Ævar Arnfjörð BjarmasonQuoted 56 lines · Ævar Arnfjörð Bjarmason
Quoted 40 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Change p7519 to use a single "test-tool touch" command to update the mtime on a series of (thousands) files instead of invoking thousands of commands to update a single file.
This is primarily for Windows where process creation is so very slow and reduces the test run time by minutes.
Signed-off-by Jeff Hostetler <[email protected]>--- t/perf/p7519-fsmonitor.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)t/perf/p7519-fsmonitor.sh +5
-6@@ -119,10 +119,11 @@ test_expect_success "one time repo setup" ' fi && mkdir 1_file 10_files 100_files 1000_files 10000_files && - for i in $(test_seq 1 10); do touch 10_files/$i; done && - for i in $(test_seq 1 100); do touch 100_files/$i; done && - for i in $(test_seq 1 1000); do touch 1000_files/$i; done && - for i in $(test_seq 1 10000); do touch 10000_files/$i; done && + test-tool touch sequence --pattern="10_files/%d" --start=1 --count=10 && + test-tool touch sequence --pattern="100_files/%d" --start=1 --count=100 && + test-tool touch sequence --pattern="1000_files/%d" --start=1 --count=1000 && + test-tool touch sequence --pattern="10000_files/%d" --start=1 --count=10000 && + git add 1_file 10_files 100_files 1000_files 10000_files && git commit -qm "Add files" &&@@ -200,15 +201,12 @@ test_fsmonitor_suite() { # Update the mtimes on upto 100k files to make status think # that they are dirty. For simplicity, omit any files with # LFs (i.e. anything that ls-files thinks it needs to dquote). - # Then fully backslash-quote the paths to capture any - # whitespace so that they pass thru xargs properly.t/perf/p7519-fsmonitor.sh test_fsmonitor_suite() { · line 211
Quoted 10 lines
t/perf/p7519-fsmonitor.sh +1
-2# test_perf_w_drop_caches "status (dirty) ($DESC)" ' git ls-files | \ head -100000 | \ grep -v \" | \ - sed '\''s/\(.\)/\\\1/g'\'' | \ - xargs test-tool chmtime -300 && + test-tool touch stdin && git status 'Did you try to replace this with some variant of:
Quoted 5 lines
test_seq 1 10000 | xargs touchWhich (depending on your xargs version) would invoke "touch" commands with however many argv items it thinks you can handle.
a quick test on my Windows machine shows that
test_seq 1 10000 | xargs touch
takes 3.1 seconds.
just a simple
test_seq 1 10000 >/dev/null
take 0.2 seconds.
using my test-tool helper cuts that time in half.
Jeff
Re: [PATCH v3 24/34] t/perf/p7519: speed up test using "test-tool touch"
Quoted 16 lines · Jeff Hostetler
Quoted 7 lines · Ævar Arnfjörð Bjarmason
Did you try to replace this with some variant of:
test_seq 1 10000 | xargs touchWhich (depending on your xargs version) would invoke "touch" commands with however many argv items it thinks you can handle.
a quick test on my Windows machine shows that
test_seq 1 10000 | xargs touchtakes 3.1 seconds.
Quoted 7 lines
just a simple
test_seq 1 10000 >/dev/nulltake 0.2 seconds.
using my test-tool helper cuts that time in half.
Yeah, test_seq is pretty bad; it's just a loop in shell. Is there a 'seq' on windows, and does using it instead of test_seq make things faster with Ævar's suggested command?
I'd really like to modify test_seq to use seq when it's available and fall back to the looping-in-shell when we need to for various platforms.
Maybe it'd even make sense to write a 'test-tool seq' and make test_seq use that just so we can rip out that super lame shell looping.
Re: [PATCH v3 24/34] t/perf/p7519: speed up test using "test-tool touch"
Quoted 12 lines · Elijah Newren
Quoted 11 lines · Jeff Hostetler
a quick test on my Windows machine shows that
test_seq 1 10000 | xargs touchtakes 3.1 seconds.
just a simple
test_seq 1 10000 >/dev/null
Quoted 7 lines
Quoted 3 lines
take 0.2 seconds.
using my test-tool helper cuts that time in half.
Yeah,
test_seqis pretty bad; it's just a loop in shell. Is there a 'seq' on windows, and does using it instead oftest_seqmake things faster with Ævar's suggested command?
Unless I am misreading Jeff's message, I do not think that makes sense. Counting to 10000 in shell loop is trivial (0.2 seconds), but letting touch invoked 10000 times to create (or smudge mtime of, but I suspect that is not what is going on here) 10000 files takes 3.1 seconds, and of course a native binary that creates 10000 files with a single invocation would be faster.
Quoted 3 lines
I'd really like to modify
test_seqto use seq when it's available and fall back to the looping-in-shell when we need to for various platforms.
So, if I am reading Jeff correctly, that optimizes something that is not a bottleneck.
Quoted 3 lines
Maybe it'd even make sense to write a 'test-tool seq' and make
test_sequse that just so we can rip out that super lame shell looping.
Re: [PATCH v3 24/34] t/perf/p7519: speed up test using "test-tool touch"
Quoted 30 lines · Junio C Hamano
Quoted 12 lines · Elijah Newren
Quoted 11 lines · Jeff Hostetler
a quick test on my Windows machine shows that
test_seq 1 10000 | xargs touchtakes 3.1 seconds.
just a simple
test_seq 1 10000 >/dev/nullQuoted 7 lines
Quoted 3 lines
take 0.2 seconds.
using my test-tool helper cuts that time in half.
Yeah,
test_seqis pretty bad; it's just a loop in shell. Is there a 'seq' on windows, and does using it instead oftest_seqmake things faster with Ævar's suggested command?Unless I am misreading Jeff's message, I do not think that makes sense. Counting to 10000 in shell loop is trivial (0.2 seconds), but letting touch invoked 10000 times to create (or smudge mtime of, but I suspect that is not what is going on here) 10000 files takes 3.1 seconds, and of course a native binary that creates 10000 files with a single invocation would be faster.
Quoted 6 lines
Quoted 3 lines
I'd really like to modify
test_seqto use seq when it's available and fall back to the looping-in-shell when we need to for various platforms.So, if I am reading Jeff correctly, that optimizes something that is not a bottleneck.
Oh, indeed. Sorry, I misread.
Re: [PATCH v3 02/34] fsmonitor--daemon: man page
In reply to Jeff HostetlerQuoted 116 lines · Jeff Hostetler
Quoted 94 lines · Ævar Arnfjörð Bjarmason
Quoted 81 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Create a manual page describing the
git fsmonitor--daemonfeature.Signed-off-by Jeff Hostetler <[email protected]>--- Documentation/git-fsmonitor--daemon.txt | 75 +++++++++++++++++++++++++ 1 file changed, 75 insertions(+)create mode 100644
Documentation/git-fsmonitor--daemon.txtDocumentation/git-fsmonitor--daemon.txt new file +65
-0@@ -0,0 +1,75 @@ +git-fsmonitor--daemon(1) +======================== + +NAME +---- +git-fsmonitor--daemon - A Built-in File System Monitor + +SYNOPSIS +-------- +[verse] +'git fsmonitor--daemon' start +'git fsmonitor--daemon' run +'git fsmonitor--daemon' stop +'git fsmonitor--daemon' status + +DESCRIPTION +----------- + +A daemon to watch the working directory for file and directory +changes using platform-specific file system notification facilities. + +This daemon communicates directly with commands like `git status` +using the link:technical/api-simple-ipc.html[simple IPC] interface +instead of the slower linkgit:githooks[5] interface. + +This daemon is built into Git so that no third-party tools are +required. + +OPTIONS +------- + +start:: + Starts a daemon in the background. + +run:: + Runs a daemon in the foreground. + +stop:: + Stops the daemon running in the current working + directory, if present. + +status:: + Exits with zero status if a daemon is watching the + current working directory. + +REMARKS +------- + +This daemon is a long running process used to watch a single working +directory and maintain a list of the recently changed files and +directories. Performance of commands such as `git status` can be +increased if they just ask for a summary of changes to the working +directory and can avoid scanning the disk. + +When `core.useBuiltinFSMonitor` is set to `true` (see +linkgit:git-config[1]) commands, such as `git status`, will ask the +daemon for changes and automatically start it (if necessary). + +For more information see the "File System Monitor" section in +linkgit:git-update-index[1]. + +CAVEATS +------- + +The fsmonitor daemon does not currently know about submodules and doesDocumentation/git-fsmonitor--daemon.txt line 75
Quoted 10 lines
Documentation/git-fsmonitor--daemon.txt new file +10
-0+not know to filter out file system events that happen within a +submodule. If fsmonitor daemon is watching a super repo and a file is +modified within the working directory of a submodule, it will report +the change (as happening against the super repo). However, the client +will properly ignore these extra events, so performance may be affected +but it will not cause an incorrect result. + +GIT +--- +Part of the linkgit:git[1] suiteLater in the series we incrementally add features to the daemon, so
Quoted 9 lines
this
is describing a state that doesn't exist yet at this point. I think it would be better to start with a stup here and add documentation as we add features, e.g. the patch tha adds "start" should add that to the synopsis + options etc.
See the outstanding ab/config-based-hooks-base for a small example of
that.I like to lead the series with the documentation that summarizes the purpose of the entire feature or patch series. This gives the reviewer the context for the complete series that follows. In the past, we've had discussions on the list about how hard it is to review a series when the
foo.ccomes (alphabetically) beforefoo.hin the patch and all the documentation is attached to the prototypes in the .h file so the reviewer needs to bounce around in the patch or series to read the intent and then go back to the beginning to read the code. In that spirit, I think that having the complete man page come first provides necessary context and is helpful.
Quoted 4 lines
The argument that the man-page should grow as the feature grows presumes that there is a meaningful cut-point mid-series where you would adopt the first portion and delay the second to a later release or something. That division would not be useful/usable.
Isn't there such a meaningful cut-off point?
In 08/34[1] you add a skeleton of the daemon, so then the NAME/SYNOPSIS (empty)/DESCRIPTION/REMARKS/GIT sections could be added, in 09/34[3] you add stop/status commands, so then the SYNOPSIS/OPTIONS could be updated/created with those, same for the addition of run/start in 13/34[5] and 14/35[6] respectively.
Quoted 4 lines
And it just clutters up later commits in the series with man-page deltas.
So I'd like to keep it as it unless there are further objections.
I'll leave it to you to do with the feedback as you choose,
I suggested this because I think it's much easier to read patches that are larger because they incrementally update docs or tests along with code, than smaller ones that are e.g. "add docs" followed by incrementally modifying the code.
That's because you can consider those atomically. If earlier doc changes refer to later code changes you're left jumping back & forth and wondering if the code you're reading that doesn't match the docs yet is a bug, or if it's solved in some later change you're now needing to mentally keep track of.
Which is not some theoretical concern b.t.w., but exactly what I found myself doing when reading this series, hence the suggestion.
1. https://lore.kernel.org/git/f88db92d4259d1c29827e97e957daf6eda39c551.1625150864.git.gitgitgadget@gmail.com/
2. https://lore.kernel.org/git/[email protected]/
3. https://lore.kernel.org/git/02e21384ef0ca4909e0bda2c78fa63c06be22a50.1625150864.git.gitgitgadget@gmail.com/
4. https://lore.kernel.org/git/5d6646df93a17659af66f136295444d1bd834090.1625150864.git.gitgitgadget@gmail.com/
5. https://lore.kernel.org/git/5d6646df93a17659af66f136295444d1bd834090.1625150864.git.gitgitgadget@gmail.com/
6. https://lore.kernel.org/git/9fe902aad87f1192705fb69ea212a2d066d0286d.1625150864.git.gitgitgadget@gmail.com/
Re: [PATCH v3 24/34] t/perf/p7519: speed up test using "test-tool touch"
In reply to Elijah NewrenQuoted 21 lines · Elijah Newren
Quoted 20 lines · Jeff Hostetler
Quoted 7 lines · Ævar Arnfjörð Bjarmason
Did you try to replace this with some variant of:
test_seq 1 10000 | xargs touchWhich (depending on your xargs version) would invoke "touch" commands with however many argv items it thinks you can handle.
a quick test on my Windows machine shows that
test_seq 1 10000 | xargs touchtakes 3.1 seconds.
just a simple
test_seq 1 10000 >/dev/null
Quoted 8 lines
Quoted 3 lines
take 0.2 seconds.
using my test-tool helper cuts that time in half.
Yeah,
test_seqis pretty bad; it's just a loop in shell. Is there a 'seq' on windows, and does using it instead oftest_seqmake things faster with Ævar's suggested command?
The Git for Windows SDK bash environment does have a /usr/bin/seq which appears to be from GNU coreutils 8.32. (This is different from the version that I have on my Mac (which doesn't have a version number).)
Using it:
seq 1 10000 >/dev/null
takes 0.04 seconds instead of 0.2.
However, it doesn't help the touch.
seq 1 10000 | xargs touch
still takes ~3.1 seconds.
FWIW, the xargs is clustering the 10,000 files into ~4 command lines, so there is a little bit of Windows process overhead, but not that much.
seq 1 10000 | xargs wc -l | grep total
Quoted 8 lines
I'd really like to modify
test_seqto use seq when it's available and fall back to the looping-in-shell when we need to for various platforms.Maybe it'd even make sense to write a 'test-tool seq' and make
test_sequse that just so we can rip out that super lame shell looping.
Re: [PATCH v3 24/34] t/perf/p7519: speed up test using "test-tool touch"
In reply to Jeff Hostetler via GitGitGadgetQuoted 20 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Change p7519 to use a single "test-tool touch" command to update the mtime on a series of (thousands) files instead of invoking thousands of commands to update a single file.
This is primarily for Windows where process creation is so very slow and reduces the test run time by minutes.
Signed-off-by Jeff Hostetler <[email protected]>--- t/perf/p7519-fsmonitor.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)t/perf/p7519-fsmonitor.sh +0
-0@@ -119,10 +119,11 @@ test_expect_success "one time repo setup" ' fi &&
t/perf/p7519-fsmonitor.sh test_expect_success "one time repo setup" ' · line 125
Quoted 10 lines
t/perf/p7519-fsmonitor.sh +4
-4mkdir 1_file 10_files 100_files 1000_files 10000_files && - for i in $(test_seq 1 10); do touch 10_files/$i; done && - for i in $(test_seq 1 100); do touch 100_files/$i; done && - for i in $(test_seq 1 1000); do touch 1000_files/$i; done && - for i in $(test_seq 1 10000); do touch 10000_files/$i; done && + test-tool touch sequence --pattern="10_files/%d" --start=1 --count=10 && + test-tool touch sequence --pattern="100_files/%d" --start=1 --count=100 && + test-tool touch sequence --pattern="1000_files/%d" --start=1 --count=1000 && + test-tool touch sequence --pattern="10000_files/%d" --start=1 --count=10000 &&
The big win in taking *minutes* off of the run time of this test was getting rid of the for loops and one touch invocation per file.
So whether we keep my test-tool touch command or switch to test_seq or seq is open for debate. Mine seems quicker, but it is more or less round off error in the larger picture considering what we started with.
I'll play with this a bit.
Jeff
Re: [PATCH v3 24/34] t/perf/p7519: speed up test using "test-tool touch"
In reply to Jeff HostetlerQuoted 4 lines · Jeff Hostetler
FWIW, the xargs is clustering the 10,000 files into ~4 command lines, so there is a little bit of Windows process overhead, but not that much.
Quoted 9 lines
seq 1 10000 | xargs wc -l | grep total
Quoted 7 lines
I'd really like to modify
test_seqto use seq when it's available and fall back to the looping-in-shell when we need to for various platforms.
Maybe it'd even make sense to write a 'test-tool seq' and maketest_sequse that just so we can rip out that super lame shell looping.
So what lame in this picture is not shell, or process overhead, but I/O performance.
I've seen some noises about Windows file creation performance raised as an issue when doing initial checkout followed by "git clone", and an idea floated to create a bunch of open file handles for writing in threads when checkout (really the caller that repeatedly calls entry.c:write_entry() by iterating the in-core index) starts, and write out the contents in parallel, as a workaround. When I heard it, I somehow thought it was meant as a not-so-funny joke, but from the sounds of it, the I/O performance may be so horrible to require such a hack to be usable there. Sigh...
Re: [PATCH v3 14/34] fsmonitor--daemon: implement 'start' command
In reply to Jeff HostetlerQuoted 95 lines · Jeff Hostetler
My response here is in addition to Dscho's remarks on this topic. He makes excellent points that I'll just #include here. I do want to add my own $0.02 here.
Quoted 80 lines · Ævar Arnfjörð Bjarmason
Quoted 68 lines · Jeff Hostetler via GitGitGadget
builtin/fsmonitor--daemon.c +68
-0+#ifdef GIT_WINDOWS_NATIVE +/* +* Create a background process to run the daemon. It should be completely +* disassociated from the terminal. +* +* Conceptually like `daemonize()` but different because Windows does not +* have `fork(2)`. Spawn a normal Windows child process but without the +* limitations of `start_command()` and `finish_command()`. +* +* The child process execs the "git fsmonitor--daemon run" command. +* +* The current process returns so that the caller can wait for the child +* to startup before exiting. +*/ +static int spawn_background_fsmonitor_daemon (pid_t *pid) +{ +char git_exe[MAX_PATH ]; +struct strvec args =STRVEC_INIT ; +int in, out; + +GetModuleFileNameA (NULL , git_exe,MAX_PATH ); + + in =open ("/dev/null" ,O_RDONLY ); + out =open ("/dev/null" ,O_WRONLY ); + +strvec_push (&args, git_exe); +strvec_push (&args,"fsmonitor--daemon" ); +strvec_push (&args,"run" ); +strvec_pushf (&args,"--ipc-threads=%d" , fsmonitor__ipc_threads); + + *pid =mingw_spawnvpe (args.v[0 ], args.v,NULL ,NULL , in, out, out); +close (in); +close (out); + +strvec_clear (&args); + +if (*pid <0 ) +return error (_ ("could not spawn fsmonitor--daemon in the background" )); + +return 0 ; +} +#else +/* +* Create a background process to run the daemon. It should be completely +* disassociated from the terminal. +* +* This is adapted from `daemonize()`. Use `fork()` to directly +* create and run the daemon in the child process. +* +* The fork-child can just call the run code; it does not need to exec +* it. +* +* The fork-parent returns the child PID so that we can wait for the +* child to startup before exiting. +*/ +static int spawn_background_fsmonitor_daemon (pid_t *pid) +{ + *pid =fork (); + +switch (*pid) { +case 0 : +if (setsid () ==-1 ) +error_errno (_ ("setsid failed" )); +close (0 ); +close (1 ); +close (2 ); +sanitize_stdfds (); +builtin/fsmonitor--daemon.c static int try_to_run_foreground_daemon(void) · line 349
Quoted 10 lines
builtin/fsmonitor--daemon.c +10
-0+return !!fsmonitor_run_daemon (); + +case -1 : +return error_errno (_ ("could not spawn fsmonitor--daemon in the background" )); + +default : + return 0; + } +} +#endif Quoted 9 lines
The
spawn_background_fsmonitor_daemon()function here is almost the same
asdaemonize(). I wonder if this & the Windows-specific one you have here can't be refactored into an API from what's now insetup.c. Then we could makebuiltin/gc.canddaemon.cuse that, so Windows could
have background GC, and we'd have a more battle-tested central codepath for this tricky bit.
Quoted 7 lines
I'd rather not refactor all of this and add unnecessary generality and complexity just to save duplicating some of the code in
daemonize().And I'd rather not destabilize existing commands like gc and daemon by changing the
daemonize()layer on them. If those commands need help, let's have a separate conversation _later_ about what help they need and if it makes sense to combine them.
Johannes suggested in
https://lore.kernel.org/git/[email protected]/
that (if I understand that correctly, and I just skimmed the linked isse some days ago), that even if such a refactoring was done these two functions are solving subtly different problems, or something. I.e. we couldn't use it for daemonize().
Which I'd say is interesting for the code comments/commit message at least, i.e. how they're solving subtly different problems (not being able to run this & not being able to test on Windows I haven't poked at it myself).
Quoted 5 lines
Quoted 4 lines
It seems to me like the only limitations on it are to have this return slightly more general things (e.g. not set its own errors, return structured data), and maybe some callback for what to do in the child/parent.
Quoted 8 lines
There are several issues here when trying to start a background process and we're already on the edge of the behavioral differences between Windows and Unix -- let's not make things more confusing with multiple callbacks, returning structures, custom errors, and etc.
Also, since Windows doesn't do
fork(), we don't have child/parent branches in the call, so this whole "just pretend it's all Unix" model doesn't work.
Fair enough, And I think replied-to above.
Quoted 6 lines
Even if we did pretend I'd still need ifdef'd callback routines to either call
fsmonitor_run_daemon()or build a command line (or have blocks of functions that "just happen to never be called on one platform or the other").
Quoted 8 lines
What I have here is an API that the primary (read: parent) calls and gets back a 0 or -1 (with error message). And that's it. The primary can then wait for the child (whether from fork or CreateProcess) to become responsive or fail to start. And then the primary can exit (with or without error).
So I think we're good. Yes, there is an ifdef here, but I think it is worth it.
FWIW what I was noting here & elsewhere is that yes, you need to ifdef some of it, but the code you're proposing to add here is using a different pattern than the one generally preferred in this codebase.
I.e. check out how we do it for threading, we intentionally compile the "if (thread) {code}" clauses on platforms we know don't have threading, ditto the code around PCRE in grep.c.
Similarly, here in e.g. spawn_background_fsmonitor_daemon just the GetModuleFileNameA() and mingw_spawnvpe() are Windows-specifics (and could be calls to some helper that *is* ifdef'd).
In this case it's not a big deal, but as a general pattern it helps to e.g. avoid subtle syntax errors in nested ifdefs and the like, and generally encourages keeping the ifdef'd code as small as possible.
Quoted 81 lines
Quoted 71 lines
Quoted 59 lines
builtin/fsmonitor--daemon.c +59
-0+/* +* This is adapted from `wait_or_whine()`. Watch the child process and +* let it get started and begin listening for requests on the socket +* before reporting our success. +*/ +static int wait_for_background_startup (pid_t pid_child) +{ +int status; +pid_t pid_seen; +enum ipc_active_state s; +time_t time_limit, now; + +time (&time_limit); + time_limit += fsmonitor__start_timeout_sec; + +for (;;) { + pid_seen =waitpid (pid_child, &status,WNOHANG ); + +if (pid_seen ==-1 ) +return error_errno (_ ("waitpid failed" )); +else if (pid_seen ==0 ) { +/* +* The child is still running (this should be +* the normal case). Try to connect to it on +* the socket and see if it is ready for +* business. +* +* If there is another daemon already running, +* our child will fail to start (possibly +* after a timeout on the lock), but we don't +* care (who responds) if the socket is live. +*/ + s =fsmonitor_ipc__get_state (); +if (s ==IPC_STATE__LISTENING ) +return 0 ; + +time (&now); +if (now > time_limit) +return error (_ ("fsmonitor--daemon not online yet" )); + }else if (pid_seen == pid_child) { +/* +* The new child daemon process shutdown while +* it was starting up, so it is not listening +* on the socket. +* +* Try to ping the socket in the odd chance +* that another daemon started (or was already +* running) while our child was starting. +* +* Again, we don't care who services the socket. +*/ + s =fsmonitor_ipc__get_state (); +if (s ==IPC_STATE__LISTENING ) +return 0 ; + +/* +* We don't care about the WEXITSTATUS() nor +* any of the WIF*(status) values because +* `cmd_fsmonitor__daemon()` does the `!!result` builtin/fsmonitor--daemon.c static int try_to_run_foreground_daemon(void) · line 419
Quoted 10 lines
builtin/fsmonitor--daemon.c +10
-0+* trick on all function return values. +* +* So it is sufficient to just report the +* early shutdown as an error. +*/ +return error (_ ("fsmonitor--daemon failed to start" )); + }else +return error (_ ("waitpid is confused" )); + } +}Ditto this. could we extend the
wait_or_whine()function (or some extended version thereof) to do what you need with callbacks?Quoted 9 lines
It seems the main difference is just being able to pass down a flag for
waitpid(), and the loop needing to check EINTR or not depending on whether WNOHANG is passed.
For e.g. the "We don't care about theWEXITSTATUS()" you'd get that behavior with an adjustedwait_or_whine(). Wouldn't it be better to report what exit status it exits with e.g. if the top-level process is signalled? We do so in trace2 for other things we spawn...
Quoted 8 lines
Again, I don't want to mix my usage here with the existing code and destabilize all existing callers. Here we are spinning to give the child a chance to *start* and confirm that it is in a listening state and ready for connections. We do not wait for the child to exit (unless it dies quickly without becoming ready).
We want to end our wait as soon as we confirm that the child is ready and return. All I really need from the system is
waitpid().
Will this code behave correctly if the daemon we start is signalled per the WIFSIGNALED() cases the code this is derived handles, but this does not?
But sure, I just meant to point out that the flip side to "destabilize all existing callers" is reviewing new code that may be subtly buggy, and those subtle bugs (if any) would be smoked out if we were forced to extend run-command.c, i.e. to use whatever feature(s) this needs for all existing callers.
Re: [PATCH v3 17/34] fsmonitor--daemon: define token-ids
In reply to Jeff HostetlerQuoted 28 lines · Jeff Hostetler
Quoted 13 lines · Ævar Arnfjörð Bjarmason
Quoted 11 lines · Jeff Hostetler via GitGitGadget
builtin/fsmonitor--daemon.c +11
-0+if (!test_env_value) { +struct timeval tv; +struct tm tm; +time_t secs; + +gettimeofday (&tv,NULL ); + secs = tv.tv_sec; +gmtime_r (&secs, &tm); + + strbuf_addf(&token->token_id, + "%"PRIu64".%d.%4d%02d%02dT%02d%02d%02d.%06ldZ",Quoted 8 lines
builtin/fsmonitor--daemon.c static int do_as_client__status(void) · line 202
Quoted 5 lines
builtin/fsmonitor--daemon.c +5
-0+ flush_count++, +getpid (), + tm.tm_year +1900 , tm.tm_mon +1 , tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, + (long)tv.tv_usec);Just bikeshedding, but can we have tokens that mostly sort numeric-wise
by time order? So time at the start, not the flush_count/getpid.As I described in a rather large comment in the code, tokens are opaque strings -- without a less-than / greater-than relationship -- just a random string that the daemon can use (along with a sequence number) to ensure that a later request is well-defined.
Quoted 6 lines
Here I'm using a counter, pid, and date-stamp. I'd prefer using a GUID or UUID just to drive that home, but I didn't want to add a new .lib or .a to the build if not necessary.
Perhaps I should compute this portion as hex(hash(time())) to remove the temptation to look inside my opaque token ??
Why does it matter if someone looks inside your opaque token if the code is treating it as opaque by just doing a strcmp(old,new) on it?
I just suggested this as a debugging aid, i.e. when you the human (as opposed to the program) are looking at this behavior it's handy to look at the token and see that your cookies don't match, and that they look to be N seconds apart.
And furthermore, if git crashes or whatever you can now easily look up what process crashed if you've got the leftover cookie, if you've also got trace2 logs.
Quoted 10 lines
Quoted 7 lines
Maybe I'm missing something, but couldn't we just re-use the trace2 SID
+ a more trivial trailer? It would have the nice property that you could find the trace2 SID whenever you looked at such a token (could e.g. split them by "/" too), and add thetv_usec, flush_count+whatever else is needed to make it unique after the "/", no?I would rather keep Trace2 out of this. The SID is another opaque string and I don't want to reach inside it.
For the purposes of the git.git codebase it's fine to reach inside of it, especially for a "I'd like a near-enough-UUID, and I know the trace2 SID already does that per-program", so you just need e.g. a sequence counter within the program to ensure global uniqueness with other git processes for such a cookie.
Re: [PATCH v3 19/34] fsmonitor-fs-listen-win32: implement FSMonitor backend on Windows
In reply to Jeff HostetlerQuoted 24 lines · Jeff Hostetler
Quoted 9 lines · Ævar Arnfjörð Bjarmason
Quoted 7 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Teach the win32 backend to register a watch on the working tree root directory (recursively). Also watch the <gitdir> if it is not inside the working tree. And to collect path change notifications into batches and publish.
Quoted 9 lines
Quoted 3 lines
Signed-off-by Jeff Hostetler <[email protected]>--- compat/fsmonitor/fsmonitor-fs-listen-win32.c | 530 +++++++++++++++++++<bikeshed mode> Spying on the early history of this (looking for the Linux backend) I saw that at some point we had just
compat/fsmonitor/linux.c, and presumably some ofcompat/fsmonitor/{windows,win32,macos,darwin}.c.
At some point those filenames became much much longer.Once upon a time having "
foo/bar/win32.c" and "abc/def/win32.c" would cause confusion in the debugger (I've long since forgotten which). Breaking at win32.c:30 was no longer unique.
Quoted 6 lines
Also, if the
Makefilesends all .o's to the root directory or a unified OBJS directory rather than to the subdir containing the .c, then we have another issue during linking...So, having been burned too many times, I prefer to make source filenames unique when possible.
A much shorter name like compat/fsmonitor/fsmon-win32.c would achieve that goal.
Quoted 9 lines
Quoted 7 lines
I've noticed you tend to prefer really long file and function names, e.g. your borrowed
daemonize()becamespawn_background_fsmonitor_daemon(), I think aiming for shorter filenames & function names helps, e.g. these long names widen diffstats, and many people who hack on the code stick religiously to 80 character width terminals.I prefer self-documenting code.
Sure, I'm not saying daemonize() is an ideal name, just suggesting that you can both get uniqueness & self-documentation and not need to split to multiple lines in some common cases to stay within the "We try to keep to at most 80 characters per line" in CodingGuidelines in this series.
Re: [PATCH v3 24/34] t/perf/p7519: speed up test using "test-tool touch"
In reply to Junio C HamanoQuoted 19 lines · Junio C Hamano
Quoted 4 lines · Jeff Hostetler
FWIW, the xargs is clustering the 10,000 files into ~4 command lines, so there is a little bit of Windows process overhead, but not that much.
Quoted 9 lines
seq 1 10000 | xargs wc -l | grep total
Quoted 7 lines
I'd really like to modify
test_seqto use seq when it's available and fall back to the looping-in-shell when we need to for various platforms.
Maybe it'd even make sense to write a 'test-tool seq' and maketest_sequse that just so we can rip out that super lame shell looping.So what lame in this picture is not shell, or process overhead, but I/O performance.
Quoted 10 lines
I've seen some noises about Windows file creation performance raised as an issue when doing initial checkout followed by "git clone", and an idea floated to create a bunch of open file handles for writing in threads when checkout (really the caller that repeatedly calls entry.c:write_entry() by iterating the in-core index) starts, and write out the contents in parallel, as a workaround. When I heard it, I somehow thought it was meant as a not-so-funny joke, but from the sounds of it, the I/O performance may be so horrible to require such a hack to be usable there. Sigh...
Yes, there are some things here (that I believe to be I/O related) on Windows that I want to look at when I wrap up FSMonitor. And yes, some of them sound pretty stupid.
Jeff
Re: [PATCH v3 24/34] t/perf/p7519: speed up test using "test-tool touch"
In reply to Jeff HostetlerQuoted 64 lines · Jeff Hostetler
Quoted 51 lines · Ævar Arnfjörð Bjarmason
Quoted 38 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Change p7519 to use a single "test-tool touch" command to update the mtime on a series of (thousands) files instead of invoking thousands of commands to update a single file.
This is primarily for Windows where process creation is so very slow and reduces the test run time by minutes.
Signed-off-by Jeff Hostetler <[email protected]>--- t/perf/p7519-fsmonitor.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)t/perf/p7519-fsmonitor.sh +5
-4@@ -119,10 +119,11 @@ test_expect_success "one time repo setup" ' fi && mkdir 1_file 10_files 100_files 1000_files 10000_files && - for i in $(test_seq 1 10); do touch 10_files/$i; done && - for i in $(test_seq 1 100); do touch 100_files/$i; done && - for i in $(test_seq 1 1000); do touch 1000_files/$i; done && - for i in $(test_seq 1 10000); do touch 10000_files/$i; done && + test-tool touch sequence --pattern="10_files/%d" --start=1 --count=10 && + test-tool touch sequence --pattern="100_files/%d" --start=1 --count=100 && + test-tool touch sequence --pattern="1000_files/%d" --start=1 --count=1000 && + test-tool touch sequence --pattern="10000_files/%d" --start=1 --count=10000 && + git add 1_file 10_files 100_files 1000_files 10000_files && git commit -qm "Add files" && @@ -200,15 +201,12 @@ test_fsmonitor_suite() { # Update the mtimes on upto 100k files to make status think# that they are dirty. For simplicity, omit any files with # LFs (i.e. anything that ls-files thinks it needs to dquote).t/perf/p7519-fsmonitor.sh +0
-2- # Then fully backslash-quote the paths to capture any - # whitespace so that they pass thru xargs properly.t/perf/p7519-fsmonitor.sh test_fsmonitor_suite() { · line 211
Quoted 10 lines
t/perf/p7519-fsmonitor.sh +1
-2# test_perf_w_drop_caches "status (dirty) ($DESC)" ' git ls-files | \ head -100000 | \ grep -v \" | \ - sed '\''s/\(.\)/\\\1/g'\'' | \ - xargs test-tool chmtime -300 && + test-tool touch stdin && git status 'Did you try to replace this with some variant of:
Quoted 5 lines
test_seq 1 10000 | xargs touchWhich (depending on your xargs version) would invoke "touch" commands
with however many argv items it thinks you can handle.a quick test on my Windows machine shows that
test_seq 1 10000 | xargs touchtakes 3.1 seconds.
Quoted 7 lines
just a simple
test_seq 1 10000 >/dev/nulltake 0.2 seconds.
using my test-tool helper cuts that time in half.
There's what Elijah mentioned about test_seq, so maybe it's just that.
But what I was suggesting was using the xargs mode where it does N arguments at a time.
Does this work for you, and does it cause xargs to invoke "touch" with the relevant N number of arguments, and does it help with the performance?
test_seq 1 10000 | xargs touch
test_seq 1 10000 | xargs -n 10 touch
test_seq 1 10000 | xargs -n 100 touch
test_seq 1 10000 | xargs -n 1000 touchetc.
Also I didn't notice this before, but the -300 part of "chmtime -300" was redundant before then? I.e. you're implicitly changing it to "=+0" instead with your "touch" helper, are you not?
Re: [PATCH v3 14/34] fsmonitor--daemon: implement 'start' command
In reply to Ævar Arnfjörð BjarmasonQuoted 98 lines · Ævar Arnfjörð Bjarmason
Quoted 95 lines · Jeff Hostetler
My response here is in addition to Dscho's remarks on this topic. He makes excellent points that I'll just #include here. I do want to add my own $0.02 here.
Quoted 74 lines · Ævar Arnfjörð Bjarmason
Quoted 59 lines · Jeff Hostetler via GitGitGadget
builtin/fsmonitor--daemon.c +59
-0+/* +* This is adapted from `wait_or_whine()`. Watch the child process and +* let it get started and begin listening for requests on the socket +* before reporting our success. +*/ +static int wait_for_background_startup (pid_t pid_child) +{ +int status; +pid_t pid_seen; +enum ipc_active_state s; +time_t time_limit, now; + +time (&time_limit); + time_limit += fsmonitor__start_timeout_sec; + +for (;;) { + pid_seen =waitpid (pid_child, &status,WNOHANG ); + +if (pid_seen ==-1 ) +return error_errno (_ ("waitpid failed" )); +else if (pid_seen ==0 ) { +/* +* The child is still running (this should be +* the normal case). Try to connect to it on +* the socket and see if it is ready for +* business. +* +* If there is another daemon already running, +* our child will fail to start (possibly +* after a timeout on the lock), but we don't +* care (who responds) if the socket is live. +*/ + s =fsmonitor_ipc__get_state (); +if (s ==IPC_STATE__LISTENING ) +return 0 ; + +time (&now); +if (now > time_limit) +return error (_ ("fsmonitor--daemon not online yet" )); + }else if (pid_seen == pid_child) { +/* +* The new child daemon process shutdown while +* it was starting up, so it is not listening +* on the socket. +* +* Try to ping the socket in the odd chance +* that another daemon started (or was already +* running) while our child was starting. +* +* Again, we don't care who services the socket. +*/ + s =fsmonitor_ipc__get_state (); +if (s ==IPC_STATE__LISTENING ) +return 0 ; + +/* +* We don't care about the WEXITSTATUS() nor +* any of the WIF*(status) values because +* `cmd_fsmonitor__daemon()` does the `!!result` builtin/fsmonitor--daemon.c static int try_to_run_foreground_daemon(void) · line 419
Quoted 10 lines
builtin/fsmonitor--daemon.c +10
-0+* trick on all function return values. +* +* So it is sufficient to just report the +* early shutdown as an error. +*/ +return error (_ ("fsmonitor--daemon failed to start" )); + }else +return error (_ ("waitpid is confused" )); + } +}Ditto this. could we extend the
wait_or_whine()function (or some extended version thereof) to do what you need with callbacks?Quoted 9 lines
It seems the main difference is just being able to pass down a flag for
waitpid(), and the loop needing to check EINTR or not depending on whether WNOHANG is passed.
For e.g. the "We don't care about theWEXITSTATUS()" you'd get that behavior with an adjustedwait_or_whine(). Wouldn't it be better to report what exit status it exits with e.g. if the top-level process is signalled? We do so in trace2 for other things we spawn...Again, I don't want to mix my usage here with the existing code and destabilize all existing callers. Here we are spinning to give the child a chance to *start* and confirm that it is in a listening state and ready for connections. We do not wait for the child to exit (unless it dies quickly without becoming ready).
Quoted 6 lines
Quoted 2 lines
We want to end our wait as soon as we confirm that the child is ready and return. All I really need from the system is
waitpid().Will this code behave correctly if the daemon we start is signalled per the
WIFSIGNALED()cases the code this is derived handles, but this does not?
We're only waiting until the child gets started and is able to receive requests -- what happens to it after we have confirmed that it is ready is not our concern (after all, the parent is about to exit anyway and the child is going to continue on).
If waitpid() gives us a WIFSIGNALED (or any other WIF*() state) before we have spoken to it, we will return a "failed to start".
But again, that signal would have to arrive immediately after we spawned it and *before* we could talk to it. If the child is signaled after we confirmed it was ready, we don't care because the parent process will be gone.
(If the child is signaled or is killed (or crashes or whatever), the next Git command (like "status") that tries to talk to it will re-start it implicitly -- the git fsmonitor--daemon start command here is an explicit start.)
Quoted 7 lines
But sure, I just meant to point out that the flip side to "destabilize all existing callers" is reviewing new code that may be subtly buggy, and those subtle bugs (if any) would be smoked out if we were forced to extend
run-command.c, i.e. to use whatever feature(s) this needs for all existing callers.
That would/could have a massive footprint. And I've already established that my usage here is sufficiently different from existing uses that the result would be a mess. IMHO.
Jeff
Re: [PATCH v3 24/34] t/perf/p7519: speed up test using "test-tool touch"
In reply to Ævar Arnfjörð BjarmasonQuoted 80 lines · Ævar Arnfjörð Bjarmason
Quoted 64 lines · Jeff Hostetler
Quoted 51 lines · Ævar Arnfjörð Bjarmason
Quoted 38 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Change p7519 to use a single "test-tool touch" command to update the mtime on a series of (thousands) files instead of invoking thousands of commands to update a single file.
This is primarily for Windows where process creation is so very slow and reduces the test run time by minutes.
Signed-off-by Jeff Hostetler <[email protected]>--- t/perf/p7519-fsmonitor.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)t/perf/p7519-fsmonitor.sh +5
-4@@ -119,10 +119,11 @@ test_expect_success "one time repo setup" ' fi && mkdir 1_file 10_files 100_files 1000_files 10000_files && - for i in $(test_seq 1 10); do touch 10_files/$i; done && - for i in $(test_seq 1 100); do touch 100_files/$i; done && - for i in $(test_seq 1 1000); do touch 1000_files/$i; done && - for i in $(test_seq 1 10000); do touch 10000_files/$i; done && + test-tool touch sequence --pattern="10_files/%d" --start=1 --count=10 && + test-tool touch sequence --pattern="100_files/%d" --start=1 --count=100 && + test-tool touch sequence --pattern="1000_files/%d" --start=1 --count=1000 && + test-tool touch sequence --pattern="10000_files/%d" --start=1 --count=10000 && + git add 1_file 10_files 100_files 1000_files 10000_files && git commit -qm "Add files" && @@ -200,15 +201,12 @@ test_fsmonitor_suite() { # Update the mtimes on upto 100k files to make status think# that they are dirty. For simplicity, omit any files with # LFs (i.e. anything that ls-files thinks it needs to dquote).t/perf/p7519-fsmonitor.sh +0
-2- # Then fully backslash-quote the paths to capture any - # whitespace so that they pass thru xargs properly.t/perf/p7519-fsmonitor.sh test_fsmonitor_suite() { · line 211
Quoted 10 lines
t/perf/p7519-fsmonitor.sh +1
-2# test_perf_w_drop_caches "status (dirty) ($DESC)" ' git ls-files | \ head -100000 | \ grep -v \" | \ - sed '\''s/\(.\)/\\\1/g'\'' | \ - xargs test-tool chmtime -300 && + test-tool touch stdin && git status 'Did you try to replace this with some variant of:
Quoted 5 lines
test_seq 1 10000 | xargs touchWhich (depending on your xargs version) would invoke "touch" commands
with however many argv items it thinks you can handle.a quick test on my Windows machine shows that
test_seq 1 10000 | xargs touchtakes 3.1 seconds.
Quoted 7 lines
just a simple
test_seq 1 10000 >/dev/nulltake 0.2 seconds.
using my test-tool helper cuts that time in half.
There's what Elijah mentioned about
test_seq, so maybe it's just that.But what I was suggesting was using the xargs mode where it does N arguments at a time.
Quoted 8 lines
Does this work for you, and does it cause xargs to invoke "touch" with the relevant N number of arguments, and does it help with the performance?
test_seq 1 10000 | xargs touch test_seq 1 10000 | xargs -n 10 touch test_seq 1 10000 | xargs -n 100 touch test_seq 1 10000 | xargs -n 1000 touch
The GFW SDK version of xargs does have -n N and it does work as advertised. And it does slow down things considerably. Letting it do ~2500 per command in 4 commands took the 3.1 seconds listed above.
Add a -n 100 to it takes 5.7 seconds, so process creation overhead is a factor here.
Quoted 7 lines
etc.
Also I didn't notice this before, but the -300 part of "chmtime -300" was redundant before then? I.e. you're implicitly changing it to "=+0" instead with your "touch" helper, are you not?
Right. I'm changing it to the current time.
Jeff
Re: [PATCH v3 02/34] fsmonitor--daemon: man page
In reply to Ævar Arnfjörð BjarmasonHi Ævar,
Quoted 8 lines · Ævar Arnfjörð Bjarmason
[snip Ævar's suggestion to populate the manual page incrementally, interspersed with the commits that finalize implementing the respective functionality]
I suggested this because I think it's much easier to read patches that are larger because they incrementally update docs or tests along with code, than smaller ones that are e.g. "add docs" followed by incrementally modifying the code.
My experience is the exact opposite of yours: shorter patches are easier to read.
Quoted 1 line
That's because you can consider those atomically.
No, in a patch series you cannot consider any patch completely atomically. Just like you don't consider any paragraph in any well-written book out of context.
Quoted 4 lines
If earlier doc changes refer to later code changes you're left jumping back & forth and wondering if the code you're reading that doesn't match the docs yet is a bug, or if it's solved in some later change you're now needing to mentally keep track of.
You only keep jumping back and forth when reviewing _patches_. We try to do code review on this mailing list, which means that you have the code locally and review it in the correct context. When you do that, a design document is quite helpful. And the proposed manual page serves as such a design document.
Quoted 2 lines
Which is not some theoretical concern b.t.w., but exactly what I found myself doing when reading this series, hence the suggestion.
Part of the problem here seems to be that this patch series saw many reviewer suggestions that forced it to increase in length. That was probably not very helpful, after all.
The first iteration had 23 patches. Reviews forced it to grow to 28 patches in the second iteration. And that was still not enough, therefore the third iteration consisted of 34 patches. And if you had had your way, requiring Jeff to include a Linux backend in the same patch series, it would have to increase in size again, and not just by a little.
This all sounds like we're truly falling into the trap of ignoring the rule that the perfect is the enemy of the good.
I really would like to come back to a focused review that truly improves the patches at hand, and avoids conflating the review of the actual patch series with matters of personal taste (which is a recipe for disagreement). After all, we are interested in getting this feature out to users who need to work with very large worktrees, right? At least that's my goal here.
Ciao,
Johannes
Re: [PATCH v3 11/34] fsmonitor-fs-listen-win32: stub in backend for Windows
In reply to Ævar Arnfjörð BjarmasonHi Ævar,
Quoted 47 lines · Ævar Arnfjörð Bjarmason
Quoted 44 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Stub in empty backend for fsmonitor--daemon on Windows.
Signed-off-by Jeff Hostetler <[email protected]>--- Makefile | 13 ++++++ compat/fsmonitor/fsmonitor-fs-listen-win32.c | 21 +++++++++ compat/fsmonitor/fsmonitor-fs-listen.h | 49 ++++++++++++++++++++ config.mak.uname | 2 + contrib/buildsystems/CMakeLists.txt | 5 ++ 5 files changed, 90 insertions(+) create mode 100644 compat/fsmonitor/fsmonitor-fs-listen-win32.c create mode 100644 compat/fsmonitor/fsmonitor-fs-listen.hMakefile +10
-0@@ -467,6 +467,11 @@ all:: # directory, and the JSON compilation database 'compile_commands.json' will be # created at the root of the repository. # +# If your platform supports a built-in fsmonitor backend, set +# FSMONITOR_DAEMON_BACKEND to the "<name>" of the corresponding +# `compat/fsmonitor/fsmonitor-fs-listen-<name>.c` that implements the +# `fsmonitor_fs_listen__*()` routines. +# # Define DEVELOPER to enable more compiler warnings. Compiler version # and family are auto detected, but could be overridden by defining # COMPILER_FEATURES (see config.mak.dev). You can still set@@ -1929,6 +1934,11 @@ ifdef NEED_ACCESS_ROOT_HANDLER COMPAT_OBJS += compat/access.o endif +ifdef FSMONITOR_DAEMON_BACKEND + COMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND + COMPAT_OBJS += compat/fsmonitor/fsmonitor-fs-listen-$(FSMONITOR_DAEMON_BACKEND).o +endif + ifeq ($(TCLTK_PATH),) NO_TCLTK = NoThanks endif@@ -2793,6 +2803,9 @@ GIT-BUILD-OPTIONS: FORCE
Quoted 8 lines
Makefile GIT-BUILD-OPTIONS: FORCE · line 2808
Quoted 6 lines
Makefile +3
-0@echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+ @echo DC_SHA1=\''$(subst ','\'',$(subst ','\'',$(DC_SHA1)))'\' >>$@+ @echo X=\'$(X)\' >>$@+ +ifdef FSMONITOR_DAEMON_BACKEND + @echo FSMONITOR_DAEMON_BACKEND=\''$(subst ','\'',$(subst ','\'',$(FSMONITOR_DAEMON_BACKEND)))'\' >>$@+ +endifWhy put this in an ifdef?
Why not? What benefit does this question bring to improving this patch series?
Ciao,
Dscho
Re: [PATCH v3 12/34] fsmonitor-fs-listen-macos: stub in backend for MacOS
In reply to Ævar Arnfjörð BjarmasonHi Ævar,
Quoted 55 lines · Ævar Arnfjörð Bjarmason
Quoted 44 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Stub in empty implementation of fsmonitor--daemon
backend for MacOS.Signed-off-by Jeff Hostetler <[email protected]>--- compat/fsmonitor/fsmonitor-fs-listen-macos.c | 20 ++++++++++++++++++++ config.mak.uname | 2 ++ contrib/buildsystems/CMakeLists.txt | 3 +++ 3 files changed, 25 insertions(+) create mode 100644 compat/fsmonitor/fsmonitor-fs-listen-macos.ccompat/fsmonitor/fsmonitor-fs-listen-macos.c new file +20
-0@@ -0,0 +1,20 @@ +#include "cache.h" +#include "fsmonitor.h" +#include "fsmonitor-fs-listen.h" + +int fsmonitor_fs_listen__ctor (struct fsmonitor_daemon_state *state) +{ +return -1 ; +} + +void fsmonitor_fs_listen__dtor (struct fsmonitor_daemon_state *state) +{ +} + +void fsmonitor_fs_listen__stop_async (struct fsmonitor_daemon_state *state) +{ +} + +void fsmonitor_fs_listen__loop (struct fsmonitor_daemon_state *state) +{ +}config.mak.uname +0
-0config.mak.uname ifeq ($(uname_S),Darwin) · line 150
Quoted 4 lines
config.mak.uname +1
-0@@ -147,6 +147,8 @@ ifeq ($(uname_S),Darwin) MSGFMT = /usr/local/opt/gettext/bin/msgfmt endif endif + FSMONITOR_DAEMON_BACKEND = macosA rather trivial point, but can't we pick one of "macos" or "darwin" (I'd think going with the existing uname is better) and name the file after the uname (or lower-case thereof)?
Quoted 3 lines
Makes these make rules more consistent too, we could just set this to "YesPlease" here, and then lower case the uname for the file compilation/include.
So you suggest that we name the new stuff after an uname that reflects a name that is no longer relevant? I haven't seen a real Darwin system in quite a long time, have you?
I don't find such a suggestion constructive, I have to admit.
Ciao,
Johannes
Re: [PATCH v3 19/34] fsmonitor-fs-listen-win32: implement FSMonitor backend on Windows
In reply to Ævar Arnfjörð BjarmasonHi Ævar,
Quoted 45 lines · Ævar Arnfjörð Bjarmason
Quoted 24 lines · Jeff Hostetler
Quoted 9 lines · Ævar Arnfjörð Bjarmason
Quoted 7 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Teach the win32 backend to register a watch on the working tree root directory (recursively). Also watch the <gitdir> if it is not inside the working tree. And to collect path change notifications into batches and publish.
Quoted 9 lines
Quoted 3 lines
Signed-off-by Jeff Hostetler <[email protected]>--- compat/fsmonitor/fsmonitor-fs-listen-win32.c | 530 +++++++++++++++++++<bikeshed mode> Spying on the early history of this (looking for the Linux backend) I saw that at some point we had just
compat/fsmonitor/linux.c, and presumably some ofcompat/fsmonitor/{windows,win32,macos,darwin}.c.
At some point those filenames became much much longer.Once upon a time having "
foo/bar/win32.c" and "abc/def/win32.c" would cause confusion in the debugger (I've long since forgotten which). Breaking at win32.c:30 was no longer unique.Quoted 6 lines
Also, if the
Makefilesends all .o's to the root directory or a unified OBJS directory rather than to the subdir containing the .c, then we have another issue during linking...So, having been burned too many times, I prefer to make source filenames unique when possible.
A much shorter name like
compat/fsmonitor/fsmon-win32.cwould achieve that goal.Quoted 8 lines
Quoted 7 lines
I've noticed you tend to prefer really long file and function names, e.g. your borrowed
daemonize()becamespawn_background_fsmonitor_daemon(), I think aiming for shorter filenames & function names helps, e.g. these long names widen diffstats, and many people who hack on the code stick religiously to 80 character width terminals.
Quoted 7 lines
Quoted 1 line
I prefer self-documenting code.
Sure, I'm not saying
daemonize()is an ideal name, just suggesting that you can both get uniqueness & self-documentation and not need to split to multiple lines in some common cases to stay within the "We try to keep to at most 80 characters per line" in CodingGuidelines in this series.
While you are entitled to have your taste, I have to point out that Jeff is just as entitled to their taste, and I don't think that you can claim that yours is better.
So I wonder what the intended outcome of this review is? To make the patch better? Or to pit taste against taste?
Ciao,
Johannes
Re: [PATCH v3 19/34] fsmonitor-fs-listen-win32: implement FSMonitor backend on Windows
Quoted 57 lines · Johannes Schindelin
Hi Ævar,
Quoted 45 lines · Ævar Arnfjörð Bjarmason
Quoted 24 lines · Jeff Hostetler
Quoted 9 lines · Ævar Arnfjörð Bjarmason
Quoted 7 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Teach the win32 backend to register a watch on the working tree root directory (recursively). Also watch the <gitdir> if it is not inside the working tree. And to collect path change notifications into batches and publish.
Quoted 9 lines
Quoted 3 lines
Signed-off-by Jeff Hostetler <[email protected]>--- compat/fsmonitor/fsmonitor-fs-listen-win32.c | 530 +++++++++++++++++++<bikeshed mode> Spying on the early history of this (looking for the Linux backend) I saw that at some point we had just
compat/fsmonitor/linux.c, and presumably some ofcompat/fsmonitor/{windows,win32,macos,darwin}.c.
At some point those filenames became much much longer.Once upon a time having "
foo/bar/win32.c" and "abc/def/win32.c" would cause confusion in the debugger (I've long since forgotten which). Breaking at win32.c:30 was no longer unique.Quoted 6 lines
Also, if the
Makefilesends all .o's to the root directory or a unified OBJS directory rather than to the subdir containing the .c, then we have another issue during linking...So, having been burned too many times, I prefer to make source filenames unique when possible.
A much shorter name like
compat/fsmonitor/fsmon-win32.cwould achieve that goal.Quoted 8 lines
Quoted 7 lines
I've noticed you tend to prefer really long file and function names, e.g. your borrowed
daemonize()becamespawn_background_fsmonitor_daemon(), I think aiming for shorter filenames & function names helps, e.g. these long names widen diffstats, and many people who hack on the code stick religiously to 80 character width terminals.Quoted 7 lines
Quoted 1 line
I prefer self-documenting code.
Sure, I'm not saying
daemonize()is an ideal name, just suggesting that you can both get uniqueness & self-documentation and not need to split to multiple lines in some common cases to stay within the "We try to keep to at most 80 characters per line" in CodingGuidelines in this series.
Quoted 6 lines
While you are entitled to have your taste, I have to point out that Jeff is just as entitled to their taste, and I don't think that you can claim that yours is better.
So I wonder what the intended outcome of this review is? To make the patch better? Or to pit taste against taste?
Neither, to address a misunderstanding.
Sure, if a reviewer points out "maybe change X to Y" and the reply is "I like X better than Y", fair enough.
My reading of Jeff H.'s upthread was that he'd misunderstood my suggesting of that Y for a Z.
I.e. that shortening a name like fsmonitor-fs-listen-win32.c (X) necessarily had to mean that we'd have a win32.c (Z), negatively impacting some debugging workflows, as opposed to just a shorter-but-unique name like fsmon-win32.c (Y).
Ditto for daemonize() (X/Z) and spawn_background_fsmonitor_daemon() (X).
I'm certain that with this reply we're thoroughly into the "respectfully disagree" territory as opposed to having a misunderstanding.
I also take and agree your implied point that there's no point in having a yes/no/yes/no/yes argument on-list, and I did not mean to engage in such a thing, only to clear up the misunderstanding, if any.
I'll only say that I don't think that something like long variable/file etc. names is *just* a matter of taste, seeing as we have a fairly strict "keep to at most 80 characters per line" as the 2nd item in the C coding style (after "use tabs, not spaces").
That matter of taste for one developer objectively makes it harder to stay within the bounds of the coding style for furute maintenance.
We do have active contributors that I understand actually use terminals of that size to work on this project (CC'd, but maybe I misrecall that for one/both). I'm not one of those people, but I do find that maintaining code with needlessly long identifiers in this codebase is painful.
E.g. in a patch I just submitted I've been working on similarly long identifiers in the refs code[1], and with say a long variable/type name and using a long-named function you get to the point of needing to place each individual argument of the function on its own line, or near enough to that.
Re: [PATCH v3 12/34] fsmonitor-fs-listen-macos: stub in backend for MacOS
In reply to Johannes SchindelinQuoted 59 lines · Johannes Schindelin
Hi Ævar,
Quoted 55 lines · Ævar Arnfjörð Bjarmason
Quoted 44 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Stub in empty implementation of fsmonitor--daemon
backend for MacOS.Signed-off-by Jeff Hostetler <[email protected]>--- compat/fsmonitor/fsmonitor-fs-listen-macos.c | 20 ++++++++++++++++++++ config.mak.uname | 2 ++ contrib/buildsystems/CMakeLists.txt | 3 +++ 3 files changed, 25 insertions(+) create mode 100644 compat/fsmonitor/fsmonitor-fs-listen-macos.ccompat/fsmonitor/fsmonitor-fs-listen-macos.c new file +20
-0@@ -0,0 +1,20 @@ +#include "cache.h" +#include "fsmonitor.h" +#include "fsmonitor-fs-listen.h" + +int fsmonitor_fs_listen__ctor (struct fsmonitor_daemon_state *state) +{ +return -1 ; +} + +void fsmonitor_fs_listen__dtor (struct fsmonitor_daemon_state *state) +{ +} + +void fsmonitor_fs_listen__stop_async (struct fsmonitor_daemon_state *state) +{ +} + +void fsmonitor_fs_listen__loop (struct fsmonitor_daemon_state *state) +{ +}config.mak.uname +0
-0config.mak.uname ifeq ($(uname_S),Darwin) · line 150
Quoted 4 lines
config.mak.uname +1
-0@@ -147,6 +147,8 @@ ifeq ($(uname_S),Darwin) MSGFMT = /usr/local/opt/gettext/bin/msgfmt endif endif + FSMONITOR_DAEMON_BACKEND = macosA rather trivial point, but can't we pick one of "macos" or "darwin" (I'd think going with the existing uname is better) and name the file after the uname (or lower-case thereof)?
Quoted 7 lines
Quoted 3 lines
Makes these make rules more consistent too, we could just set this to "YesPlease" here, and then lower case the uname for the file compilation/include.
So you suggest that we name the new stuff after an
unamethat reflects a name that is no longer relevant? I haven't seen a real Darwin system in quite a long time, have you?
It's not current? On an Mac Mini M1 which got released this year:
% uname -s
Darwin
We then have the same in config.mak.uname, it seemed the most obvious and consistent to carry that through to file inclusion.
Re: [PATCH v3 19/34] fsmonitor-fs-listen-win32: implement FSMonitor backend on Windows
In reply to Johannes SchindelinQuoted 11 lines · Johannes Schindelin
Quoted 9 lines · Ævar Arnfjörð Bjarmason
Quoted 1 line · Jeff Hostetler
I prefer self-documenting code.
Sure, I'm not saying
daemonize()is an ideal name, just suggesting that you can both get uniqueness & self-documentation and not need to split to multiple lines in some common cases to stay within the "We try to keep to at most 80 characters per line" in CodingGuidelines in this series.
Quoted 6 lines
While you are entitled to have your taste, I have to point out that Jeff is just as entitled to their taste, and I don't think that you can claim that yours is better.
So I wonder what the intended outcome of this review is? To make the patch better? Or to pit taste against taste?
Unless you read minds you can't possibly know what the taste of other people will be.
So you put forward what *you* think is better, and then find out if others agree with your taste or not. If it turns out you are the only one that thinks it's better, so be it.
For what it's worth, I agree with Ævar that daemonize() is better and I find your statement "I prefer self-documenting code" a) not an argument,
b) not a valid argument if we fill in the dots, and c) passively aggressive.
Each one of us can only do one thing: express our opinion. What else can we do?
Reviewers should not be chastised for expressing their opinion.
Signature · 1 line
Felipe Contreras
Re: [PATCH v3 11/34] fsmonitor-fs-listen-win32: stub in backend for Windows
In reply to Johannes SchindelinQuoted 60 lines · Johannes Schindelin
Hi Ævar,
Quoted 47 lines · Ævar Arnfjörð Bjarmason
Quoted 44 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Stub in empty backend for fsmonitor--daemon on Windows.
Signed-off-by Jeff Hostetler <[email protected]>--- Makefile | 13 ++++++ compat/fsmonitor/fsmonitor-fs-listen-win32.c | 21 +++++++++ compat/fsmonitor/fsmonitor-fs-listen.h | 49 ++++++++++++++++++++ config.mak.uname | 2 + contrib/buildsystems/CMakeLists.txt | 5 ++ 5 files changed, 90 insertions(+) create mode 100644 compat/fsmonitor/fsmonitor-fs-listen-win32.c create mode 100644 compat/fsmonitor/fsmonitor-fs-listen.hMakefile +10
-0@@ -467,6 +467,11 @@ all:: # directory, and the JSON compilation database 'compile_commands.json' will be # created at the root of the repository. # +# If your platform supports a built-in fsmonitor backend, set +# FSMONITOR_DAEMON_BACKEND to the "<name>" of the corresponding +# `compat/fsmonitor/fsmonitor-fs-listen-<name>.c` that implements the +# `fsmonitor_fs_listen__*()` routines. +# # Define DEVELOPER to enable more compiler warnings. Compiler version # and family are auto detected, but could be overridden by defining # COMPILER_FEATURES (see config.mak.dev). You can still set@@ -1929,6 +1934,11 @@ ifdef NEED_ACCESS_ROOT_HANDLER COMPAT_OBJS += compat/access.o endif +ifdef FSMONITOR_DAEMON_BACKEND + COMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND + COMPAT_OBJS += compat/fsmonitor/fsmonitor-fs-listen-$(FSMONITOR_DAEMON_BACKEND).o +endif + ifeq ($(TCLTK_PATH),) NO_TCLTK = NoThanks endif@@ -2793,6 +2803,9 @@ GIT-BUILD-OPTIONS: FORCEQuoted 8 lines
Makefile GIT-BUILD-OPTIONS: FORCE · line 2808
Quoted 6 lines
Makefile +3
-0@echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+ @echo DC_SHA1=\''$(subst ','\'',$(subst ','\'',$(DC_SHA1)))'\' >>$@+ @echo X=\'$(X)\' >>$@+ +ifdef FSMONITOR_DAEMON_BACKEND + @echo FSMONITOR_DAEMON_BACKEND=\''$(subst ','\'',$(subst ','\'',$(FSMONITOR_DAEMON_BACKEND)))'\' >>$@+ +endifWhy put this in an ifdef?
Quoted 2 lines
Why not? What benefit does this question bring to improving this patch series?
I think that when adding code to the Makefile it makes sense to follow the prevailing pattern, unless there's a good reason to do otherwise, e.g. on my build:
$ grep "''" GIT-BUILD-OPTIONS NO_CURL='' NO_EXPAT='' NO_PERL='' NO_PTHREADS='' NO_PYTHON='' NO_UNIX_SOCKETS='' X=''
Why does the FSMONITOR_DAEMON_BACKEND option require a nonexistent line as opposed to an empty one?
Re: [PATCH v3 11/34] fsmonitor-fs-listen-win32: stub in backend for Windows
In reply to Johannes SchindelinQuoted 13 lines · Johannes Schindelin
Quoted 11 lines · Ævar Arnfjörð Bjarmason
Makefile GIT-BUILD-OPTIONS: FORCE · line 2808
Quoted 7 lines · Jeff Hostetler via GitGitGadget
Makefile +3
-0@@ -2793,6 +2803,9 @@ GIT-BUILD-OPTIONS: FORCE @echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+ @echo DC_SHA1=\''$(subst ','\'',$(subst ','\'',$(DC_SHA1)))'\' >>$@+ @echo X=\'$(X)\' >>$@+ +ifdef FSMONITOR_DAEMON_BACKEND + @echo FSMONITOR_DAEMON_BACKEND=\''$(subst ','\'',$(subst ','\'',$(FSMONITOR_DAEMON_BACKEND)))'\' >>$@+ +endifWhy put this in an ifdef?
Quoted 2 lines
Why not? What benefit does this question bring to improving this patch series?
This is a common debate tactic known as "shifting the burden of proof".
Ævar does not need to prove that your patch is undesirable, *you* have to prove that it is desirable.
You have the burden of proof, so you should answer the question.
https://www.logicallyfallacious.com/logicalfallacies/Shifting-of-the-Burden-of-Proof
Signature · 1 line
Felipe Contreras
Re: [PATCH v3 02/34] fsmonitor--daemon: man page
In reply to Johannes SchindelinQuoted 6 lines · Johannes Schindelin
Quoted 4 lines · Ævar Arnfjörð Bjarmason
[snip Ævar's suggestion to populate the manual page incrementally, interspersed with the commits that finalize implementing the respective functionality]
Quoted 7 lines
Quoted 4 lines
I suggested this because I think it's much easier to read patches that are larger because they incrementally update docs or tests along with code, than smaller ones that are e.g. "add docs" followed by incrementally modifying the code.
My experience is the exact opposite of yours: shorter patches are easier to read.
It depends.
Quoted 5 lines
Quoted 1 line
That's because you can consider those atomically.
No, in a patch series you cannot consider any patch completely atomically. Just like you don't consider any paragraph in any well-written book out of context.
But you do not put every sentence in a paragraph.
Sometimes a paragraph can contain a single sentence, or a single word even. But other times to properly read what is being tried to say you need a pretty big paragraph.
Quoted 2 lines
This all sounds like we're truly falling into the trap of ignoring the rule that the perfect is the enemy of the good.
Have you established that this is good enough?
Signature · 1 line
Felipe Contreras
Re: [PATCH v3 11/34] fsmonitor-fs-listen-win32: stub in backend for Windows
In reply to Ævar Arnfjörð BjarmasonQuoted 28 lines · Ævar Arnfjörð Bjarmason
Quoted 10 lines
Quoted 10 lines
Quoted 8 lines
Makefile +5
-0+ifdef FSMONITOR_DAEMON_BACKEND + COMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND + COMPAT_OBJS += compat/fsmonitor/fsmonitor-fs-listen-$(FSMONITOR_DAEMON_BACKEND).o +endif + ifeq ($(TCLTK_PATH),) NO_TCLTK = NoThanks endif...
Quoted 4 lines
Quoted 1 line
Why put this in an ifdef?
Why not? What benefit does this question bring to improving this patch series?
I think that when adding code to the
Makefileit makes sense to follow the prevailing pattern, unless there's a good reason to do otherwise, e.g. on my build:$ grep "''" GIT-BUILD-OPTIONS NO_CURL='' NO_EXPAT='' NO_PERL='' NO_PTHREADS='' NO_PYTHON='' NO_UNIX_SOCKETS='' X=''
Quoted 2 lines
Why does the
FSMONITOR_DAEMON_BACKENDoption require a nonexistent line as opposed to an empty one?
I do not quite get the question.
#!/bin/sh
cat >make.file <<\EOF
all::
ifeq ($(FSMONITOR_DAEMON_BACKEND),)
echo it is empty
endif
ifdef FSMONITOR_DAEMON_BACKEND
echo it is undefined
endif
EOF
echo "unset???"
make -f make.file
echo "set to empty???"
make -f make.file FSMONITOR_DAEMON_BACKEND=
These two make invocations will give us the same result, showing that "is it set to empty" and "is it unset" are the same.
Re: [PATCH v3 11/34] fsmonitor-fs-listen-win32: stub in backend for Windows
Quoted 7 lines · Junio C Hamano
#!/bin/sh
cat >make.file <<\EOF
all::
ifeq ($(FSMONITOR_DAEMON_BACKEND),)
echo it is empty
endif
ifdefFSMONITOR_DAEMON_BACKEND
An obvious typo. This must be "ifndef", of course.
Quoted 12 lines
echo it is undefined
endif
EOFecho "unset???"
make -f make.fileecho "set to empty???"
make -f make.file FSMONITOR_DAEMON_BACKEND=These two make invocations will give us the same result, showing that "is it set to empty" and "is it unset" are the same.
Re: [PATCH v3 19/34] fsmonitor-fs-listen-win32: implement FSMonitor backend on Windows
In reply to Ævar Arnfjörð BjarmasonQuoted 42 lines · Ævar Arnfjörð Bjarmason
Quoted 41 lines · Johannes Schindelin
Hi Ævar,
Quoted 37 lines · Ævar Arnfjörð Bjarmason
Quoted 24 lines · Jeff Hostetler
Quoted 9 lines · Ævar Arnfjörð Bjarmason
Quoted 7 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Teach the win32 backend to register a watch on the working tree root directory (recursively). Also watch the <gitdir> if it is not inside the working tree. And to collect path change notifications into batches and publish.
Quoted 9 lines
Quoted 3 lines
Signed-off-by Jeff Hostetler <[email protected]>--- compat/fsmonitor/fsmonitor-fs-listen-win32.c | 530 +++++++++++++++++++<bikeshed mode> Spying on the early history of this (looking for the Linux backend) I saw that at some point we had just
compat/fsmonitor/linux.c, and presumably some ofcompat/fsmonitor/{windows,win32,macos,darwin}.c.
At some point those filenames became much much longer.Once upon a time having "
foo/bar/win32.c" and "abc/def/win32.c" would cause confusion in the debugger (I've long since forgotten which). Breaking at win32.c:30 was no longer unique.Quoted 6 lines
Also, if the
Makefilesends all .o's to the root directory or a unified OBJS directory rather than to the subdir containing the .c, then we have another issue during linking...So, having been burned too many times, I prefer to make source filenames unique when possible.
A much shorter name like
compat/fsmonitor/fsmon-win32.cwould achieve that goal.
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
I've noticed you tend to prefer really long file and function names, e.g. your borrowed
daemonize()becamespawn_background_fsmonitor_daemon(), I think aiming for shorter filenames & function names helps, e.g. these long names widen diffstats, and many people who hack on the code stick religiously to 80 character width terminals.
At least "daemon"/"daemonize" already implies "background"; so even if we have the extra function, "spawn_fsmon_daemon()" would be enough info.
Quoted 22 lines
Quoted 10 lines
Quoted 9 lines
Quoted 3 lines
Quoted 1 line
I prefer self-documenting code.
Sure, I'm not saying
daemonize()is an ideal name, just suggesting that you can both get uniqueness & self-documentation and not need to split to multiple lines in some common cases to stay within the "We try to keep to at most 80 characters per line" in CodingGuidelines in this series.Quoted 6 lines
While you are entitled to have your taste, I have to point out that Jeff is just as entitled to their taste, and I don't think that you can claim that yours is better.
So I wonder what the intended outcome of this review is? To make the patch better? Or to pit taste against taste?
Neither, to address a misunderstanding.
Sure, if a reviewer points out "maybe change X to Y" and the reply is "I like X better than Y", fair enough.
Quoted 7 lines
My reading of Jeff H.'s upthread was that he'd misunderstood my suggesting of that Y for a Z.
I.e. that shortening a name like
fsmonitor-fs-listen-win32.c(X) necessarily had to mean that we'd have awin32.c(Z), negatively impacting some debugging workflows, as opposed to just a shorter-but-unique name likefsmon-win32.c(Y).
Short-as-possible-while-being-meaningful is a pretty important usability thing git. There's a good reason git supports OID prefix abbreviations, after all.
Not my area of expertise, but AFAIK git's rename detection is affected by basename; and I've encountered debugger confusion with non-unique basenames while debugging other codebases.
My brain works like a naive "strcmp"/"memcmp": long common prefixes slows down my ability to differentiate filenames.
Having lots of common terms/prefixes on the screen works like camoflage to me and slows down my ability to process things. I suppose my eyes and cognitive abilities are below average; and even worse due to the pandemic numbing my brain.
Quoted 1 line
Ditto for
daemonize()(X/Z) andspawn_background_fsmonitor_daemon()(X).
(what I said above)
Quoted 12 lines
I'm certain that with this reply we're thoroughly into the "respectfully disagree" territory as opposed to having a misunderstanding.
I also take and agree your implied point that there's no point in having a yes/no/yes/no/yes argument on-list, and I did not mean to engage in such a thing, only to clear up the misunderstanding, if any.
I'll only say that I don't think that something like long variable/file etc. names is *just* a matter of taste, seeing as we have a fairly strict "keep to at most 80 characters per line" as the 2nd item in the C coding style (after "use tabs, not spaces").
Quoted 8 lines
That matter of taste for one developer objectively makes it harder to stay within the bounds of the coding style for furute maintenance.
We do have active contributors that I understand actually use terminals of that size to work on this project (CC'd, but maybe I misrecall that for one/both). I'm not one of those people, but I do find that maintaining code with needlessly long identifiers in this codebase is painful.
Thanks for Cc-ing me.
Yes, I'm one of those developers. Accessibility matters to me:
my eyesight certainly isn't getting better with age (nor do I expect anyone elses'). I need giant fonts to reduce eye and neck strain.
Fwiw, newspaper publishers figured out line width decades/centuries ago and wrap lines despite having large sheets to work on.
I mostly work over mosh or ssh to reduce noise and heat locally. There's no bandwidth for VNC or similar, and graphical stuff tends to be unstable UI-wise anyways so I stick to the terminal.
Taste does have much to do with it: I favor stable, reliable tools (e.g. POSIX, Perl5, git) that works well on both old and new hardware. I avoid mainstream "desktop" software since they tend to have unstable UIs which break users' workflows while requiring more powerful HW.
Complex graphics drivers tend to get unreliable, too, especially when one is stuck with old HW that gets limited support from vendors. It's also difficult to fix complex drivers as a hobbyist given the one-off HW/vendor-specific knowledge required.
So we shouldn't expect a developer with old HW can have more than a standard text terminal. This is an accessibility problem for developers lacking in finances.
This is also a problem for developers wishing to backdoors+bugs found in modern systems (IntelME, AMD-PSP, endless stream of CPU bugs).
Back to health-related accessibility; I've also had joint problems for many years so shorter identifiers helps reduce typing I need to do. I mostly had that under control pre-pandemic, but it's been a huge struggle to find adequate replacements for activities I used to rely on to manage the pain.
Re: [PATCH v3 11/34] fsmonitor-fs-listen-win32: stub in backend for Windows
In reply to Junio C HamanoQuoted 46 lines · Junio C Hamano
Quoted 28 lines · Ævar Arnfjörð Bjarmason
Quoted 10 lines
Quoted 10 lines
Quoted 8 lines
Makefile +5
-0+ifdef FSMONITOR_DAEMON_BACKEND + COMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND + COMPAT_OBJS += compat/fsmonitor/fsmonitor-fs-listen-$(FSMONITOR_DAEMON_BACKEND).o +endif + ifeq ($(TCLTK_PATH),) NO_TCLTK = NoThanks endif...
Quoted 4 lines
Quoted 1 line
Why put this in an ifdef?
Why not? What benefit does this question bring to improving this patch series?
I think that when adding code to the
Makefileit makes sense to follow the prevailing pattern, unless there's a good reason to do otherwise, e.g. on my build:$ grep "''" GIT-BUILD-OPTIONS NO_CURL='' NO_EXPAT='' NO_PERL='' NO_PTHREADS='' NO_PYTHON='' NO_UNIX_SOCKETS='' X=''Quoted 2 lines
Why does the
FSMONITOR_DAEMON_BACKENDoption require a nonexistent line as opposed to an empty one?I do not quite get the question.
#!/bin/sh
cat >make.file <<\EOF
all::
ifeq ($(FSMONITOR_DAEMON_BACKEND),)
echo it is empty
endif
ifdefFSMONITOR_DAEMON_BACKEND
echo it is undefined
endif
EOF
Quoted 8 lines
echo "unset???"
make -f make.fileecho "set to empty???"
make -f make.file FSMONITOR_DAEMON_BACKEND=These two make invocations will give us the same result, showing that "is it set to empty" and "is it unset" are the same.
Indeed, which is why I'm pointing out that wrapping it in an ifdef is pointless, which is why we don't do it for the other ones.
We do have a bunch of ifdef'd things there for perf etc., I'm not sure if it matters or not for those.
Re: [PATCH v3 11/34] fsmonitor-fs-listen-win32: stub in backend for Windows
In reply to Ævar Arnfjörð BjarmasonQuoted 61 lines · Ævar Arnfjörð Bjarmason
Quoted 44 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Stub in empty backend for fsmonitor--daemon on Windows.
Signed-off-by Jeff Hostetler <[email protected]>--- Makefile | 13 ++++++ compat/fsmonitor/fsmonitor-fs-listen-win32.c | 21 +++++++++ compat/fsmonitor/fsmonitor-fs-listen.h | 49 ++++++++++++++++++++ config.mak.uname | 2 + contrib/buildsystems/CMakeLists.txt | 5 ++ 5 files changed, 90 insertions(+)create mode 100644
compat/fsmonitor/fsmonitor-fs-listen-win32.ccreate mode 100644compat/fsmonitor/fsmonitor-fs-listen.hMakefile +10
-0@@ -467,6 +467,11 @@ all:: # directory, and the JSON compilation database 'compile_commands.json' will be # created at the root of the repository. # +# If your platform supports a built-in fsmonitor backend, set +# FSMONITOR_DAEMON_BACKEND to the "<name>" of the corresponding +# `compat/fsmonitor/fsmonitor-fs-listen-<name>.c` that implements the +# `fsmonitor_fs_listen__*()` routines. +# # Define DEVELOPER to enable more compiler warnings. Compiler version # and family are auto detected, but could be overridden by defining # COMPILER_FEATURES (see config.mak.dev). You can still set@@ -1929,6 +1934,11 @@ ifdef NEED_ACCESS_ROOT_HANDLER COMPAT_OBJS += compat/access.o endif +ifdef FSMONITOR_DAEMON_BACKEND + COMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND + COMPAT_OBJS += compat/fsmonitor/fsmonitor-fs-listen-$(FSMONITOR_DAEMON_BACKEND).o +endif + ifeq ($(TCLTK_PATH),) NO_TCLTK = NoThanks endifMakefile GIT-BUILD-OPTIONS: FORCE · line 2808
Quoted 6 lines
Makefile +3
-0@@ -2793,6 +2803,9 @@ GIT-BUILD-OPTIONS: FORCE @echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+ @echo DC_SHA1=\''$(subst ','\'',$(subst ','\'',$(DC_SHA1)))'\' >>$@+ @echo X=\'$(X)\' >>$@+ +ifdef FSMONITOR_DAEMON_BACKEND + @echo FSMONITOR_DAEMON_BACKEND=\''$(subst ','\'',$(subst ','\'',$(FSMONITOR_DAEMON_BACKEND)))'\' >>$@+ +endifWhy put this in an ifdef?
In
342e9ef2d9e(Introduce a performance testing framework, 2012-02-17) we started doing that for some perf/test options (which b.t.w., I don't really see the reason for, maybe it's some subtlety in howtest-lib.shpicks those up).
Quoted 6 lines
But for all the other compile-time stuff we don't ifdef it, we just define it, and then you get an empty value or not.
This would AFAICT be the first build-time-for-the-C-program option we ifdef for writing a line to GIT-BUILD-OPTIONS.
(I'm going to respond here on the original question rather than on any of the follow up responses in an attempt at diffusing things a bit.)
I added the ifdef because I thought it to be the *most conservative* thing that I could do. The output of the generated file on unsupported platforms should be *identical* to what it was before my changes. I only alter the contents of the generated file on supported platforms.
Later, when the generated file is consumed, we don't need to worry about the effect (if any) on incremental compiles -- we will know that it won't be set -- just like it was not set in the original compile.
That change appears right before a 12 other ifdef'd symbols also being written to that generated file. Most are test and perf, but some are not. But my point is that the pattern is present already.
The original question also references a 9.5 year old commit which uses the same pattern as I've used here. It also muddies the water on why it was/wasn't needed back then. And hints at possible side-effects in some of our test scripts. So it is clear that the confusion/disagreements that we are having with the current patch and whether or not to ifdef are not new.
So, is there value in being explicit and having the ifdef ??
There are well defined Make rules (and Junio gave us a very elegant little script to demonstrate that), but the subtleties are there. Especially with our use generated files like GIT-BUILD-OPTIONS. We have a mailing list full of experts and yet this question received a lot more discussion than I thought possible or necessary, but it took a test script to demonstrate that the results are the same and it doesn't matter. Perhaps the clarity is worth it for the price of a simple ifdef.
So, how much time have we (collectively) wasted discussing this subtlety ??
To summarize, I added the ifdef to make it explicitly clear that I'm not altering behavior on unsupported platforms. I can remove it from V4 if desired or I can keep it. (We all now know that it doesn't functionally matter -- it does however, provide clarity.)
Sorry if this sounded like a rant,
Jeff
Re: [PATCH v3 06/34] fsmonitor: config settings are repository-specific
In reply to Ævar Arnfjörð BjarmasonQuoted 11 lines · Ævar Arnfjörð Bjarmason
On Thu, Jul 01 2021, Jeff Hostetler via GitGitGadget wrote:
In a reference to a discussion[1] about an earlier version of this patch you said:
I'm going to ignore all of the thread responses to this patch dealing with how we acquire config settings and macros and etc. Those issues are completely independent of FSMonitor (which is already way too big).
Quoted 5 lines
Since then the changes to
repo-settings.chave become a lot larger, so let's take a look...1. https://lore.kernel.org/git/[email protected]/
2. https://lore.kernel.org/git/[email protected]/
Yes, there was a large conversation about re-doing how config values are acquired or whatever and it was nested inside of the context of a completely unrelated topic. It still is.
I would like to focus on FSMonitor using the existing config API. I'm only looking up ~2 config values. And that code is fairly minor considering everything else in this patch series.
Yes, there's a bizarre initialization with memset(-1), but it can wait.
Later (in a clean context) we can address and focus on the config API and/or structure initialization that you're talking about here.
FWIW, in V4 I'll refactor the block of code that I added into the body of prepare_repo_settings() to address your later concerns. This version will hopefully be more readable.
Quoted 29 lines
Quoted 13 lines
repo-settings.c +3
-0@@ -5,10 +5,42 @@#define UPDATE_DEFAULT_BOOL (s,v) do { if (s == -1) { s = v; } } while(0) +/* +* Return 1 if the repo/workdir is incompatible with FSMonitor. +*/ repo-settings.c line 20
Quoted 10 lines
repo-settings.c +10
-0+static int is_repo_incompatible_with_fsmonitor (struct repository *r) +{ +const char *const_strval; + +/* +* Bare repositories don't have a working directory and +* therefore, nothing to watch. +*/ +if (!r->worktree) +return 1 ;Looking ahead in this series you end up using
FSMONITOR_MODE_INCOMPATIBLEin two places in the codebase. Inbuiltin/update-index.cto throw a "repository is incompatible with fsmonitor" error.
Quoted 5 lines
Can't that case just be replaced with
setup_work_tree()? Other sub-modes of update-index already die implicitly on that, e.g.:$ git update-index test fatal: this operation must be run in a work tree
I will refactor that static function in V4, but I want it to return an indication of whether the repo is compatible only and let the command print the error/die as is appropriate for the daemon and/or update-index. The daemon should not start on an incompatible repo. Likewise, update-index should not enable the extension in the index.
We share some of that code with the client side code, like status, which wants to talk to the hook or daemon if supported/present/allowed. If the repo is incompatible, then status should just behave in the classic manner. So I don't want the detection code to print those error messages or die.
Quoted 11 lines
The other case is:
+
prepare_repo_settings(the_repository);
+ if (!the_repository->worktree)
+ return error(_("fsmonitor-daemon does not support bare repos '%s'"),
+xgetcwd());
+ if (the_repository->settings.fsmonitor_mode==FSMONITOR_MODE_INCOMPATIBLE)
+ return error(_("fsmonitor-daemon is incompatible with this repo '%s'"),
+the_repository->worktree);
Quoted 1 line
I.e. we just checked
the_repository->worktree, but it's not that, but....
yes, I currently have 2 types of repos that I want to disable both the hook and daemon version of FSMonitor. I'll update the error messages to specify the reason why we are incompatible.
Quoted 10 lines
Quoted 10 lines
repo-settings.c +9
-0+ +/* +* GVFS (aka VFS for Git) is incompatible with FSMonitor. +* +* Granted, core Git does not know anything about GVFS and +* we shouldn't make assumptions about a downstream feature, +* but users can install both versions. And this can lead +* to incorrect results from core Git commands. So, without +* bringing in any of the GVFS code, do a simple config test
Quoted 8 lines
repo-settings.c line 34
Quoted 5 lines
repo-settings.c +5
-0+* for a published config setting. (We do not look at the +* various *_TEST_* environment variables.) +*/ +if (!repo_config_get_value (r,"core.virtualfilesystem" , &const_strval)) +return 1 ;I'm skeptical of us hardcoding a third-party software config variable. Can't GitVFS handle this somehow on its end?
Adding the test for a GVFS-specific config setting is questionable. And perhaps we should move it to our downstream fork.
The value in putting it here for now (at least) is that it makes clear the structure for supporting other types of incompatible repos.
For example, perhaps we want to disallow repos that are on remote file systems (since we might not be able to get FS events). It would be nice to be able to prevent the daemon from starting and/or from status from trying to connect to a daemon that will be a disappointment. The code I have here serves as a model for adding such additional restrictions. And keeps us from trying to prematurely collapse the code into a simple expression.
Quoted 7 lines
But just in terms of implementation it seems the end result of that is to emit a very confusing error to the user. Sinc we already checked for bare repos we run into this and instead of sayingwhen we should really say "hey, maybe disable your core.virtualFileSystem setting", we say "your repo is incompatible".
I'll update the error messages to make that clear.
Quoted 19 lines
repo-settings.c line 43
Quoted 9 lines
repo-settings.c +5
-0+ +return 0 ; +} +void prepare_repo_settings (struct repository *r) {int value;char *strval; +const char *const_strval;Can be declared in the "else" below.
Quoted 7 lines
repo-settings.c +0
-0if (r->settings.initialized)return ;@@ -26,6 +58,22 @@ void prepare_repo_settings(struct repository *r)UPDATE_DEFAULT_BOOL (r->settings.commit_graph_read_changed_paths,1 );UPDATE_DEFAULT_BOOL (r->settings.gc_write_commit_graph,1 );
Quoted 7 lines
repo-settings.c void prepare_repo_settings(struct repository *r) · line 62
Quoted 2 lines
repo-settings.c +2
-0+ r->settings.fsmonitor_hook_path =NULL ; + r->settings.fsmonitor_mode =FSMONITOR_MODE_DISABLED ;With the memset earlier (b.t.w. I've got a patch to fix all this bizarre behavior in
repo-settings.c, but have been waiting on this series we implicitly set it toFSMONITOR_MODE_UNSET(-1) with the memset, but then never use that ever.
I'm working around the bogus -1 value that the structure has been initialize with and that I'm inheriting. I'll completely set my fsmonitor_mode variable so that I don't care what it is initialized to.
I created the _UNSET value as a reminder that there is this memset(-1) there and that must be attended to.
In my V4 I'll add comments to that effect.
Quoted 17 lines
Your code in
update-index.cthen for a check against "FSMONITOR_MODE_DISABLED" says "core.useBuiltinFSMonitor is unset;".repo-settings.c void prepare_repo_settings(struct repository *r) · line 64
Quoted 2 lines
repo-settings.c +2
-0+if (is_repo_incompatible_with_fsmonitor (r)) + r->settings.fsmonitor_mode =FSMONITOR_MODE_INCOMPATIBLE ;Style: should have {} braces on all arms.
repo-settings.c void prepare_repo_settings(struct repository *r) · line 67
Quoted 3 lines
repo-settings.c +3
-0+else if (!repo_config_get_bool (r,"core.usebuiltinfsmonitor" , &value) + && value) + r->settings.fsmonitor_mode =FSMONITOR_MODE_IPC ;Here you're conflating false with whether the variable is set at all. I guess that works out here since if it's false we want to fall through to...
Quoted 3 lines
repo-settings.c void prepare_repo_settings(struct repository *r) · line 68
Quoted 1 line
repo-settings.c +1
-0+else {...ignoring it and looing at core.fsmonitor instead.
yes, if core.useBuiltinFSMonitor is true, we do not need to look at the hook pathname.
Quoted 7 lines
repo-settings.c void prepare_repo_settings(struct repository *r) · line 70
Quoted 3 lines
repo-settings.c +2
-0+if (repo_config_get_pathname (r,"core.fsmonitor" , &const_strval)) + const_strval =getenv ("GIT_TEST_FSMONITOR" );If it's not set we pay attention to
GIT_TEST_FSMONITOR, so the behavior from the oldgit_config_get_fsmonitor(). So even if the env variable is set we want to take the config variable over it, correct?
GIT_TEST_FSMONITOR sets the hook path for testing and we're not using the hook API at all. So it is kind of ill-defined what that test env var should do if you have IPC turned on, so I'm ignoring it.
Quoted 10 lines
repo-settings.c void prepare_repo_settings(struct repository *r) · line 72
Quoted 3 lines
repo-settings.c +2
-0+if (const_strval && *const_strval) { + r->settings.fsmonitor_hook_path =strdup (const_strval);We had a strbuf_detach()'d string in the case of
repo_config_get_pathname(), but here westrdup()it again in case we were in thegetenv()codepath. This code probably leaks memory now anyway, but perhaps it's better to split up the two so we make it easier to deal with who owns/frees what in the future.
repo_config_get_pathname() returns a const char** which implies that it is not a detached buffer. it is a deep trek thru the config code, but it eventually gets to git_configset_get_*() which is documented as returning a pointer into a configset cache and that the caller should not free it. so dup'ing it is appropriate.
Similarly, getenv() is also returning a pointer to a buffer that the caller does not own. so dup'ing it here is also appropriate.
Thanks,
Jeff
Re: [PATCH v3 11/34] fsmonitor-fs-listen-win32: stub in backend for Windows
In reply to Ævar Arnfjörð BjarmasonQuoted 24 lines · Ævar Arnfjörð Bjarmason
Quoted 24 lines
Quoted 4 lines
Quoted 2 lines
Quoted 1 line
Why put this in an ifdef?
...
Why does the
FSMONITOR_DAEMON_BACKENDoption require a nonexistent line as opposed to an empty one?I do not quite get the question.
#!/bin/sh
cat >make.file <<\EOF
all::
ifeq ($(FSMONITOR_DAEMON_BACKEND),)
echo it is empty
endif
ifndefFSMONITOR_DAEMON_BACKEND
echo it is undefined
endif
EOFecho "unset???"
make -f make.fileecho "set to empty???"
make -f make.file FSMONITOR_DAEMON_BACKEND=
Quoted 8 lines
Quoted 2 lines
These two make invocations will give us the same result, showing that "is it set to empty" and "is it unset" are the same.
Indeed, which is why I'm pointing out that wrapping it in an ifdef is pointless, which is why we don't do it for the other ones.
We do have a bunch of ifdef'd things there for perf etc., I'm not sure if it matters or not for those.
Sorry, but I still do not get the question. There are bunch of ifndef in Makefile in addition to ifeq/ifneq and your question
FSMONITOR_DAEMON_BACKEND option require a nonexistent line as opposed to an empty one?
is asking "why is it X" when X is not quite true. I presume that your "wrapping it in an ifdef" refers to a construct like this:
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Makefile ifdef NEED_ACCESS_ROOT_HANDLER · line 1940
Quoted 4 lines
Makefile +4
-0+ifdef FSMONITOR_DAEMON_BACKEND + COMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND + COMPAT_OBJS += compat/fsmonitor/fsmonitor-fs-listen-$(FSMONITOR_DAEMON_BACKEND).o +endif
but is your suggestion that it should be written like this instead?
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Makefile ifdef NEED_ACCESS_ROOT_HANDLER · line 1940
Quoted 4 lines
Makefile +4
-0+ifneq ($(FSMONITOR_DAEMON_BACKEND),) + COMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND + COMPAT_OBJS += compat/fsmonitor/fsmonitor-fs-listen-$(FSMONITOR_DAEMON_BACKEND).o +endif
I do not think the latter is any easier to follow (and we have many ifdef and ifndef in our Makefile already). Perhaps I will see what you mean when I see your "better alternative", but so far, I am not successfully guessing what it is.
Re: [PATCH v3 08/34] fsmonitor--daemon: add a built-in fsmonitor daemon
In reply to Ævar Arnfjörð BjarmasonQuoted 60 lines · Ævar Arnfjörð Bjarmason
On Thu, Jul 01 2021, Jeff Hostetler via GitGitGadget wrote:
A general comment on this series (including previous patches). We've usually tried to bend over backwards in git's codebase not to have big ifdef blocks, so we compile most code the same everywhere. We waste a bit of object code, but that's fine.
See
9c897c5c2ad(pack-objects: remove #ifdefNO_PTHREADS, 2018-11-03) for a good exmaple of bad code being turned to good.E.g. in this case:
Quoted 31 lines
builtin/fsmonitor--daemon.c new file +31
-0+#ifdef HAVE_FSMONITOR_DAEMON_BACKEND + +int cmd_fsmonitor__daemon (int argc,const char **argv,const char *prefix) +{ +const char *subcmd; + +struct option options[] = { +OPT_END () + }; + +if (argc <2 ) +usage_with_options (builtin_fsmonitor__daemon_usage, options); + +if (argc ==2 && !strcmp (argv[1 ],"-h" )) +usage_with_options (builtin_fsmonitor__daemon_usage, options); + +git_config (git_default_config,NULL ); + + subcmd = argv[1 ]; + argv--; + argc++; + + argc =parse_options (argc, argv, prefix, options, + builtin_fsmonitor__daemon_usage,0 ); + +die (_ ("Unhandled subcommand '%s'" ), subcmd); +} + +#else +int cmd_fsmonitor__daemon (int argc,const char **argv,const char *prefix) +{builtin/fsmonitor--daemon.c line 53
Quoted 10 lines
builtin/fsmonitor--daemon.c new file +10
-0+struct option options[] = { +OPT_END () + }; + +if (argc ==2 && !strcmp (argv[1 ],"-h" )) +usage_with_options (builtin_fsmonitor__daemon_usage, options); + + die(_("fsmonitor--daemon not supported on this platform")); +} +#endif This whole thing could really just be a -DHAVE_FSMONITOR_DAEMON_BACKEND=1 or -DHAVE_FSMONITOR_DAEMON_BACKEND=0 somewhere (depending), and then somewhere in the middle of the first function:
Quoted 3 lines
if (!HAVE_FSMONITOR_DAEMON_BACKEND)
die(_("fsmonitor--daemon not supported on this platform"));This whole file will be filled up with ~1500 lines of static functions that only make sense when the daemon is supported and that make calls to platform-specific backends.
I suppose we could stub in an empty backend (something like that in 11/34 and 12/34) and hack in all stuff in the makefile to link to it in the unsupported case, but that seems like a lot of effort just to avoid an ifdef here.
I mean, the intent of the #else block is quite clear and we're not fooling the reader with a large source file of code that will never be used on their platform.
We could consider splitting this source file into a supported and unsupported version and have the makefile select the right .c file. We'd have to move the usage and stuff to a shared header and etc. That would eliminate the ifdef, but it would break the convention of the source filename matching the command name.
I'm not sure it's worth the bother TBH.
Jeff
Re: [PATCH v3 19/34] fsmonitor-fs-listen-win32: implement FSMonitor backend on Windows
In reply to Eric WongQuoted 7 lines · Eric Wong
Quoted 6 lines · Ævar Arnfjörð Bjarmason
Quoted 5 lines · Johannes Schindelin
Hi Ævar,
Quoted 1 line · Ævar Arnfjörð Bjarmason
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines · Jeff Hostetler
Quoted 6 lines · Ævar Arnfjörð Bjarmason
Quoted 4 lines · Jeff Hostetler via GitGitGadget
Quoted 3 lines
From: Jeff Hostetler <[email protected]>
...
Eric, welcome to the conversation and thanks for sharing your concerns.
For my upcoming V4 I've shortened the filenames of the various backends, renamed the -macos one to -darwin, and shortened the names of the fsm-listener API, and the names of those static functions associated with starting the daemon in the background.
I think this covers all of the issues raised across several patches in the series.
Jeff
Re: [PATCH v3 24/34] t/perf/p7519: speed up test using "test-tool touch"
In reply to Jeff HostetlerQuoted 87 lines · Jeff Hostetler
Quoted 81 lines · Ævar Arnfjörð Bjarmason
Quoted 64 lines · Jeff Hostetler
Quoted 51 lines · Ævar Arnfjörð Bjarmason
Quoted 38 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Change p7519 to use a single "test-tool touch" command to update the mtime on a series of (thousands) files instead of invoking thousands of commands to update a single file.
This is primarily for Windows where process creation is so very slow and reduces the test run time by minutes.
Signed-off-by Jeff Hostetler <[email protected]>--- t/perf/p7519-fsmonitor.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)t/perf/p7519-fsmonitor.sh +5
-4@@ -119,10 +119,11 @@ test_expect_success "one time repo setup" ' fi && mkdir 1_file 10_files 100_files 1000_files 10000_files && - for i in $(test_seq 1 10); do touch 10_files/$i; done && - for i in $(test_seq 1 100); do touch 100_files/$i; done && - for i in $(test_seq 1 1000); do touch 1000_files/$i; done && - for i in $(test_seq 1 10000); do touch 10000_files/$i; done && + test-tool touch sequence --pattern="10_files/%d" --start=1 --count=10 && + test-tool touch sequence --pattern="100_files/%d" --start=1 --count=100 && + test-tool touch sequence --pattern="1000_files/%d" --start=1 --count=1000 && + test-tool touch sequence --pattern="10000_files/%d" --start=1 --count=10000 && + git add 1_file 10_files 100_files 1000_files 10000_files && git commit -qm "Add files" && @@ -200,15 +201,12 @@ test_fsmonitor_suite() { # Update the mtimes on upto 100k files to make status think# that they are dirty. For simplicity, omit any files with # LFs (i.e. anything that ls-files thinks it needs to dquote).t/perf/p7519-fsmonitor.sh +0
-2- # Then fully backslash-quote the paths to capture any - # whitespace so that they pass thru xargs properly.t/perf/p7519-fsmonitor.sh test_fsmonitor_suite() { · line 211
Quoted 10 lines
t/perf/p7519-fsmonitor.sh +1
-2# test_perf_w_drop_caches "status (dirty) ($DESC)" ' git ls-files | \ head -100000 | \ grep -v \" | \ - sed '\''s/\(.\)/\\\1/g'\'' | \ - xargs test-tool chmtime -300 && + test-tool touch stdin && git status 'Did you try to replace this with some variant of:
Quoted 5 lines
test_seq 1 10000 | xargs touchWhich (depending on your xargs version) would invoke "touch" commands
with however many argv items it thinks you can handle.a quick test on my Windows machine shows that
test_seq 1 10000 | xargs touchtakes 3.1 seconds.
Quoted 7 lines
just a simple
test_seq 1 10000 >/dev/nulltake 0.2 seconds.
using my test-tool helper cuts that time in half.
There's what Elijah mentioned about
test_seq, so maybe it's just that.
But what I was suggesting was using the xargs mode where it does N arguments at a time.
Does this work for you, and does it cause xargs to invoke "touch" with
the relevant N number of arguments, and does it help with the performance?Quoted 4 lines
test_seq 1 10000 | xargs touch test_seq 1 10000 | xargs -n 10 touch test_seq 1 10000 | xargs -n 100 touch test_seq 1 10000 | xargs -n 1000 touch
Quoted 6 lines
The GFW SDK version of xargs does have
-n Nand it does work as advertised. And it does slow down things considerably. Letting it do ~2500 per command in 4 commands took the 3.1 seconds listed above.Add a -n 100 to it takes 5.7 seconds, so process creation overhead is a factor here.
Doesn't -n 2500 being faster than -n 100 suggest the opposite of process overhead being the deciding factor? With -n 2500 you'll invoke 4 touch processes, so one takes 2500/3.1 =~ 0.8s to run, whereas with -n 100 you invoke 100 of them, so if the overall time is then 5.7 seconds that's 5.7/100 =~ ~0.06s.
Or am I misunderstanding you, or does some implicit parallelism kick in with that version of xargs depending on -n?
Quoted 8 lines
Quoted 6 lines
etc.
Also I didn't notice this before, but the -300 part of "chmtime -300"
was redundant before then? I.e. you're implicitly changing it to "=+0" instead with your "touch" helper, are you not?Right. I'm changing it to the current time.
If that "while we're at it change the behavior of the test" is wanted I think it should be called out in the commit message. Right now it looks like it might be an unintentional regression in the test.
Re: [PATCH v3 14/34] fsmonitor--daemon: implement 'start' command
In reply to Jeff HostetlerQuoted 105 lines · Jeff Hostetler
Quoted 97 lines · Ævar Arnfjörð Bjarmason
Quoted 95 lines · Jeff Hostetler
My response here is in addition to Dscho's remarks on this topic. He makes excellent points that I'll just #include here. I do want to add my own $0.02 here.
Quoted 74 lines · Ævar Arnfjörð Bjarmason
Quoted 59 lines · Jeff Hostetler via GitGitGadget
(quoted diff) +59
-0+/* + * This is adapted from `wait_or_whine()`. Watch the child process and + * let it get started and begin listening for requests on the socket + * before reporting our success. + */ +static int wait_for_background_startup(pid_t pid_child) +{ + int status; + pid_t pid_seen; + enum ipc_active_state s; + time_t time_limit, now; + + time(&time_limit); + time_limit += fsmonitor__start_timeout_sec; + + for (;;) { + pid_seen = waitpid(pid_child, &status, WNOHANG); + + if (pid_seen == -1) + return error_errno(_("waitpid failed")); + else if (pid_seen == 0) { + /* + * The child is still running (this should be + * the normal case). Try to connect to it on + * the socket and see if it is ready for + * business. + * + * If there is another daemon already running, + * our child will fail to start (possibly + * after a timeout on the lock), but we don't + * care (who responds) if the socket is live. + */ + s = fsmonitor_ipc__get_state(); + if (s == IPC_STATE__LISTENING) + return 0; + + time(&now); + if (now > time_limit) + return error(_("fsmonitor--daemon not online yet")); + } else if (pid_seen == pid_child) { + /* + * The new child daemon process shutdown while + * it was starting up, so it is not listening + * on the socket. + * + * Try to ping the socket in the odd chance + * that another daemon started (or was already + * running) while our child was starting. + * + * Again, we don't care who services the socket. + */ + s = fsmonitor_ipc__get_state(); + if (s == IPC_STATE__LISTENING) + return 0; + + /* + * We don't care about the WEXITSTATUS() nor + * any of the WIF*(status) values because + * `cmd_fsmonitor__daemon()` does the `!!result`Review
Quoted 10 lines
(quoted diff) +10
-0+ * trick on all function return values. + * + * So it is sufficient to just report the + * early shutdown as an error. + */ + return error(_("fsmonitor--daemon failed to start")); + } else + return error(_("waitpid is confused")); + } +}Ditto this. could we extend the
wait_or_whine()function (or some extended version thereof) to do what you need with callbacks?Quoted 9 lines
It seems the main difference is just being able to pass down a flag for
waitpid(), and the loop needing to check EINTR or not depending on whether WNOHANG is passed.
For e.g. the "We don't care about theWEXITSTATUS()" you'd get that behavior with an adjustedwait_or_whine(). Wouldn't it be better to report what exit status it exits with e.g. if the top-level process is signalled? We do so in trace2 for other things we spawn...Again, I don't want to mix my usage here with the existing code and destabilize all existing callers. Here we are spinning to give the child a chance to *start* and confirm that it is in a listening state and ready for connections. We do not wait for the child to exit (unless it dies quickly without becoming ready).
Quoted 6 lines
Quoted 2 lines
We want to end our wait as soon as we confirm that the child is ready and return. All I really need from the system is
waitpid().Will this code behave correctly if the daemon we start is signalled per
theWIFSIGNALED()cases the code this is derived handles, but this does not?
Quoted 7 lines
We're only waiting until the child gets started and is able to receive requests -- what happens to it after we have confirmed that it is ready is not our concern (after all, the parent is about to exit anyway and the child is going to continue on).
If
waitpid()gives us a WIFSIGNALED (or any other WIF*() state) before we have spoken to it, we will return a "failed to start".
So in wait_or_whine() and finish_command() we capture all of that in trace2 logs, we explicitly don't want that in this case? We do concern ourselves with the exact exit status/signal status etc. of children we start in most other scenarios for trace2 logging purposes.
Quoted 19 lines
But again, that signal would have to arrive immediately after we spawned it and *before* we could talk to it. If the child is signaled after we confirmed it was ready, we don't care because the parent process will be gone.
(If the child is signaled or is killed (or crashes or whatever), the next Git command (like "status") that tries to talk to it will re-start it implicitly -- the
git fsmonitor--daemon startcommand here is an explicit start.)Quoted 7 lines
But sure, I just meant to point out that the flip side to "destabilize
all existing callers" is reviewing new code that may be subtly buggy, and those subtle bugs (if any) would be smoked out if we were forced to extendrun-command.c, i.e. to use whatever feature(s) this needs for all existing callers.
Quoted 3 lines
That would/could have a massive footprint. And I've already established that my usage here is sufficiently different from existing uses that the result would be a mess. IMHO.
I hadn't see this before but it seems pretty much exactly the same code was already added (by you) in 36a7eb68760 (t0052: add simple-ipc tests and t/helper/test-simple-ipc tool, 2021-03-22), perhaps splitting it into a utility function for the two to use with a callback mechanism would reduce the footprint?
What I was suggesting was some continuation of the below.
(I stopped once I noticed the changes I was making to builtin/fsmonitor--daemon.c didn't even compile (almost the entire file is hidden behind a macro), but I've commented on that aspect elsewhere. I.e. it's nice to be able to do tree-wide refactoring without tripping over code hidden by ifdefs)
It passes all current tests for whatever that's worth, obviously not a pretty API, and I'm not claiming it's correct.
But I think it's clear how the trace2/error handling part of it could be further extracted into some utility, so this would just be a mode of run-command.
Not saying you need to do it, but the comments about us explicitly not caring at all about the exit state make me wonder if there's some reason for why someone else would be tripping over some landmine if they did that refactoring.
Anyway, just a thought. I see from other feedback that you seem to be getting pretty exasperated with me.
I'm just trying to help this along, usually being able to piggy-back on existing in-tree code and proving the correctness by passing all in-tree tests with that piggy-backing helps more than hinders.
builtin/fsmonitor--daemon.c +6 -5
@@ -8,6 +8,7 @@#include "simple-ipc.h" #include "khash.h" #include "pkt-line.h" +#include "run-command.h" static const char *const builtin_fsmonitor__daemon_usage[] = {N_ ("git fsmonitor--daemon start [<options>]" ),
@@ -1403,11 +1404,12 @@ static int wait_for_background_startup(pid_t pid_child) time_limit += fsmonitor__start_timeout_sec;for (;;) { +int saved_errno =0 ; + code =wait_or_whine_extended (pid_child, &pid_seen,"TODO" , +0 ,WNOHANG , &saved_errno); pid_seen =waitpid (pid_child, &status,WNOHANG ); -if (pid_seen ==-1 ) -return error_errno (_ ("waitpid failed" )); -else if (pid_seen ==0 ) { +if (pid_seen ==0 ) {/* * The child is still running (this should be * the normal case). Try to connect to it on
@@ -1452,8 +1454,7 @@ static int wait_for_background_startup(pid_t pid_child)* early shutdown as an error. */ return error (_ ("fsmonitor--daemon failed to start" )); - }else -return error (_ ("waitpid is confused" )); + } } }
run-command.c +22 -9
@@ -543,24 +543,28 @@ static inline void set_cloexec(int fd)fcntl (fd,F_SETFD , flags |FD_CLOEXEC ); } -static int wait_or_whine (pid_t pid,const char *argv0,int in_signal) +int wait_or_whine_extended (pid_t pid,pid_t *waiting,const char *argv0, +int in_signal,int waitpid_options,int *failed_errno) {int status, code =-1 ; -pid_t waiting; -int failed_errno =0 ; -while ((waiting =waitpid (pid, &status,0 )) <0 && errno ==EINTR ) +if (waitpid_options &WNOHANG ) + *waiting =waitpid (pid, &status, waitpid_options); +else +while ((*waiting =waitpid (pid, &status, waitpid_options)) <0 && + errno ==EINTR ) ;/* nothing */ +if (in_signal) {if (WIFEXITED (status)) code =WEXITSTATUS (status);return code; } -if (waiting <0 ) { - failed_errno = errno; +if (*waiting <0 ) { + *failed_errno = errno;error_errno ("waitpid for %s failed" , argv0); - }else if (waiting != pid) { + }else if (*waiting != pid) {error ("waitpid is confused (%s)" , argv0); }else if (WIFSIGNALED (status)) { code =WTERMSIG (status);
@@ -574,14 +578,23 @@ static int wait_or_whine(pid_t pid, const char *argv0, int in_signal) code +=128 ; }else if (WIFEXITED (status)) { code =WEXITSTATUS (status); + }else if (waitpid_options &WNOHANG && *waiting ==0 ) { + code =0 ; }else {error ("waitpid is confused (%s)" , argv0); } -clear_child_for_cleanup (pid); +return code; +} +static int wait_or_whine (pid_t pid,const char *argv0,int in_signal) +{ +pid_t ignore; +int failed_errno =0 ; +int ret =wait_or_whine_extended (pid, &ignore, argv0, in_signal,0 , &failed_errno); +clear_child_for_cleanup (pid); errno = failed_errno; -return code; +return ret; }static void trace_add_env (struct strbuf *dst,const char *const *deltaenv)
run-command.h +3 -0
@@ -182,6 +182,9 @@ void child_process_clear(struct child_process *);int is_executable (const char *name); +int wait_or_whine_extended (pid_t pid,pid_t *waiting,const char *argv0, +int in_signal,int waitpid_options,int *failed_errno); +/** * Start a sub-process. Takes a pointer to a `struct child_process` * that specifies the details and returns pipe FDs (if requested).
t/helper/test-simple-ipc.c +9 -13
@@ -9,6 +9,7 @@#include "parse-options.h" #include "thread-utils.h" #include "strvec.h" +#include "run-command.h" #ifndef SUPPORTS_SIMPLE_IPC int cmd__simple_ipc (int argc,const char **argv)
@@ -349,7 +350,7 @@ static int spawn_server(pid_t *pid)*/ static int wait_for_server_startup (pid_t pid_child) { -int status; +int code;pid_t pid_seen;enum ipc_active_state s;time_t time_limit, now;
@@ -358,12 +359,10 @@ static int wait_for_server_startup(pid_t pid_child) time_limit += cl_args.max_wait_sec;for (;;) { - pid_seen =waitpid (pid_child, &status,WNOHANG ); - -if (pid_seen ==-1 ) -return error_errno (_ ("waitpid failed" )); - -else if (pid_seen ==0 ) { +int saved_errno =0 ; + code =wait_or_whine_extended (pid_child, &pid_seen,"TODO" , +0 ,WNOHANG , &saved_errno); +if (pid_seen ==0 ) {/* * The child is still running (this should be * the normal case). Try to connect to it on
@@ -384,9 +383,7 @@ static int wait_for_server_startup(pid_t pid_child)return error (_ ("daemon not online yet" ));continue ; - } - -else if (pid_seen == pid_child) { + }else if (pid_seen == pid_child) {/* * The new child daemon process shutdown while * it was starting up, so it is not listening
@@ -412,10 +409,9 @@ static int wait_for_server_startup(pid_t pid_child)* early shutdown as an error. */ return error (_ ("daemon failed to start" )); + }else if (code) { +BUG ("??" ); } - -else -return error (_ ("waitpid is confused" )); } }
Re: [PATCH v3 11/34] fsmonitor-fs-listen-win32: stub in backend for Windows
In reply to Jeff HostetlerQuoted 68 lines · Jeff Hostetler
Quoted 54 lines · Ævar Arnfjörð Bjarmason
Quoted 43 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Stub in empty backend for fsmonitor--daemon on Windows.
Signed-off-by Jeff Hostetler <[email protected]>--- Makefile | 13 ++++++ compat/fsmonitor/fsmonitor-fs-listen-win32.c | 21 +++++++++ compat/fsmonitor/fsmonitor-fs-listen.h | 49 ++++++++++++++++++++ config.mak.uname | 2 + contrib/buildsystems/CMakeLists.txt | 5 ++ 5 files changed, 90 insertions(+)create mode 100644
compat/fsmonitor/fsmonitor-fs-listen-win32.ccreate mode 100644compat/fsmonitor/fsmonitor-fs-listen.hMakefile +9
-0@@ -467,6 +467,11 @@ all:: # directory, and the JSON compilation database 'compile_commands.json' will be # created at the root of the repository. # +# If your platform supports a built-in fsmonitor backend, set +# FSMONITOR_DAEMON_BACKEND to the "<name>" of the corresponding +# `compat/fsmonitor/fsmonitor-fs-listen-<name>.c` that implements the +# `fsmonitor_fs_listen__*()` routines. +# # Define DEVELOPER to enable more compiler warnings. Compiler version # and family are auto detected, but could be overridden by defining # COMPILER_FEATURES (see config.mak.dev). You can still set@@ -1929,6 +1934,11 @@ ifdef NEED_ACCESS_ROOT_HANDLER COMPAT_OBJS += compat/access.o endif +ifdef FSMONITOR_DAEMON_BACKEND + COMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND + COMPAT_OBJS += compat/fsmonitor/fsmonitor-fs-listen-$(FSMONITOR_DAEMON_BACKEND).o +endif + ifeq ($(TCLTK_PATH),) NO_TCLTK = NoThanks endifMakefile GIT-BUILD-OPTIONS: FORCE · line 2808
Quoted 6 lines
Makefile +3
-0@@ -2793,6 +2803,9 @@ GIT-BUILD-OPTIONS: FORCE @echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+ @echo DC_SHA1=\''$(subst ','\'',$(subst ','\'',$(DC_SHA1)))'\' >>$@+ @echo X=\'$(X)\' >>$@+ +ifdef FSMONITOR_DAEMON_BACKEND + @echo FSMONITOR_DAEMON_BACKEND=\''$(subst ','\'',$(subst ','\'',$(FSMONITOR_DAEMON_BACKEND)))'\' >>$@+ +endifWhy put this in an ifdef?
In342e9ef2d9e(Introduce a performance testing framework, 2012-02-17)Quoted 9 lines
we started doing that for some perf/test options (which b.t.w., I don't really see the reason for, maybe it's some subtlety in how
test-lib.shpicks those up).
But for all the other compile-time stuff we don't ifdef it, we just define it, and then you get an empty value or not.
This would AFAICT be the first build-time-for-the-C-program option we
ifdef for writing a line to GIT-BUILD-OPTIONS.(I'm going to respond here on the original question rather than on any of the follow up responses in an attempt at diffusing things a bit.)
Quoted 8 lines
I added the ifdef because I thought it to be the *most conservative* thing that I could do. The output of the generated file on unsupported platforms should be *identical* to what it was before my changes. I only alter the contents of the generated file on supported platforms.
Later, when the generated file is consumed, we don't need to worry about the effect (if any) on incremental compiles -- we will know that it won't be set -- just like it was not set in the original compile.
Okey, so e.g. when we added e.g. USE_LIBPCRE2 we added it TO GIT-BUILD-OPTIONS unconditionally, so if you pulled that commit you'd trigger a rebuild on anything that cares about GIT-BUILD-OPTIONS (which is almost everything).
But you'd like to have the line not added to avoid that one-off recompile....
Quoted 29 lines
That change appears right before a 12 other ifdef'd symbols also being written to that generated file. Most are test and perf, but some are not. But my point is that the pattern is present already.
The original question also references a 9.5 year old commit which uses the same pattern as I've used here. It also muddies the water on why it was/wasn't needed back then. And hints at possible side-effects in some of our test scripts. So it is clear that the confusion/disagreements that we are having with the current patch and whether or not to ifdef are not new.
So, is there value in being explicit and having the ifdef ??
There are well defined Make rules (and Junio gave us a very elegant little script to demonstrate that), but the subtleties are there. Especially with our use generated files like
GIT-BUILD-OPTIONS. We have a mailing list full of experts and yet this question received a lot more discussion than I thought possible or necessary, but it took a test script to demonstrate that the results are the same and it doesn't matter. Perhaps the clarity is worth it for the price of a simple ifdef.So, how much time have we (collectively) wasted discussing this subtlety ??
Quoted 7 lines
To summarize, I added the ifdef to make it explicitly clear that I'm not altering behavior on unsupported platforms. I can remove it from V4 if desired or I can keep it. (We all now know that it doesn't functionally matter -- it does however, provide clarity.)
Sorry if this sounded like a rant,
...I asked because I've looked at that ifdef soup around GIT-BUILD-OPTIONS and wondered if I could make it go away, and before a patch lands is a good time to ask "what's this pattern for?", as opposed to inferring this after the fact.
For me it was just a minor curiosity, I didn't expect to start this big discussion about it. I expected just a "oh, I just copy/pasted that from the lines at the end" or something, which would be fair enough.
I really don't care which one we go for here. If you want to change it fine, if not that's fine too.
I have noticed a pattern where you seem to really carefully consider why you'd like X over Y. I.e. it wasn't just copy/pasting in this case if I understand you correctly, but a carefully thought out decision to not do it like the other C-level-GIT-BUILD-OPTIONS.
Okey, fair enough, but that decision then doesn't go into the commit message, and then when I innocently ask about it...
..I guess I'll stop before this starts resembling a rant on my part :)
Anyway, I have also had really non-trivial comments on this fsmonitor series, not just a few bikeshed comments. I.e. the un-addressed question about the wildly different performance numbers we seem to have seen in our respective testing:
https://lore.kernel.org/git/[email protected]
I think that's much more interesting than this relatively light-reading bikeshedding I had while giving this a read-through.
Re: [PATCH v3 29/34] fsmonitor--daemon: use a cookie file to sync with file system
In reply to Ævar Arnfjörð BjarmasonQuoted 32 lines · Ævar Arnfjörð Bjarmason
Quoted 10 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Teach fsmonitor--daemon client threads to create a cookie file inside the .git directory and then wait until FS events for the cookie are observed by the FS listener thread.
This helps address the racy nature of file system events by blocking the client response until the kernel has drained any event backlog.
Quoted 8 lines
This is especially important on MacOS where kernel events are only issued with a limited frequency. See the
latencyargument ofFSeventStreamCreate(). The kernel only signals everylatencyseconds, but does not guarantee that the kernel queue is completely drained, so we may have to wait more than one interval. If we increase the frequency, the system is more likely to drop events. We avoid these issues by having each client thread create a unique cookie file and then wait until it is seen in the event stream.Is this a guaranteed property of any API fsmonitor might need to work with (linux, darwin, Windows) that if I perform a bunch of FS operations on my working tree, that if I finish up by touching this cookie file that that'll happen last?
I'd think that wouldn't be the case, i.e. on POSIX filesystems unless you run around fsyncing both files and directories you're not guaranteed that they're on disk, and even then the kernel might decide to sync your cookie earlier, won't it?
Quoted 8 lines
E.g. on Linux you can even have cross-FS watches, and mix & match different FS types. I'd expect to get events in whatever implementation-defined order the VFS layer + FS decided to sync them to disk in & get to firing off an event for me.
Or do these APIs all guarantee that a linear view of the world is presented to the API consumer?
Theoretically, none of these APIs guarantee a complete linear ordering. We receive events from the FS in the order that the FS decides to perform the actual IO. And the inner workings of the FS is private. Even if we directly read the journal rather than listening for notifications, we probably still don't know whether the FS reordered the queue of things heading to disk.
However in practice, the events for the cookie files do tend to arrive in order. And the net effect is that the worker thread in the daemon is sync'd up with IO activity that was initiated before the request.
BTW Watchman also uses cookie files for this same reason.
It should also be noted that some operations are just racy. If you're doing a bunch of IO in one window and a 'git status' in another window, your result will be racy -- status (without FSM) makes 2 passes on the
disk: the first to verify mtimes on items in the index and the second to look for untracked files. the status result may be "blurry" (for lack of a better word). So the same questions
"does the FS reorder my IO?",
"did status see the fully sync'd FS?",
and etc can also be asked in the normal (non FSM) case, right?
So it may be the case that having an fsmonitor (mine, Watchman, etc) and the untracked-cache, we'll have less skew in status results because the status process shouldn't have to do any scanning. But I'm not sure I want to make that assertion yet.
Thanks,
Jeff
Re: [PATCH v3 12/34] fsmonitor-fs-listen-macos: stub in backend for MacOS
In reply to Ævar Arnfjörð BjarmasonHi Ævar,
Quoted 6 lines · Ævar Arnfjörð Bjarmason
Quoted 3 lines · Johannes Schindelin
So you suggest that we name the new stuff after an
unamethat reflects a name that is no longer relevant? I haven't seen a real Darwin system in quite a long time, have you?
Quoted 7 lines
It's not current? On an Mac Mini M1 which got released this year:
% uname -s
DarwinWe then have the same in config.mak.uname, it seemed the most obvious and consistent to carry that through to file inclusion.
Sorry. I assumed that you knew that Darwin was the name for an open source Operating System. See
https://en.wikipedia.org/wiki/Darwin_%28operating_system%29 for more details.
Ciao,
Johannes
Re: [PATCH v3 12/34] fsmonitor-fs-listen-macos: stub in backend for MacOS
Quoted 18 lines · Johannes Schindelin
Hi Ævar,
Quoted 6 lines · Ævar Arnfjörð Bjarmason
Quoted 3 lines · Johannes Schindelin
So you suggest that we name the new stuff after an
unamethat reflects a name that is no longer relevant? I haven't seen a real Darwin system in quite a long time, have you?Quoted 7 lines
It's not current? On an Mac Mini M1 which got released this year:
% uname -s
DarwinWe then have the same in config.mak.uname, it seemed the most obvious and consistent to carry that through to file inclusion.
Quoted 7 lines
Sorry. I assumed that you knew that Darwin was the name for an open source Operating System. See
https://en.wikipedia.org/wiki/Darwin_%28operating_system%29 for more details.Ciao,
Johannes
Sorry, but I do not see that you are being more constructive than the other party, whom you blame to be not constructive, in this exchange.
The part of the file that the patch applies to uses $(uname_S) to implement platform specific special cases, and we are looking at
ifeq ($(uname_S),Darwin) ... FSMONITOR_DAEMON_BACKEND = macos ... endif
I find it a fair question why the name used there has to be different from the one we can automatically and mechanically get out of "uname -s".
Then you respond that uname output is no longer relevant because Darwin is a name that is no longer relevant? And when asked why the name is no longer relevant, you make a sniding comment implying that the other party does not know the name is an operating system?
What is going on here?
It does not really matter how "Darwin" is described in an encyclopedia in the context of this discussion. What matters is that it is what the system's "uname -s" currently uses to identify itself, and what we guard the section of makefile snippet with, isn't it?
ci/lib.sh seems to have an attempt to unify/translate among these names, and
* on azure-pipelines, it wants to translate darwin to osx
* on github-actions, it wants to translate macos to osx
Presumably that is because these two systems call the platform with these two different names, and you want to pick a middle ground that nobody uses to be neutral, or something?
Also, in contrib/vscode/init.sh, I see Darwin obtained from "uname -s" gets translated to "macOS".
In any case, if your argument was "we picked macos because we use the same token elsewhere, while trying to translate away from Darwin as much as possible for such and such reasons", I would have found it a productive exchange, but unfortunately that is not what I am seeing here.
Re: [PATCH v3 12/34] fsmonitor-fs-listen-macos: stub in backend for MacOS
Quoted 8 lines · Junio C Hamano
Quoted 6 lines · Johannes Schindelin
Hi Ævar,
Quoted 2 lines · Ævar Arnfjörð Bjarmason
On Fri, Jul 16 2021, Johannes Schindelin wrote:
...
I'm not sure that there is a "correct" answer here, but for the sake of harmony, in V4 I'll set this to "darwin" and update the name of the backend driver source file to match. So that we are consistently using 1 term throughout "Makefile" and "config.mak.uname".
ifeq ($(uname_S),Darwin) ... FSMONITOR_DAEMON_BACKEND = darwin endif
FWIW, I suspect that it is not worth the effort to directly set the backend name from $(uname_S). For example, on Windows we currently have two different uname values depending on which compiler is being used.
ifeq ($(uname_S),Windows) ... FSMONITOR_DAEMON_BACKEND = win32 endif ifneq (,$(findstring MINGW,$(uname_S))) ... FSMONITOR_DAEMON_BACKEND = win32 endif
Also, since the backend layer is highly platform-specific, it may be a while (if ever) before we have universal coverage for all platforms. Until then, we can simply set $FSMONITOR_DAEMON_BACKEND to a literal value on a platform-by-platform basis as support is added.
Thanks,
Jeff
Re: [PATCH v3 12/34] fsmonitor-fs-listen-macos: stub in backend for MacOS
Quoted 39 lines · Jeff Hostetler
Quoted 8 lines · Junio C Hamano
Quoted 6 lines · Johannes Schindelin
Hi Ævar,
Quoted 2 lines · Ævar Arnfjörð Bjarmason
On Fri, Jul 16 2021, Johannes Schindelin wrote:
...
I'm not sure that there is a "correct" answer here, but for the sake of harmony, in V4 I'll set this to "darwin" and update the name of the backend driver source file to match. So that we are consistently using 1 term throughout "
Makefile" and "config.mak.uname".ifeq ($(uname_S),Darwin) ... FSMONITOR_DAEMON_BACKEND = darwin endifFWIW, I suspect that it is not worth the effort to directly set the backend name from $(uname_S). For example, on Windows we currently have two different uname values depending on which compiler is being used.
ifeq ($(uname_S),Windows) ... FSMONITOR_DAEMON_BACKEND = win32 endif ifneq (,$(findstring MINGW,$(uname_S))) ... FSMONITOR_DAEMON_BACKEND = win32 endif
Quoted 4 lines
Also, since the backend layer is highly platform-specific, it may be a while (if ever) before we have universal coverage for all platforms. Until then, we can simply set $FSMONITOR_DAEMON_BACKEND to a literal value on a platform-by-platform basis as support is added.
Re "harmony": For what it's worth I don't think you should change it on my accord.
I should probably have more explicitly said (but I've also been trying to check the general verbosity of my E-Mails), that when I read a series like this and have some general trivial comments like this, I mean them as something like:
Just a thought while reading this through, i.e. a person familiar with the general codebase but not necessarily your specific are. Maybe this suggestion makes things easier/simpler, but if you think not and decide not to take the suggestion that's fine too.
I.e. that along with the general implicit suggestion that I'd say applies in general on list that if someone is perplexed by a patch by default that's a comment on the commit message.
That person (i.e. me in this case) could also just be hopelessly confused & nothing needs to change. When I get comments like that I sometimes change things, sometimes not. You should do the same.
As noted in another reply on this general thread & what's cooking I seem to have poked a bit of a hornet's nest here that I wasn't expecting to poke. I'd not been following earlier rounds of this topic, and didn't know that it had (seemingly) reached some phase of critical updates only in the minds of its authors.
Re: [PATCH v3 12/34] fsmonitor-fs-listen-macos: stub in backend for MacOS
In reply to Jeff HostetlerQuoted 10 lines · Jeff Hostetler
I'm not sure that there is a "correct" answer here, but for the sake of harmony, in V4 I'll set this to "darwin" and update the name of the backend driver source file to match. So that we are consistently using 1 term throughout "
Makefile" and "config.mak.uname".ifeq ($(uname_S),Darwin) ... FSMONITOR_DAEMON_BACKEND = darwin endif...
I do not think it would help "harmony" to change the name, but any one of 'darwin', 'macos' and 'osx' would be fine.
It was irritating to see a simple "why is this particular name chosen?" answered with such a hostility, when even a "we have no deep reasoning behind the choice of the name. It is only seen by developers in names of the source files, and it does not make much difference" would have been sufficient. I somehow view it more problematic.
I guess the blame goes both ways, though. We all have worked with each other long enough to know which of your recipients are prone to go overly defensive when asked questions, and we should know that it would help to prepend "I am just being curious but..." to your questions whose answers do not make a huge difference at the end either way (or not asking such questions at all).
Thanks.
Re: [PATCH v3 12/34] fsmonitor-fs-listen-macos: stub in backend for MacOS
Quoted 6 lines · Junio C Hamano
I guess the blame goes both ways, though. We all have worked with each other long enough to know which of your recipients are prone to go overly defensive when asked questions, and we should know that it would help to prepend "I am just being curious but..." to your questions whose answers do not make a huge difference at the end either way (or not asking such questions at all).
I disagree. It's not OK for contributors to react defensively when asked questions, and in particular I don't think it's OK that some contributors are punished for merely disagreeing, while others are given a pass for snarling at feedback. This is double standards.
The Git project should not play favorites, and all contributors should be asked to assume good faith.
https://en.wikipedia.org/wiki/Wikipedia:Assume_good_faith
It should be implied that the feedback given is to try to improve the project, it should not need to be stated.
Signature · 1 line
Felipe Contreras