Thread (3 messages) 3 messages, 2 authors, 2017-06-27

Re: [PATCH v7 6/6] convert: add "status=delayed" to filter process protocol

From: Lars Schneider <hidden>
Date: 2017-06-27 20:35:14

On 27 Jun 2017, at 21:00, Junio C Hamano [off-list ref] wrote:

Lars Schneider [off-list ref] writes:
quoted
@@ -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.

...
quoted
+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.

quoted
+	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?

quoted
+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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help