fsmonitor--daemon: define token-ids

Teach fsmonitor--daemon to create token-ids and define the overall token naming scheme. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff Hostetler committed Mar 25, 2022 at 18:02 UTC aeef767a4155f55233338928f705a3e9986fe2bf
1 file changed +115 -1
builtin/fsmonitor--daemon.c
+115 -1
@@ -106,6 +106,120 @@ static int do_as_client__status(void)
106 }
107 }
108
109 +/*
110 + * Requests to and from a FSMonitor Protocol V2 provider use an opaque
111 + * "token" as a virtual timestamp. Clients can request a summary of all
112 + * created/deleted/modified files relative to a token. In the response,
113 + * clients receive a new token for the next (relative) request.
114 + *
115 + *
116 + * Token Format
117 + * ============
118 + *
119 + * The contents of the token are private and provider-specific.
120 + *
121 + * For the built-in fsmonitor--daemon, we define a token as follows:
122 + *
123 + * "builtin" ":" <token_id> ":" <sequence_nr>
124 + *
125 + * The "builtin" prefix is used as a namespace to avoid conflicts
126 + * with other providers (such as Watchman).
127 + *
128 + * The <token_id> is an arbitrary OPAQUE string, such as a GUID,
129 + * UUID, or {timestamp,pid}. It is used to group all filesystem
130 + * events that happened while the daemon was monitoring (and in-sync
131 + * with the filesystem).
132 + *
133 + * Unlike FSMonitor Protocol V1, it is not defined as a timestamp
134 + * and does not define less-than/greater-than relationships.
135 + * (There are too many race conditions to rely on file system
136 + * event timestamps.)
137 + *
138 + * The <sequence_nr> is a simple integer incremented whenever the
139 + * daemon needs to make its state public. For example, if 1000 file
140 + * system events come in, but no clients have requested the data,
141 + * the daemon can continue to accumulate file changes in the same
142 + * bin and does not need to advance the sequence number. However,
143 + * as soon as a client does arrive, the daemon needs to start a new
144 + * bin and increment the sequence number.
145 + *
146 + * The sequence number serves as the boundary between 2 sets
147 + * of bins -- the older ones that the client has already seen
148 + * and the newer ones that it hasn't.
149 + *
150 + * When a new <token_id> is created, the <sequence_nr> is reset to
151 + * zero.
152 + *
153 + *
154 + * About Token Ids
155 + * ===============
156 + *
157 + * A new token_id is created:
158 + *
159 + * [1] each time the daemon is started.
160 + *
161 + * [2] any time that the daemon must re-sync with the filesystem
162 + * (such as when the kernel drops or we miss events on a very
163 + * active volume).
164 + *
165 + * [3] in response to a client "flush" command (for dropped event
166 + * testing).
167 + *
168 + * When a new token_id is created, the daemon is free to discard all
169 + * cached filesystem events associated with any previous token_ids.
170 + * Events associated with a non-current token_id will never be sent
171 + * to a client. A token_id change implicitly means that the daemon
172 + * has gap in its event history.
173 + *
174 + * Therefore, clients that present a token with a stale (non-current)
175 + * token_id will always be given a trivial response.
176 + */
177 +struct fsmonitor_token_data {
178 + struct strbuf token_id;
179 + struct fsmonitor_batch *batch_head;
180 + struct fsmonitor_batch *batch_tail;
181 + uint64_t client_ref_count;
182 +};
183 +
184 +static struct fsmonitor_token_data *fsmonitor_new_token_data(void)
185 +{
186 + static int test_env_value = -1;
187 + static uint64_t flush_count = 0;
188 + struct fsmonitor_token_data *token;
189 +
190 + CALLOC_ARRAY(token, 1);
191 +
192 + strbuf_init(&token->token_id, 0);
193 + token->batch_head = NULL;
194 + token->batch_tail = NULL;
195 + token->client_ref_count = 0;
196 +
197 + if (test_env_value < 0)
198 + test_env_value = git_env_bool("GIT_TEST_FSMONITOR_TOKEN", 0);
199 +
200 + if (!test_env_value) {
201 + struct timeval tv;
202 + struct tm tm;
203 + time_t secs;
204 +
205 + gettimeofday(&tv, NULL);
206 + secs = tv.tv_sec;
207 + gmtime_r(&secs, &tm);
208 +
209 + strbuf_addf(&token->token_id,
210 + "%"PRIu64".%d.%4d%02d%02dT%02d%02d%02d.%06ldZ",
211 + flush_count++,
212 + getpid(),
213 + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
214 + tm.tm_hour, tm.tm_min, tm.tm_sec,
215 + (long)tv.tv_usec);
216 + } else {
217 + strbuf_addf(&token->token_id, "test_%08x", test_env_value++);
218 + }
219 +
220 + return token;
221 +}
222 +
223 static ipc_server_application_cb handle_client;
224
225 static int handle_client(void *data,
@@ -298,7 +412,7 @@ static int fsmonitor_run_daemon(void)
412
413 pthread_mutex_init(&state.main_lock, NULL);
414 state.error_code = 0;
301 - state.current_token_data = NULL;
415 + state.current_token_data = fsmonitor_new_token_data();
416
417 /* Prepare to (recursively) watch the <worktree-root> directory. */
418 strbuf_init(&state.path_worktree_watch, 0);