git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v2 7/7] index-pack: make quantum of work smaller

From
JTJonathan Tan <jonathantanmy@google.com>
Date
Mar 10, 2020, 21:42 UTC
Message-ID
<20200310214206.81606-1-jonathantanmy@google.com>
In-Reply-To
<20200228000459.GE12115@google.com>
Show 32 quoted lines
> > +/*
> > + * Stack of struct base_data that have unprocessed children.
> > + * threaded_second_pass() uses this as a source of work (the other being the
> > + * objects array).
> > + */
> > +LIST_HEAD(work_head);
> > +
> > +/*
> > + * Stack of struct base_data that have children, all of whom have been
> > + * processed or are being processed, and at least one child is being processed.
> > + * These struct base_data must be kept around until the last child is
> > + * processed.
> > + */
> > +LIST_HEAD(done_head);
> > +
> > +/*
> > + * All threads share one delta base cache.
> > + */
> > +size_t base_cache_used;
> > +size_t base_cache_limit;
> > +
> >  struct thread_local {
> >  	pthread_t thread;
> > -	size_t base_cache_used;
> >  	int pack_fd;
> >  };
> 
> Seeing several new shared variables made me paranoid about whether or
> not these were thread-safe. There are already five mutexes in this file,
> but there are no clear descriptions (apart from hints in the lock names)
> about which locks protect which variables. It would be nice to make this
> a bit more explicit.

I think that this patch set should confine itself to the work mutex and what it controls (since that's what this patch set is affecting), but admittedly, that is already more than a handful.

The work mutex controls work_head and done_head, and all the struct base_data contained therein, except for data. However, data is controlled by the semaphore retain_data (and the semaphore is itself controlled by the work mutex, as described in the previous sentence): when retain_data is positive, data can be read from but not written to. The work mutex also controls base_cache_used but not base_cache_limit. The work mutex does not control any struct base_data outside of work_head and done_head, so it is possible to generate a struct base_data outside the mutex, take the mutex, and add it to the list (reducing time spent in the mutex).

This scheme seems rather complicated to me, but I couldn't make it simpler - I was hoping for advice on this. If this is the best we can do, I can try to document it in code comments.

Show 5 quoted lines
> I wish we had something similar to Abseil's "GUARDED_BY" annotations [1]
> so that we could ensure thread safety with static analysis, but that's
> obviously out of scope for this series.
> 
> [1]: https://abseil.io/docs/cpp/guides/synchronization#thread-annotations

I think what we need is more complicated than what these annotations provide, but anyway, Abseil is C++-only, I believe.

Show 29 quoted lines
> > @@ -1065,33 +1011,128 @@ static int compare_ref_delta_entry(const void *a, const void *b)
> [snip]
> >  		work_unlock();
> >  
> > -		resolve_base(&objects[i]);
> > +		if (parent) {
> > +			child = resolve_delta(child_obj, parent);
> > +			if (!child->children_remaining)
> > +				FREE_AND_NULL(child->data);
> > +		} else {
> > +			child = make_base(child_obj, NULL);
> > +			if (child->children_remaining) {
> > +				/*
> > +				 * Since this child has its own delta children,
> > +				 * we will need this data in the future.
> > +				 * Inflate now so that future iterations will
> > +				 * have access to this object's data while
> > +				 * outside the work mutex.
> > +				 */
> > +				child->data = get_data_from_pack(child_obj);
> > +				child->size = child_obj->size;
> > +			}
> > +		}
> > +
> > +		work_lock();
> 
> This part made me uneasy at first, because it looks like we might be
> doing work on shared data outside of the work lock. But just prior to
> unlocking, we parent->retain_data, so we know it won't be freed.

That's correct that it made you uneasy (I think the usual solution is to make data refcounted, but we don't have a refcounting implementation in Git), and that's correct that my solution was that parent->retain_data prevents data from being concurrently written to (in this case, freed). I didn't know how better to implement this.

> And the
> only part we modify is child, which I don't believe is shared with any
> other threads.

Yes - in the part you quoted, child is a whole new object (created from resolve_delta() or make_base()). It is only added to the list within the mutex (the work_lock()).

Previous: Josh SteadmonNext: Josh Steadmon
Message 29 of 31 in “Better threaded delta resolution in index-pack”
  1. 0/6 Better threaded delta resolution in index-packJonathan Tan, Oct 9, 2019
  2. 1/6 index-pack: unify threaded and unthreaded codeJonathan Tan, Oct 9, 2019
  3. Jeff KingOct 17, 2019
  4. 2/6 index-pack: remove redundant parameterJonathan Tan, Oct 9, 2019
  5. Jeff KingOct 17, 2019
  6. 3/6 index-pack: remove redundant child fieldJonathan Tan, Oct 9, 2019
  7. Derrick StoleeOct 10, 2019
  8. Jonathan TanOct 10, 2019
  9. Jeff KingOct 17, 2019
  10. Jeff KingOct 17, 2019
  11. 4/6 index-pack: calculate {ref,ofs}_{first,last} earlyJonathan Tan, Oct 9, 2019
  12. Jeff KingOct 17, 2019
  13. 5/6 index-pack: make resolve_delta() assume base dataJonathan Tan, Oct 9, 2019
  14. Jeff KingOct 17, 2019
  15. 6/6 index-pack: make quantum of work smallerJonathan Tan, Oct 9, 2019
  16. Jeff KingOct 17, 2019
  17. 0/7 Better threaded delta resolution in index-packJonathan Tan, Oct 17, 2019
  18. 1/7 Documentation: deltaBaseCacheLimit is per-threadJonathan Tan, Oct 17, 2019
  19. 3/7 index-pack: remove redundant parameterJonathan Tan, Oct 17, 2019
  20. Josh SteadmonFeb 28, 2020
  21. Jonathan TanMar 10, 2020
  22. 2/7 index-pack: unify threaded and unthreaded codeJonathan Tan, Oct 17, 2019
  23. 4/7 index-pack: remove redundant child fieldJonathan Tan, Oct 17, 2019
  24. Josh SteadmonFeb 28, 2020
  25. 5/7 index-pack: calculate {ref,ofs}_{first,last} earlyJonathan Tan, Oct 17, 2019
  26. 6/7 index-pack: make resolve_delta() assume base dataJonathan Tan, Oct 17, 2019
  27. 7/7 index-pack: make quantum of work smallerJonathan Tan, Oct 17, 2019
  28. Josh SteadmonFeb 28, 2020
  29. Jonathan TanMar 10, 2020
  30. Josh SteadmonFeb 28, 2020
  31. Jonathan TanMar 10, 2020

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.