[DTrace-devel] [PATCH v2 08/11] htab: add a _next-style iterator
Eugene Loh
eugene.loh at oracle.com
Fri Oct 22 12:03:22 PDT 2021
Reviewed-by: Eugene Loh <eugene.loh at oracle.com>
How about renaming the patch "htab: add an iterator". The kind of
iterator doesn't that much matter. And I don't know what _next-style is
anyhow.
For dt_consume.c and dt_kernel_module.c, as I mentioned in the other
review, add next after del to stick to the same order that is
established in dt_htab.h. Also, consider adding a
DEFINE_HTAB_STD_OPS_RES(ID) macro to simplify the dt_htab_ops_t definitions.
On 10/20/21 2:54 PM, Nick Alcock wrote:
> This commit defines an in the libctf _next style because it's *so much*
> nicer to use than the function-calling _iter form:
"defines an in the"? Did you mean
"defines an iterator in the"?
Again, the kind of iterator seems unimportant to me. Specifically, I'm
not familiar with libctf anyhow.
It would seem to me that a bunch of the usage information is better off
in the source code, where a future user/developer would be looking, than
in the commit message.
> extern void *dt_htab_next(const dt_htab_t *htab, dt_htab_next_t **it);
> extern void dt_htab_next_destroy(dt_htab_next_t *i);
> extern void dt_htab_next_delete(dt_htab_next_t *i);
>
> Call dt_htab_next with the htab to iterate over and a dt_htab_next_t
> initialized to NULL. It allocates an iterator, returns new elements from
> the hash until iteration is complete, then frees the iterator and
> re-annuls it, ready for another iteration cycle. If you need to exit
> early, dt_htab_next_destroy frees the iterator for you (it's just a
> free() for now, but see binutils-gdb/libctf/ctf-util.c:ctf_next_destroy
> for how this can change over time if your iterator family can iterate
> over many different things, like the ctf_next_t can.)
>
> There are no restrictions whatsoever on what you can do inside iteration
> (iterate over other things, fork, longjmp out of it, you name it) except
> for deleting the hash entry you're iterating over: for that, we have
> dt_htab_next_delete. (You might repeat some elements you've already seen
> after calling this, but for most use cases I can think of this is much
> safer than missing some. This is not a fundamental limitation: I just
> need to add a .prev htab operation, symmetric with .next, to fix it. For
> now this feels unnecessary: the only likely caller of
> dt_htab_next_delete doesn't care if it sees some elements more than
> once.)
I am not sure I understand this. IIUC:
*) You cannot delete the entry the iterator is currently on since that
would break the iterator.
*) If you call the next_delete function, the iterator returns to the
bucket head, potentially revisiting some entries.
The fix for the second problem would be adding a .prev operation, as you
point out, and that would almost be shorter than the discussion of this
topic in the commit message! (I think. I might be wrong.)
But I'm unsure why there is a next_delete function in the first place.
I'm not convinced I'm thinking about this straight, but it seems to me
to be pretty routine to delete stuff in linked lists. You just make sure
where you go next before deleting where you're at. E.g.,
last = NULL;
while (me = dt_htab_next(...)) {
[...]
dt_htab_delete(htab, last);
last = me;
}
dt_htab_delete(htab, last)
Or something like that.
I'm just thinking here. Again, no need for anyone here to be making a
career out of these functions.
> diff --git a/libdtrace/dt_htab.c b/libdtrace/dt_htab.c
> @@ -246,6 +253,78 @@ int dt_htab_delete(dt_htab_t *htab, void *entry)
> return 0;
> }
>
> +/*
> + * Iterate over a hashtable, returning each element in turn (as a pointer to
> + * void). NULL is returned at end-of-iteration (and OOM).
> + */
> +void *
> +dt_htab_next(const dt_htab_t *htab, dt_htab_next_t **it)
> +{
> + dt_htab_next_t *i = *it;
> + /*
> + * Start of iteration.
> + */
Insert a blank line after the declaration and before the block comment.
> + if (!i) {
> + if ((i = malloc(sizeof(dt_htab_next_t))) == NULL)
> + return NULL;
> + i->htab = htab;
> + i->idx = 0;
> + i->bucket = NULL;
> + i->ent = NULL;
> + *it = i;
> + }
At this point, you could say "htab = i->htab" and change the remaining
i->htab dereferences in the rest of the function to simply htab, if you
want.
> +
> + /*
> + * One iteration cycle. Exhaust the current idx: moving on to the next
> + * restarts the bucket and ent subloops.
> + */
> + for (; i->idx < i->htab->size; i->idx++, i->bucket = NULL, i->ent = NULL) {
> +
> + if (i->htab->tab[i->idx] == NULL)
> + continue;
> + if (i->bucket == NULL)
> + i->bucket = i->htab->tab[i->idx];
> +
> + /*
> + * Check the current bucket: moving on to the next restarts the
> + * ent subloop.
> + */
> + for (; i->bucket; i->bucket = i->bucket->next, i->ent = NULL) {
> +
> + if (i->ent == NULL)
> + i->ent = i->bucket->head;
> + else
> + i->ent = i->htab->ops->next(i->ent);
> + if (i->ent)
> + return i->ent;
> + }
> + }
> +
> + free(i);
> + *it = NULL;
> + return NULL;
> +}
> +
More information about the DTrace-devel
mailing list