[DTrace-devel] [PATCH v2 04/11] htab: have dt_htab_destroy del all the elements

Nick Alcock nick.alcock at oracle.com
Wed Oct 27 06:40:11 PDT 2021


On 21 Oct 2021, Eugene Loh outgrape:

> I'm struggling with this one.

So was I, and even Kris misread his own code: hence the extra comments
to try to clarify things a bit.

> On 10/20/21 2:53 PM, Nick Alcock wrote:
>> This means that a dt_htab_destroy of a non-empy htab doesn't leak memory
>> and leave all its former elements full of wild pointers.
>
> s/empy/empty/

Whoops, fixed.

> Were we leaking memory?  If so, what does this patch do to fix that?  I 

We weren't, but only because every single existing user of htabs goes to
some lengths to manually dt_htab_delete every single element before
destroying the htab.  I don't like having to do that and it means that
(in the absence of a hash iterator) you have to track the members
elsewhere, hence all those otherwise-pointless lists.

> can see that the former elements will have their next/prev pointers set 
> to NULL, dealing with those "wild pointers," but the patch does not free 
> any memory.  Or, what am I missing?

This:

>> +		while (bucket && bucket->head)
>> +			bucket->head = htab->ops->del(bucket->head,
>> +			    bucket->head);
>>> >> +	free(bucket);  <<<
>> +	}

So any buckets still in existence get freed at this stage rather than
being leaked.

Also, calling ->del only sets pointers to NULL and does nothing else *by
default*. Users can override it to do other things too (though, at this
point in the series, nobody did, because ->del was not reliably called).

>> Also add some comments to dt_htab_delete given how many mistakes I made
>> reading it.
>
> Thanks for adding the comments, but I don't think that merits mention in 
> the commit message.  Personal choice, I suppose.

I only note it because I went back and forth on what the comments should
say so many times: I spent longer agonizing over what I thought were
bugs in dt_htab_destroy (none of which were) than I did over the whole
rest of the change :)

>> diff --git a/libdtrace/dt_htab.c b/libdtrace/dt_htab.c
>>   void dt_htab_destroy(dtrace_hdl_t *dtp, dt_htab_t *htab)
>>   {
>> +	size_t		i;
>> +
>>   	if (!htab)
>>   		return;
>>   
>> +	for (i = 0; i < htab->size; i++) {
>> +		dt_hbucket_t	*bucket = htab->tab[i];
>> +
>> +		while (bucket && bucket->head)
>> +			bucket->head = htab->ops->del(bucket->head,
>> +			    bucket->head);
>> +		free(bucket);
>> +	}
>> +
>>   	dt_free(dtp, htab->tab);
>>   	dt_free(dtp, htab);
>>   }
>
> Inside the "for i" loop, bucket does not change.  So, maybe replace
>          while (bucket && bucket->head)
> with
>          if (bucket == NULL) continue;
>          while (bucket->head)
>
> More importantly, is the looping right?  You loop over slots.  For each 
> slot, you choose a bucket, deleting its entries and then freeing the 
> bucket.  But what if there are multiple buckets per slot?  Don't you 
> have to chase bucket->next before freeing bucket?

... that's obviously wrong, thank you! Fixed. I wish I knew some way to
*test* this. Synthesising hash collisions is quite hard... maybe a full
amke check run with leak detection going would spot it. Will try.

The loop now reads

	for (i = 0; i < htab->size; i++) {
		dt_hbucket_t	*bucket = htab->tab[i];

		while (bucket) {
			dt_hbucket_t *obucket = bucket;

			while (bucket->head)
				bucket->head = htab->ops->del(bucket->head,
				    bucket->head);
			bucket = bucket->next;
			free(obucket);
		};
	}

i.e. we loop over all the entries in a given bucket, within a loop over
all the buckets in a given tab slot.

>> @@ -201,9 +216,16 @@ int dt_htab_delete(dt_htab_t *htab, void *entry)
>>   	if (bucket == NULL)
>>   		return -ENOENT;
>>   
>> +	/*
>> +	 * Destroy the specified entry, now known to be in this bucket.
>> +	 */
>>   	head = htab->ops->del(bucket->head, entry);
>>   	bucket->nentries--;
>>   	htab->nentries--;
> s/Destroy/Delete/ maybe?  To align better with the function name del()?

Yeah, I had "destroy" on the brain.

-- 
NULL && (void)



More information about the DTrace-devel mailing list