Merge branch 'jt/threaded-index-pack'
"git index-pack" learned to resolve deltified objects with greater parallelism. * jt/threaded-index-pack: index-pack: make quantum of work smaller index-pack: make resolve_delta() assume base data index-pack: calculate {ref,ofs}_{first,last} early index-pack: remove redundant child field index-pack: unify threaded and unthreaded code index-pack: remove redundant parameter Documentation: deltaBaseCacheLimit is per-thread
This commit is contained in:
commit
b7e65b51e5
@ -399,7 +399,7 @@ the largest projects. You probably do not need to adjust this value.
|
|||||||
Common unit suffixes of 'k', 'm', or 'g' are supported.
|
Common unit suffixes of 'k', 'm', or 'g' are supported.
|
||||||
|
|
||||||
core.deltaBaseCacheLimit::
|
core.deltaBaseCacheLimit::
|
||||||
Maximum number of bytes to reserve for caching base objects
|
Maximum number of bytes per thread to reserve for caching base objects
|
||||||
that may be referenced by multiple deltified objects. By storing the
|
that may be referenced by multiple deltified objects. By storing the
|
||||||
entire decompressed base objects in a cache Git is able
|
entire decompressed base objects in a cache Git is able
|
||||||
to avoid unpacking and decompressing frequently used base
|
to avoid unpacking and decompressing frequently used base
|
||||||
|
@ -33,19 +33,61 @@ struct object_stat {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct base_data {
|
struct base_data {
|
||||||
|
/* Initialized by make_base(). */
|
||||||
struct base_data *base;
|
struct base_data *base;
|
||||||
struct base_data *child;
|
|
||||||
struct object_entry *obj;
|
struct object_entry *obj;
|
||||||
void *data;
|
|
||||||
unsigned long size;
|
|
||||||
int ref_first, ref_last;
|
int ref_first, ref_last;
|
||||||
int ofs_first, ofs_last;
|
int ofs_first, ofs_last;
|
||||||
|
/*
|
||||||
|
* Threads should increment retain_data if they are about to call
|
||||||
|
* patch_delta() using this struct's data as a base, and decrement this
|
||||||
|
* when they are done. While retain_data is nonzero, this struct's data
|
||||||
|
* will not be freed even if the delta base cache limit is exceeded.
|
||||||
|
*/
|
||||||
|
int retain_data;
|
||||||
|
/*
|
||||||
|
* The number of direct children that have not been fully processed
|
||||||
|
* (entered work_head, entered done_head, left done_head). When this
|
||||||
|
* number reaches zero, this struct base_data can be freed.
|
||||||
|
*/
|
||||||
|
int children_remaining;
|
||||||
|
|
||||||
|
/* Not initialized by make_base(). */
|
||||||
|
struct list_head list;
|
||||||
|
void *data;
|
||||||
|
unsigned long size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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).
|
||||||
|
*
|
||||||
|
* Guarded by work_mutex.
|
||||||
|
*/
|
||||||
|
static 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.
|
||||||
|
*
|
||||||
|
* Guarded by work_mutex.
|
||||||
|
*/
|
||||||
|
static LIST_HEAD(done_head);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All threads share one delta base cache.
|
||||||
|
*
|
||||||
|
* base_cache_used is guarded by work_mutex, and base_cache_limit is read-only
|
||||||
|
* in a thread.
|
||||||
|
*/
|
||||||
|
static size_t base_cache_used;
|
||||||
|
static size_t base_cache_limit;
|
||||||
|
|
||||||
struct thread_local {
|
struct thread_local {
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
struct base_data *base_cache;
|
|
||||||
size_t base_cache_used;
|
|
||||||
int pack_fd;
|
int pack_fd;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -364,56 +406,42 @@ static void set_thread_data(struct thread_local *data)
|
|||||||
pthread_setspecific(key, data);
|
pthread_setspecific(key, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct base_data *alloc_base_data(void)
|
|
||||||
{
|
|
||||||
struct base_data *base = xcalloc(1, sizeof(struct base_data));
|
|
||||||
base->ref_last = -1;
|
|
||||||
base->ofs_last = -1;
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_base_data(struct base_data *c)
|
static void free_base_data(struct base_data *c)
|
||||||
{
|
{
|
||||||
if (c->data) {
|
if (c->data) {
|
||||||
FREE_AND_NULL(c->data);
|
FREE_AND_NULL(c->data);
|
||||||
get_thread_data()->base_cache_used -= c->size;
|
base_cache_used -= c->size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prune_base_data(struct base_data *retain)
|
static void prune_base_data(struct base_data *retain)
|
||||||
{
|
{
|
||||||
struct base_data *b;
|
struct list_head *pos;
|
||||||
struct thread_local *data = get_thread_data();
|
|
||||||
for (b = data->base_cache;
|
if (base_cache_used <= base_cache_limit)
|
||||||
data->base_cache_used > delta_base_cache_limit && b;
|
return;
|
||||||
b = b->child) {
|
|
||||||
if (b->data && b != retain)
|
list_for_each_prev(pos, &done_head) {
|
||||||
|
struct base_data *b = list_entry(pos, struct base_data, list);
|
||||||
|
if (b->retain_data || b == retain)
|
||||||
|
continue;
|
||||||
|
if (b->data) {
|
||||||
free_base_data(b);
|
free_base_data(b);
|
||||||
|
if (base_cache_used <= base_cache_limit)
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void link_base_data(struct base_data *base, struct base_data *c)
|
list_for_each_prev(pos, &work_head) {
|
||||||
{
|
struct base_data *b = list_entry(pos, struct base_data, list);
|
||||||
if (base)
|
if (b->retain_data || b == retain)
|
||||||
base->child = c;
|
continue;
|
||||||
else
|
if (b->data) {
|
||||||
get_thread_data()->base_cache = c;
|
free_base_data(b);
|
||||||
|
if (base_cache_used <= base_cache_limit)
|
||||||
c->base = base;
|
return;
|
||||||
c->child = NULL;
|
}
|
||||||
if (c->data)
|
}
|
||||||
get_thread_data()->base_cache_used += c->size;
|
|
||||||
prune_base_data(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void unlink_base_data(struct base_data *c)
|
|
||||||
{
|
|
||||||
struct base_data *base = c->base;
|
|
||||||
if (base)
|
|
||||||
base->child = NULL;
|
|
||||||
else
|
|
||||||
get_thread_data()->base_cache = NULL;
|
|
||||||
free_base_data(c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_delta_type(enum object_type type)
|
static int is_delta_type(enum object_type type)
|
||||||
@ -614,7 +642,7 @@ static int compare_ofs_delta_bases(off_t offset1, off_t offset2,
|
|||||||
0;
|
0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int find_ofs_delta(const off_t offset, enum object_type type)
|
static int find_ofs_delta(const off_t offset)
|
||||||
{
|
{
|
||||||
int first = 0, last = nr_ofs_deltas;
|
int first = 0, last = nr_ofs_deltas;
|
||||||
|
|
||||||
@ -624,7 +652,8 @@ static int find_ofs_delta(const off_t offset, enum object_type type)
|
|||||||
int cmp;
|
int cmp;
|
||||||
|
|
||||||
cmp = compare_ofs_delta_bases(offset, delta->offset,
|
cmp = compare_ofs_delta_bases(offset, delta->offset,
|
||||||
type, objects[delta->obj_no].type);
|
OBJ_OFS_DELTA,
|
||||||
|
objects[delta->obj_no].type);
|
||||||
if (!cmp)
|
if (!cmp)
|
||||||
return next;
|
return next;
|
||||||
if (cmp < 0) {
|
if (cmp < 0) {
|
||||||
@ -637,10 +666,9 @@ static int find_ofs_delta(const off_t offset, enum object_type type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void find_ofs_delta_children(off_t offset,
|
static void find_ofs_delta_children(off_t offset,
|
||||||
int *first_index, int *last_index,
|
int *first_index, int *last_index)
|
||||||
enum object_type type)
|
|
||||||
{
|
{
|
||||||
int first = find_ofs_delta(offset, type);
|
int first = find_ofs_delta(offset);
|
||||||
int last = first;
|
int last = first;
|
||||||
int end = nr_ofs_deltas - 1;
|
int end = nr_ofs_deltas - 1;
|
||||||
|
|
||||||
@ -668,7 +696,7 @@ static int compare_ref_delta_bases(const struct object_id *oid1,
|
|||||||
return oidcmp(oid1, oid2);
|
return oidcmp(oid1, oid2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int find_ref_delta(const struct object_id *oid, enum object_type type)
|
static int find_ref_delta(const struct object_id *oid)
|
||||||
{
|
{
|
||||||
int first = 0, last = nr_ref_deltas;
|
int first = 0, last = nr_ref_deltas;
|
||||||
|
|
||||||
@ -678,7 +706,8 @@ static int find_ref_delta(const struct object_id *oid, enum object_type type)
|
|||||||
int cmp;
|
int cmp;
|
||||||
|
|
||||||
cmp = compare_ref_delta_bases(oid, &delta->oid,
|
cmp = compare_ref_delta_bases(oid, &delta->oid,
|
||||||
type, objects[delta->obj_no].type);
|
OBJ_REF_DELTA,
|
||||||
|
objects[delta->obj_no].type);
|
||||||
if (!cmp)
|
if (!cmp)
|
||||||
return next;
|
return next;
|
||||||
if (cmp < 0) {
|
if (cmp < 0) {
|
||||||
@ -691,10 +720,9 @@ static int find_ref_delta(const struct object_id *oid, enum object_type type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void find_ref_delta_children(const struct object_id *oid,
|
static void find_ref_delta_children(const struct object_id *oid,
|
||||||
int *first_index, int *last_index,
|
int *first_index, int *last_index)
|
||||||
enum object_type type)
|
|
||||||
{
|
{
|
||||||
int first = find_ref_delta(oid, type);
|
int first = find_ref_delta(oid);
|
||||||
int last = first;
|
int last = first;
|
||||||
int end = nr_ref_deltas - 1;
|
int end = nr_ref_deltas - 1;
|
||||||
|
|
||||||
@ -866,15 +894,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function is part of find_unresolved_deltas(). There are two
|
* Walk from current node up
|
||||||
* walkers going in the opposite ways.
|
|
||||||
*
|
|
||||||
* The first one in find_unresolved_deltas() traverses down from
|
|
||||||
* parent node to children, deflating nodes along the way. However,
|
|
||||||
* memory for deflated nodes is limited by delta_base_cache_limit, so
|
|
||||||
* at some point parent node's deflated content may be freed.
|
|
||||||
*
|
|
||||||
* The second walker is this function, which goes from current node up
|
|
||||||
* to top parent if necessary to deflate the node. In normal
|
* to top parent if necessary to deflate the node. In normal
|
||||||
* situation, its parent node would be already deflated, so it just
|
* situation, its parent node would be already deflated, so it just
|
||||||
* needs to apply delta.
|
* needs to apply delta.
|
||||||
@ -902,7 +922,7 @@ static void *get_base_data(struct base_data *c)
|
|||||||
if (!delta_nr) {
|
if (!delta_nr) {
|
||||||
c->data = get_data_from_pack(obj);
|
c->data = get_data_from_pack(obj);
|
||||||
c->size = obj->size;
|
c->size = obj->size;
|
||||||
get_thread_data()->base_cache_used += c->size;
|
base_cache_used += c->size;
|
||||||
prune_base_data(c);
|
prune_base_data(c);
|
||||||
}
|
}
|
||||||
for (; delta_nr > 0; delta_nr--) {
|
for (; delta_nr > 0; delta_nr--) {
|
||||||
@ -918,7 +938,7 @@ static void *get_base_data(struct base_data *c)
|
|||||||
free(raw);
|
free(raw);
|
||||||
if (!c->data)
|
if (!c->data)
|
||||||
bad_object(obj->idx.offset, _("failed to apply delta"));
|
bad_object(obj->idx.offset, _("failed to apply delta"));
|
||||||
get_thread_data()->base_cache_used += c->size;
|
base_cache_used += c->size;
|
||||||
prune_base_data(c);
|
prune_base_data(c);
|
||||||
}
|
}
|
||||||
free(delta);
|
free(delta);
|
||||||
@ -926,10 +946,27 @@ static void *get_base_data(struct base_data *c)
|
|||||||
return c->data;
|
return c->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void resolve_delta(struct object_entry *delta_obj,
|
static struct base_data *make_base(struct object_entry *obj,
|
||||||
struct base_data *base, struct base_data *result)
|
struct base_data *parent)
|
||||||
{
|
{
|
||||||
void *base_data, *delta_data;
|
struct base_data *base = xcalloc(1, sizeof(struct base_data));
|
||||||
|
base->base = parent;
|
||||||
|
base->obj = obj;
|
||||||
|
find_ref_delta_children(&obj->idx.oid,
|
||||||
|
&base->ref_first, &base->ref_last);
|
||||||
|
find_ofs_delta_children(obj->idx.offset,
|
||||||
|
&base->ofs_first, &base->ofs_last);
|
||||||
|
base->children_remaining = base->ref_last - base->ref_first +
|
||||||
|
base->ofs_last - base->ofs_first + 2;
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct base_data *resolve_delta(struct object_entry *delta_obj,
|
||||||
|
struct base_data *base)
|
||||||
|
{
|
||||||
|
void *delta_data, *result_data;
|
||||||
|
struct base_data *result;
|
||||||
|
unsigned long result_size;
|
||||||
|
|
||||||
if (show_stat) {
|
if (show_stat) {
|
||||||
int i = delta_obj - objects;
|
int i = delta_obj - objects;
|
||||||
@ -942,115 +979,26 @@ static void resolve_delta(struct object_entry *delta_obj,
|
|||||||
obj_stat[i].base_object_no = j;
|
obj_stat[i].base_object_no = j;
|
||||||
}
|
}
|
||||||
delta_data = get_data_from_pack(delta_obj);
|
delta_data = get_data_from_pack(delta_obj);
|
||||||
base_data = get_base_data(base);
|
assert(base->data);
|
||||||
result->obj = delta_obj;
|
result_data = patch_delta(base->data, base->size,
|
||||||
result->data = patch_delta(base_data, base->size,
|
delta_data, delta_obj->size, &result_size);
|
||||||
delta_data, delta_obj->size, &result->size);
|
|
||||||
free(delta_data);
|
free(delta_data);
|
||||||
if (!result->data)
|
if (!result_data)
|
||||||
bad_object(delta_obj->idx.offset, _("failed to apply delta"));
|
bad_object(delta_obj->idx.offset, _("failed to apply delta"));
|
||||||
hash_object_file(the_hash_algo, result->data, result->size,
|
hash_object_file(the_hash_algo, result_data, result_size,
|
||||||
type_name(delta_obj->real_type), &delta_obj->idx.oid);
|
type_name(delta_obj->real_type), &delta_obj->idx.oid);
|
||||||
sha1_object(result->data, NULL, result->size, delta_obj->real_type,
|
sha1_object(result_data, NULL, result_size, delta_obj->real_type,
|
||||||
&delta_obj->idx.oid);
|
&delta_obj->idx.oid);
|
||||||
|
|
||||||
|
result = make_base(delta_obj, base);
|
||||||
|
result->data = result_data;
|
||||||
|
result->size = result_size;
|
||||||
|
|
||||||
counter_lock();
|
counter_lock();
|
||||||
nr_resolved_deltas++;
|
nr_resolved_deltas++;
|
||||||
counter_unlock();
|
counter_unlock();
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
return result;
|
||||||
* Standard boolean compare-and-swap: atomically check whether "*type" is
|
|
||||||
* "want"; if so, swap in "set" and return true. Otherwise, leave it untouched
|
|
||||||
* and return false.
|
|
||||||
*/
|
|
||||||
static int compare_and_swap_type(signed char *type,
|
|
||||||
enum object_type want,
|
|
||||||
enum object_type set)
|
|
||||||
{
|
|
||||||
enum object_type old;
|
|
||||||
|
|
||||||
type_cas_lock();
|
|
||||||
old = *type;
|
|
||||||
if (old == want)
|
|
||||||
*type = set;
|
|
||||||
type_cas_unlock();
|
|
||||||
|
|
||||||
return old == want;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct base_data *find_unresolved_deltas_1(struct base_data *base,
|
|
||||||
struct base_data *prev_base)
|
|
||||||
{
|
|
||||||
if (base->ref_last == -1 && base->ofs_last == -1) {
|
|
||||||
find_ref_delta_children(&base->obj->idx.oid,
|
|
||||||
&base->ref_first, &base->ref_last,
|
|
||||||
OBJ_REF_DELTA);
|
|
||||||
|
|
||||||
find_ofs_delta_children(base->obj->idx.offset,
|
|
||||||
&base->ofs_first, &base->ofs_last,
|
|
||||||
OBJ_OFS_DELTA);
|
|
||||||
|
|
||||||
if (base->ref_last == -1 && base->ofs_last == -1) {
|
|
||||||
free(base->data);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
link_base_data(prev_base, base);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (base->ref_first <= base->ref_last) {
|
|
||||||
struct object_entry *child = objects + ref_deltas[base->ref_first].obj_no;
|
|
||||||
struct base_data *result = alloc_base_data();
|
|
||||||
|
|
||||||
if (!compare_and_swap_type(&child->real_type, OBJ_REF_DELTA,
|
|
||||||
base->obj->real_type))
|
|
||||||
die("REF_DELTA at offset %"PRIuMAX" already resolved (duplicate base %s?)",
|
|
||||||
(uintmax_t)child->idx.offset,
|
|
||||||
oid_to_hex(&base->obj->idx.oid));
|
|
||||||
|
|
||||||
resolve_delta(child, base, result);
|
|
||||||
if (base->ref_first == base->ref_last && base->ofs_last == -1)
|
|
||||||
free_base_data(base);
|
|
||||||
|
|
||||||
base->ref_first++;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (base->ofs_first <= base->ofs_last) {
|
|
||||||
struct object_entry *child = objects + ofs_deltas[base->ofs_first].obj_no;
|
|
||||||
struct base_data *result = alloc_base_data();
|
|
||||||
|
|
||||||
assert(child->real_type == OBJ_OFS_DELTA);
|
|
||||||
child->real_type = base->obj->real_type;
|
|
||||||
resolve_delta(child, base, result);
|
|
||||||
if (base->ofs_first == base->ofs_last)
|
|
||||||
free_base_data(base);
|
|
||||||
|
|
||||||
base->ofs_first++;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
unlink_base_data(base);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void find_unresolved_deltas(struct base_data *base)
|
|
||||||
{
|
|
||||||
struct base_data *new_base, *prev_base = NULL;
|
|
||||||
for (;;) {
|
|
||||||
new_base = find_unresolved_deltas_1(base, prev_base);
|
|
||||||
|
|
||||||
if (new_base) {
|
|
||||||
prev_base = base;
|
|
||||||
base = new_base;
|
|
||||||
} else {
|
|
||||||
free(base);
|
|
||||||
base = prev_base;
|
|
||||||
if (!base)
|
|
||||||
return;
|
|
||||||
prev_base = base->base;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int compare_ofs_delta_entry(const void *a, const void *b)
|
static int compare_ofs_delta_entry(const void *a, const void *b)
|
||||||
@ -1071,34 +1019,131 @@ static int compare_ref_delta_entry(const void *a, const void *b)
|
|||||||
return oidcmp(&delta_a->oid, &delta_b->oid);
|
return oidcmp(&delta_a->oid, &delta_b->oid);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void resolve_base(struct object_entry *obj)
|
|
||||||
{
|
|
||||||
struct base_data *base_obj = alloc_base_data();
|
|
||||||
base_obj->obj = obj;
|
|
||||||
base_obj->data = NULL;
|
|
||||||
find_unresolved_deltas(base_obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *threaded_second_pass(void *data)
|
static void *threaded_second_pass(void *data)
|
||||||
{
|
{
|
||||||
set_thread_data(data);
|
if (data)
|
||||||
|
set_thread_data(data);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int i;
|
struct base_data *parent = NULL;
|
||||||
counter_lock();
|
struct object_entry *child_obj;
|
||||||
display_progress(progress, nr_resolved_deltas);
|
struct base_data *child;
|
||||||
counter_unlock();
|
|
||||||
work_lock();
|
work_lock();
|
||||||
while (nr_dispatched < nr_objects &&
|
if (list_empty(&work_head)) {
|
||||||
is_delta_type(objects[nr_dispatched].type))
|
/*
|
||||||
nr_dispatched++;
|
* Take an object from the object array.
|
||||||
if (nr_dispatched >= nr_objects) {
|
*/
|
||||||
work_unlock();
|
while (nr_dispatched < nr_objects &&
|
||||||
break;
|
is_delta_type(objects[nr_dispatched].type))
|
||||||
|
nr_dispatched++;
|
||||||
|
if (nr_dispatched >= nr_objects) {
|
||||||
|
work_unlock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
child_obj = &objects[nr_dispatched++];
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Peek at the top of the stack, and take a child from
|
||||||
|
* it.
|
||||||
|
*/
|
||||||
|
parent = list_first_entry(&work_head, struct base_data,
|
||||||
|
list);
|
||||||
|
|
||||||
|
if (parent->ref_first <= parent->ref_last) {
|
||||||
|
int offset = ref_deltas[parent->ref_first++].obj_no;
|
||||||
|
child_obj = objects + offset;
|
||||||
|
if (child_obj->real_type != OBJ_REF_DELTA)
|
||||||
|
die("REF_DELTA at offset %"PRIuMAX" already resolved (duplicate base %s?)",
|
||||||
|
(uintmax_t) child_obj->idx.offset,
|
||||||
|
oid_to_hex(&parent->obj->idx.oid));
|
||||||
|
child_obj->real_type = parent->obj->real_type;
|
||||||
|
} else {
|
||||||
|
child_obj = objects +
|
||||||
|
ofs_deltas[parent->ofs_first++].obj_no;
|
||||||
|
assert(child_obj->real_type == OBJ_OFS_DELTA);
|
||||||
|
child_obj->real_type = parent->obj->real_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent->ref_first > parent->ref_last &&
|
||||||
|
parent->ofs_first > parent->ofs_last) {
|
||||||
|
/*
|
||||||
|
* This parent has run out of children, so move
|
||||||
|
* it to done_head.
|
||||||
|
*/
|
||||||
|
list_del(&parent->list);
|
||||||
|
list_add(&parent->list, &done_head);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure that the parent has data, since we will need
|
||||||
|
* it later.
|
||||||
|
*
|
||||||
|
* NEEDSWORK: If parent data needs to be reloaded, this
|
||||||
|
* prolongs the time that the current thread spends in
|
||||||
|
* the mutex. A mitigating factor is that parent data
|
||||||
|
* needs to be reloaded only if the delta base cache
|
||||||
|
* limit is exceeded, so in the typical case, this does
|
||||||
|
* not happen.
|
||||||
|
*/
|
||||||
|
get_base_data(parent);
|
||||||
|
parent->retain_data++;
|
||||||
}
|
}
|
||||||
i = nr_dispatched++;
|
|
||||||
work_unlock();
|
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();
|
||||||
|
if (parent)
|
||||||
|
parent->retain_data--;
|
||||||
|
if (child->data) {
|
||||||
|
/*
|
||||||
|
* This child has its own children, so add it to
|
||||||
|
* work_head.
|
||||||
|
*/
|
||||||
|
list_add(&child->list, &work_head);
|
||||||
|
base_cache_used += child->size;
|
||||||
|
prune_base_data(NULL);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* This child does not have its own children. It may be
|
||||||
|
* the last descendant of its ancestors; free those
|
||||||
|
* that we can.
|
||||||
|
*/
|
||||||
|
struct base_data *p = parent;
|
||||||
|
|
||||||
|
while (p) {
|
||||||
|
struct base_data *next_p;
|
||||||
|
|
||||||
|
p->children_remaining--;
|
||||||
|
if (p->children_remaining)
|
||||||
|
break;
|
||||||
|
|
||||||
|
next_p = p->base;
|
||||||
|
free_base_data(p);
|
||||||
|
list_del(&p->list);
|
||||||
|
free(p);
|
||||||
|
|
||||||
|
p = next_p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
work_unlock();
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -1199,6 +1244,7 @@ static void resolve_deltas(void)
|
|||||||
nr_ref_deltas + nr_ofs_deltas);
|
nr_ref_deltas + nr_ofs_deltas);
|
||||||
|
|
||||||
nr_dispatched = 0;
|
nr_dispatched = 0;
|
||||||
|
base_cache_limit = delta_base_cache_limit * nr_threads;
|
||||||
if (nr_threads > 1 || getenv("GIT_FORCE_THREADS")) {
|
if (nr_threads > 1 || getenv("GIT_FORCE_THREADS")) {
|
||||||
init_thread();
|
init_thread();
|
||||||
for (i = 0; i < nr_threads; i++) {
|
for (i = 0; i < nr_threads; i++) {
|
||||||
@ -1213,15 +1259,7 @@ static void resolve_deltas(void)
|
|||||||
cleanup_thread();
|
cleanup_thread();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
threaded_second_pass(¬hread_data);
|
||||||
for (i = 0; i < nr_objects; i++) {
|
|
||||||
struct object_entry *obj = &objects[i];
|
|
||||||
|
|
||||||
if (is_delta_type(obj->type))
|
|
||||||
continue;
|
|
||||||
resolve_base(obj);
|
|
||||||
display_progress(progress, nr_resolved_deltas);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1376,22 +1414,28 @@ static void fix_unresolved_deltas(struct hashfile *f)
|
|||||||
for (i = 0; i < nr_ref_deltas; i++) {
|
for (i = 0; i < nr_ref_deltas; i++) {
|
||||||
struct ref_delta_entry *d = sorted_by_pos[i];
|
struct ref_delta_entry *d = sorted_by_pos[i];
|
||||||
enum object_type type;
|
enum object_type type;
|
||||||
struct base_data *base_obj = alloc_base_data();
|
void *data;
|
||||||
|
unsigned long size;
|
||||||
|
|
||||||
if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
|
if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
|
||||||
continue;
|
continue;
|
||||||
base_obj->data = read_object_file(&d->oid, &type,
|
data = read_object_file(&d->oid, &type, &size);
|
||||||
&base_obj->size);
|
if (!data)
|
||||||
if (!base_obj->data)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (check_object_signature(the_repository, &d->oid,
|
if (check_object_signature(the_repository, &d->oid,
|
||||||
base_obj->data, base_obj->size,
|
data, size,
|
||||||
type_name(type)))
|
type_name(type)))
|
||||||
die(_("local object %s is corrupt"), oid_to_hex(&d->oid));
|
die(_("local object %s is corrupt"), oid_to_hex(&d->oid));
|
||||||
base_obj->obj = append_obj_to_pack(f, d->oid.hash,
|
|
||||||
base_obj->data, base_obj->size, type);
|
/*
|
||||||
find_unresolved_deltas(base_obj);
|
* Add this as an object to the objects array and call
|
||||||
|
* threaded_second_pass() (which will pick up the added
|
||||||
|
* object).
|
||||||
|
*/
|
||||||
|
append_obj_to_pack(f, d->oid.hash, data, size, type);
|
||||||
|
threaded_second_pass(NULL);
|
||||||
|
|
||||||
display_progress(progress, nr_resolved_deltas);
|
display_progress(progress, nr_resolved_deltas);
|
||||||
}
|
}
|
||||||
free(sorted_by_pos);
|
free(sorted_by_pos);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user