> On 27 Jun 2017, at 21:00, Junio C Hamano <gitster@xxxxxxxxx> wrote: > > Lars Schneider <larsxschneider@xxxxxxxxx> writes: > >> @@ -533,7 +534,8 @@ static int start_multi_file_filter_fn(struct subprocess_entry *subprocess) >> if (err) >> goto done; >> >> - err = packet_writel(process->in, "capability=clean", "capability=smudge", NULL); >> + err = packet_writel(process->in, >> + "capability=clean", "capability=smudge", "capability=delay", NULL); >> >> for (;;) { >> cap_buf = packet_read_line(process->out, NULL); >> @@ -549,6 +551,8 @@ static int start_multi_file_filter_fn(struct subprocess_entry *subprocess) >> entry->supported_capabilities |= CAP_CLEAN; >> } else if (!strcmp(cap_name, "smudge")) { >> entry->supported_capabilities |= CAP_SMUDGE; >> + } else if (!strcmp(cap_name, "delay")) { >> + entry->supported_capabilities |= CAP_DELAY; >> } else { >> warning( >> "external filter '%s' requested unsupported filter capability '%s'", > > I thought you said something about attempting to make this more > table-driven; did the attempt produce a better result? Just being > curious. I treated that as low-prio as I got the impression that you are generally OK with the current implementation. I promise to send a patch with this change. Either as part of this series or as a follow up patch. > ... > >> +struct delayed_checkout { >> + /* State of the currently processed cache entry. If the state is >> + * CE_CAN_DELAY, then the filter can change the 'is_delayed' flag >> + * to signal that the current cache entry was delayed. If the state is >> + * CE_RETRY, then this signals the filter that the cache entry was >> + * requested before. >> + */ > > /* > * Our multi-line comment look like this; slash-aster > * and aster-slash that opens and closes the block are > * on their own lines. > */ Oops. Lesson learned. >> + enum ce_delay_state state; >> + int is_delayed; > > Hmph, I do not terribly mind but is this thing really needed? > > Wouldn't filters and paths being non-empty be a good enough sign > that the backend said "ok, I am allowed to give a delayed response > so I acknowledge this path but would not give a result right away"? Yes. I guess that was a premature optimization on my end. This is how "write_entry()" in entry.c would change: - dco->is_delayed = 0; ret = async_convert_to_working_tree( ce->name, new, size, &buf, dco); - if (ret && dco->is_delayed) { + if (ret && string_list_has_string(&dco->paths, ce->name)) { free(new); goto finish; } OK? >> +int finish_delayed_checkout(struct checkout *state) >> +{ >> + int errs = 0; >> + struct string_list_item *filter, *path; >> + struct delayed_checkout *dco = state->delayed_checkout; >> + >> + if (!state->delayed_checkout) >> + return errs; >> + >> + dco->state = CE_RETRY; >> + while (dco->filters.nr > 0) { >> + for_each_string_list_item(filter, &dco->filters) { >> + struct string_list available_paths = STRING_LIST_INIT_NODUP; >> + >> + if (!async_query_available_blobs(filter->string, &available_paths)) { >> + /* Filter reported an error */ >> + errs = 1; >> + filter->string = ""; >> + continue; >> + } >> + if (available_paths.nr <= 0) { >> + /* Filter responded with no entries. That means >> + * the filter is done and we can remove the >> + * filter from the list (see >> + * "string_list_remove_empty_items" call below). >> + */ >> + filter->string = ""; >> + continue; >> + } >> + >> + /* In dco->paths we store a list of all delayed paths. >> + * The filter just send us a list of available paths. >> + * Remove them from the list. >> + */ >> + filter_string_list(&dco->paths, 0, >> + &remove_available_paths, &available_paths); >> + >> + for_each_string_list_item(path, &available_paths) { >> + struct cache_entry* ce; >> + >> + if (!path->util) { >> + error("external filter '%s' signaled that '%s' " >> + "is now available although it has not been " >> + "delayed earlier", >> + filter->string, path->string); >> + errs |= 1; >> + >> + /* Do not ask the filter for available blobs, >> + * again, as the filter is likely buggy. >> + */ >> + filter->string = ""; >> + continue; >> + } >> + ce = index_file_exists(state->istate, path->string, >> + strlen(path->string), 0); >> + assert(dco->state == CE_RETRY); > > Can anything futz with dco->state at this late in the game? You > entered into CE_RETRY state at the beginning of this function, and > this loop is going through each delayed ones. At this point, you are > going to make , but not yet have made, a request to the backend via > another call to checkout_entry() again. > > Just wondering what kind of programming errors you are protecting > yourself against. I briefly wondered perhaps you are afraid of a > bug in checkout_entry() that may flip the state back, but it that > is the case then the assert() would be after checkout_entry(). At this point I want to ensure that checkout_entry() is called with the CE_RETRY state. Because checkout_entry() needs to pass that flag to the convert machinery. This assert was useful when checkout_entry() changed dco->state between CAN_DELAY and DELAYED. In the current implementation the state should not be changed anymore. Would you prefer if I remove it? (with or without is both fine with me) Thanks, Lars