312
batch_src->interned_paths[k];
313
}
314
315
+/*
316
+ * To keep the batch list from growing unbounded in response to filesystem
317
+ * activity, we try to truncate old batches from the end of the list as
318
+ * they become irrelevant.
319
+ *
320
+ * We assume that the .git/index will be updated with the most recent token
321
+ * any time the index is updated. And future commands will only ask for
322
+ * recent changes *since* that new token. So as tokens advance into the
323
+ * future, older batch items will never be requested/needed. So we can
324
+ * truncate them without loss of functionality.
325
+ *
326
+ * However, multiple commands may be talking to the daemon concurrently
327
+ * or perform a slow command, so a little "token skew" is possible.
328
+ * Therefore, we want this to be a little bit lazy and have a generous
329
+ * delay.
330
+ *
331
+ * The current reader thread walked backwards in time from `token->batch_head`
332
+ * back to `batch_marker` somewhere in the middle of the batch list.
333
+ *
334
+ * Let's walk backwards in time from that marker an arbitrary delay
335
+ * and truncate the list there. Note that these timestamps are completely
336
+ * artificial (based on when we pinned the batch item) and not on any
337
+ * filesystem activity.
338
+ *
339
+ * Return the obsolete portion of the list after we have removed it from
340
+ * the official list so that the caller can free it after leaving the lock.
341
+ */
342
+#define MY_TIME_DELAY_SECONDS (5 * 60) /* seconds */
343
+
344
+static struct fsmonitor_batch *with_lock__truncate_old_batches(
345
+ struct fsmonitor_daemon_state *state,
346
+ const struct fsmonitor_batch *batch_marker)
347
+{
348
+ /* assert current thread holding state->main_lock */
349
+
350
+ const struct fsmonitor_batch *batch;
351
+ struct fsmonitor_batch *remainder;
352
+
353
+ if (!batch_marker)
354
+ return NULL;
355
+
356
+ trace_printf_key(&trace_fsmonitor, "Truncate: mark (%"PRIu64",%"PRIu64")",
357
+ batch_marker->batch_seq_nr,
358
+ (uint64_t)batch_marker->pinned_time);
359
+
360
+ for (batch = batch_marker; batch; batch = batch->next) {
361
+ time_t t;
362
+
363
+ if (!batch->pinned_time) /* an overflow batch */
364
+ continue;
365
+
366
+ t = batch->pinned_time + MY_TIME_DELAY_SECONDS;
367
+ if (t > batch_marker->pinned_time) /* too close to marker */
368
+ continue;
369
+
370
+ goto truncate_past_here;
371
+ }
372
+
373
+ return NULL;
374
+
375
+truncate_past_here:
376
+ state->current_token_data->batch_tail = (struct fsmonitor_batch *)batch;
377
+
378
+ remainder = ((struct fsmonitor_batch *)batch)->next;
379
+ ((struct fsmonitor_batch *)batch)->next = NULL;
380
+
381
+ return remainder;
382
+}
383
+
384
static void fsmonitor_free_token_data(struct fsmonitor_token_data *token)
385
{
386
if (!token)
494
const char *p;
495
const struct fsmonitor_batch *batch_head;
496
const struct fsmonitor_batch *batch;
497
+ struct fsmonitor_batch *remainder = NULL;
498
intmax_t count = 0, duplicates = 0;
499
kh_str_t *shown;
500
int hash_ret;
722
* that work.
723
*/
724
fsmonitor_free_token_data(token_data);
725
+ } else if (batch) {
726
+ /*
727
+ * We are holding the lock and are the only
728
+ * reader of the ref-counted portion of the
729
+ * list, so we get the honor of seeing if the
730
+ * list can be truncated to save memory.
731
+ *
732
+ * The main loop did not walk to the end of the
733
+ * list, so this batch is the first item in the
734
+ * batch-list that is older than the requested
735
+ * end-point sequence number. See if the tail
736
+ * end of the list is obsolete.
737
+ */
738
+ remainder = with_lock__truncate_old_batches(state,
739
+ batch);
740
}
741
}
742
743
pthread_mutex_unlock(&state->main_lock);
744
745
+ if (remainder)
746
+ fsmonitor_batch__free_list(remainder);
747
+
748
trace2_data_intmax("fsmonitor", the_repository, "response/length", total_response_len);
749
trace2_data_intmax("fsmonitor", the_repository, "response/count/files", count);
750
trace2_data_intmax("fsmonitor", the_repository, "response/count/duplicates", duplicates);