On Mon 6, May 2019 at 00:16:57, Nathanael Rensen wrote: > On Sun, 5 May 2019 at 06:41, Martin Pieuchot <m...@openbsd.org> wrote:
[snip] > > Do I understand correctly that you're not recursing if not object is > > inserted? > > Yes. > > You have probably also noticed that using a single recurse flag is not > optimally efficient. It is only necessary to recurse child nodes where > n->data->grpsym_gen != _dl_grpsym_gen > With my approach if any child node satisfies that then all child nodes > are recursed. [snip] > What do you think about the approach below? > > static void > _dl_insert_grpsym(elf_object_t *object) > { > struct dep_node *n; > > n = _dl_malloc(sizeof *n); > if (n == NULL) > _dl_oom(); > n->data = object; > TAILQ_INSERT_TAIL(&_dl_loading_object->grpsym_list, n, next_sib); > } > > void > _dl_link_grpsym(elf_object_t *object) > { > struct dep_node *n; > > TAILQ_FOREACH(n, &_dl_loading_object->grpsym_list, next_sib) > if (n->data == object && object->grpsym_gen == _dl_grpsym_gen) > return; /* found, dont bother adding */ > > _dl_insert_grpsym(object); > object->grpsym_gen = _dl_grpsym_gen; > } > > void > _dl_cache_grpsym_list(elf_object_t *object) > { > struct dep_node *n; > > /* > * grpsym_list is an ordered list of all child libs of the > * _dl_loading_object with no dups. The order is equivalent > * to a breadth-first traversal of the child list without dups. > */ > > TAILQ_FOREACH(n, &object->child_list, next_sib) > if (n->data->grpsym_gen != _dl_grpsym_gen) > _dl_insert_grpsym(n->data); > > TAILQ_FOREACH(n, &object->child_list, next_sib) > if (n->data->grpsym_gen != _dl_grpsym_gen) { > object->grpsym_gen = _dl_grpsym_gen; > _dl_cache_grpsym_list(n->data); > } > } For the record, the approach above is wrong. It is essential that object->grpsym_gen is updated by _dl_insert_grpsym() otherwise a tree such as that below would result in z being added to grpsym_list twice: x -> y,z y -> z While processing x both children y and z are inserted without updating grpsym_gen. Then while recursing into y, z is added a second time. This can be fixed using a status flag as follows: static void _dl_insert_grpsym(elf_object_t *object) { struct dep_node *n; object->grpsym_gen = _dl_grpsym_gen; n = _dl_malloc(sizeof *n); if (n == NULL) _dl_oom(); n->data = object; TAILQ_INSERT_TAIL(&_dl_loading_object->grpsym_list, n, next_sib); } void _dl_link_grpsym(elf_object_t *object) { struct dep_node *n; TAILQ_FOREACH(n, &_dl_loading_object->grpsym_list, next_sib) if (n->data == object && object->grpsym_gen == _dl_grpsym_gen) return; /* found, dont bother adding */ _dl_insert_grpsym(object); } void _dl_cache_grpsym_list(elf_object_t *object) { struct dep_node *n; /* * grpsym_list is an ordered list of all child libs of the * _dl_loading_object with no dups. The order is equivalent * to a breadth-first traversal of the child list without dups. */ TAILQ_FOREACH(n, &object->child_list, next_sib) if (n->data->grpsym_gen != _dl_grpsym_gen) { n->data->status |= STAT_GS_RECURSE; _dl_insert_grpsym(n->data); } TAILQ_FOREACH(n, &object->child_list, next_sib) if (n->data->status & STAT_GS_RECURSE) { n->data->status &= ~STAT_GS_RECURSE; _dl_cache_grpsym_list(n->data); } } This approach maintains the integrity of the grpsym_gen mechanism ensuring a node will not be added to the grpsym_list multiple times. The STAT_GS_RECURSE flag is used to record the requirement to recurse for those nodes added on this iteration. This extends the per-iteration recurse flag of the original diff to a per-child flag without damaging the integrity of the grpsym_gen mechanism. For the example tree above, while processing x both y and z are inserted and STAT_GS_RECURSE is set on both. Then when recursing y, z is not inserted again because grpsym_gen has already been updated. The flaw described above is not present in the original part 1 diff, only in the variation I proposed in response to mpi@'s questions. Nathanael