Skip to content

Commit fbead89

Browse files
vstinnermcepl
authored andcommitted
bpo-40609: Add destroy functions to _Py_hashtable (pythonGH-20062)
Add key_destroy_func and value_destroy_func parameters to _Py_hashtable_new_full(). marshal.c and _tracemalloc.c use these destroy functions.
1 parent 0c31c96 commit fbead89

File tree

4 files changed

+96
-74
lines changed

4 files changed

+96
-74
lines changed

Modules/_tracemalloc.c

Lines changed: 48 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -279,12 +279,13 @@ hashtable_compare_pointer_t(const void *key1, const void *key2)
279279
static _Py_hashtable_t *
280280
hashtable_new(size_t data_size,
281281
_Py_hashtable_hash_func hash_func,
282-
_Py_hashtable_compare_func compare_func)
282+
_Py_hashtable_compare_func compare_func,
283+
_Py_hashtable_value_destroy_func value_destroy_fun)
283284
{
284285
_Py_hashtable_allocator_t hashtable_alloc = {malloc, free};
285286
return _Py_hashtable_new_full(data_size, 0,
286287
hash_func, compare_func,
287-
&hashtable_alloc);
288+
NULL, value_destroy_fun, &hashtable_alloc);
288289
}
289290

290291

@@ -519,59 +520,41 @@ static int
519520
tracemalloc_use_domain_cb(_Py_hashtable_t *old_traces,
520521
_Py_hashtable_entry_t *entry, void *user_data)
521522
{
522-
uintptr_t ptr;
523-
pointer_t key;
524-
_Py_hashtable_t *new_traces = (_Py_hashtable_t *)user_data;
525-
const void *pdata = _Py_HASHTABLE_ENTRY_PDATA(old_traces, entry);
523+
return hashtable_new(sizeof(trace_t),
524+
_Py_hashtable_hash_ptr,
525+
_Py_hashtable_compare_direct,
526+
NULL);
527+
}
526528

527529
_Py_HASHTABLE_ENTRY_READ_KEY(old_traces, entry, ptr);
528530
key.ptr = ptr;
529531
key.domain = DEFAULT_DOMAIN;
530532

531-
return _Py_hashtable_set(new_traces,
532-
sizeof(key), &key,
533-
old_traces->data_size, pdata);
533+
static void
534+
tracemalloc_destroy_domain_table(_Py_hashtable_t *domains,
535+
_Py_hashtable_entry_t *entry)
536+
{
537+
_Py_hashtable_t *traces;
538+
_Py_HASHTABLE_ENTRY_READ_DATA(domains, entry, traces);
539+
_Py_hashtable_destroy(traces);
534540
}
535541

536542

537-
/* Convert tracemalloc_traces from compact key (uintptr_t) to pointer_t key.
538-
* Return 0 on success, -1 on error. */
539-
static int
540-
tracemalloc_use_domain(void)
543+
static _Py_hashtable_t*
544+
tracemalloc_create_domains_table(void)
541545
{
542-
_Py_hashtable_t *new_traces = NULL;
543-
544-
assert(!tracemalloc_config.use_domain);
545-
546-
new_traces = hashtable_new(sizeof(pointer_t),
547-
sizeof(trace_t),
548-
hashtable_hash_pointer_t,
549-
hashtable_compare_pointer_t);
550-
if (new_traces == NULL) {
551-
return -1;
552-
}
553-
554-
if (_Py_hashtable_foreach(tracemalloc_traces, tracemalloc_use_domain_cb,
555-
new_traces) < 0)
556-
{
557-
_Py_hashtable_destroy(new_traces);
558-
return -1;
559-
}
560-
561-
_Py_hashtable_destroy(tracemalloc_traces);
562-
tracemalloc_traces = new_traces;
563-
564-
tracemalloc_config.use_domain = 1;
565-
566-
return 0;
546+
return hashtable_new(sizeof(_Py_hashtable_t *),
547+
hashtable_hash_uint,
548+
_Py_hashtable_compare_direct,
549+
tracemalloc_destroy_domain_table);
567550
}
568551

569552

570553
static void
571554
tracemalloc_remove_trace(_PyTraceMalloc_domain_t domain, uintptr_t ptr)
572555
{
573-
trace_t trace;
574-
int removed;
556+
_Py_hashtable_destroy(domains);
557+
}
575558

576559
assert(tracemalloc_config.tracing);
577560

@@ -1002,11 +985,13 @@ tracemalloc_init(void)
1002985

1003986
tracemalloc_filenames = hashtable_new(0,
1004987
hashtable_hash_pyobject,
1005-
hashtable_compare_unicode);
988+
hashtable_compare_unicode,
989+
NULL);
1006990

1007991
tracemalloc_tracebacks = hashtable_new(0,
1008992
hashtable_hash_traceback,
1009-
hashtable_compare_traceback);
993+
hashtable_compare_traceback,
994+
NULL);
1010995

1011996
if (tracemalloc_config.use_domain) {
1012997
tracemalloc_traces = hashtable_new(sizeof(pointer_t),
@@ -1324,14 +1309,30 @@ tracemalloc_get_traces_fill(_Py_hashtable_t *traces, _Py_hashtable_entry_t *entr
13241309

13251310

13261311
static int
1312+
tracemalloc_get_traces_domain(_Py_hashtable_t *domains,
1313+
_Py_hashtable_entry_t *entry,
1314+
void *user_data)
1315+
{
1316+
get_traces_t *get_traces = user_data;
1317+
1318+
unsigned int domain = (unsigned int)FROM_PTR(entry->key);
1319+
_Py_hashtable_t *traces;
1320+
_Py_HASHTABLE_ENTRY_READ_DATA(domains, entry, traces);
1321+
1322+
get_traces->domain = domain;
1323+
return _Py_hashtable_foreach(traces,
1324+
tracemalloc_get_traces_fill,
1325+
get_traces);
1326+
}
1327+
1328+
1329+
static void
13271330
tracemalloc_pyobject_decref_cb(_Py_hashtable_t *tracebacks,
1328-
_Py_hashtable_entry_t *entry,
1329-
void *user_data)
1331+
_Py_hashtable_entry_t *entry)
13301332
{
13311333
PyObject *obj;
13321334
_Py_HASHTABLE_ENTRY_READ_DATA(tracebacks, entry, obj);
13331335
Py_DECREF(obj);
1334-
return 0;
13351336
}
13361337

13371338

@@ -1364,7 +1365,8 @@ py_tracemalloc_get_traces(PyObject *self, PyObject *obj)
13641365
get_traces.tracebacks = hashtable_new(sizeof(traceback_t *),
13651366
sizeof(PyObject *),
13661367
_Py_hashtable_hash_ptr,
1367-
_Py_hashtable_compare_direct);
1368+
_Py_hashtable_compare_direct,
1369+
tracemalloc_pyobject_decref_cb);
13681370
if (get_traces.tracebacks == NULL) {
13691371
PyErr_NoMemory();
13701372
goto error;
@@ -1393,8 +1395,6 @@ py_tracemalloc_get_traces(PyObject *self, PyObject *obj)
13931395

13941396
finally:
13951397
if (get_traces.tracebacks != NULL) {
1396-
_Py_hashtable_foreach(get_traces.tracebacks,
1397-
tracemalloc_pyobject_decref_cb, NULL);
13981398
_Py_hashtable_destroy(get_traces.tracebacks);
13991399
}
14001400
if (get_traces.traces != NULL) {

Modules/hashtable.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,21 @@ typedef struct {
2929
/* data (data_size bytes) follows */
3030
} _Py_hashtable_entry_t;
3131

32-
#define _Py_HASHTABLE_ENTRY_PDATA(TABLE, ENTRY) \
32+
#define _Py_HASHTABLE_ENTRY_PDATA(ENTRY) \
3333
((const void *)((char *)(ENTRY) \
3434
+ sizeof(_Py_hashtable_entry_t)))
3535

3636
#define _Py_HASHTABLE_ENTRY_READ_DATA(TABLE, ENTRY, DATA) \
3737
do { \
3838
assert(sizeof(DATA) == (TABLE)->data_size); \
39-
memcpy(&(DATA), _Py_HASHTABLE_ENTRY_PDATA(TABLE, (ENTRY)), \
39+
memcpy(&(DATA), _Py_HASHTABLE_ENTRY_PDATA((ENTRY)), \
4040
sizeof(DATA)); \
4141
} while (0)
4242

4343
#define _Py_HASHTABLE_ENTRY_WRITE_DATA(TABLE, ENTRY, DATA) \
4444
do { \
4545
assert(sizeof(DATA) == (TABLE)->data_size); \
46-
memcpy((void *)_Py_HASHTABLE_ENTRY_PDATA((TABLE), (ENTRY)), \
46+
memcpy((void *)_Py_HASHTABLE_ENTRY_PDATA(ENTRY), \
4747
&(DATA), sizeof(DATA)); \
4848
} while (0)
4949

@@ -56,6 +56,9 @@ typedef struct _Py_hashtable_t _Py_hashtable_t;
5656

5757
typedef Py_uhash_t (*_Py_hashtable_hash_func) (const void *pkey);
5858
typedef int (*_Py_hashtable_compare_func) (const void *key1, const void *key2);
59+
typedef void (*_Py_hashtable_destroy_func) (void *key);
60+
typedef void (*_Py_hashtable_value_destroy_func) (_Py_hashtable_t *ht,
61+
_Py_hashtable_entry_t *entry);
5962
typedef _Py_hashtable_entry_t* (*_Py_hashtable_get_entry_func)(_Py_hashtable_t *ht,
6063
const void *key);
6164
typedef int (*_Py_hashtable_get_func) (_Py_hashtable_t *ht,
@@ -81,6 +84,8 @@ struct _Py_hashtable_t {
8184
_Py_hashtable_get_entry_func get_entry_func;
8285
_Py_hashtable_hash_func hash_func;
8386
_Py_hashtable_compare_func compare_func;
87+
_Py_hashtable_destroy_func key_destroy_func;
88+
_Py_hashtable_value_destroy_func value_destroy_func;
8489
_Py_hashtable_allocator_t alloc;
8590
};
8691

@@ -102,6 +107,8 @@ PyAPI_FUNC(_Py_hashtable_t *) _Py_hashtable_new_full(
102107
size_t init_size,
103108
_Py_hashtable_hash_func hash_func,
104109
_Py_hashtable_compare_func compare_func,
110+
_Py_hashtable_destroy_func key_destroy_func,
111+
_Py_hashtable_value_destroy_func value_destroy_func,
105112
_Py_hashtable_allocator_t *allocator);
106113

107114
PyAPI_FUNC(void) _Py_hashtable_destroy(_Py_hashtable_t *ht);

Python/hashtable.c

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,14 @@
6464
#define ENTRY_READ_PDATA(TABLE, ENTRY, DATA_SIZE, PDATA) \
6565
do { \
6666
assert((DATA_SIZE) == (TABLE)->data_size); \
67-
memcpy((PDATA), _Py_HASHTABLE_ENTRY_PDATA(TABLE, (ENTRY)), \
67+
memcpy((PDATA), _Py_HASHTABLE_ENTRY_PDATA(ENTRY), \
6868
(DATA_SIZE)); \
6969
} while (0)
7070

7171
#define ENTRY_WRITE_PDATA(TABLE, ENTRY, DATA_SIZE, PDATA) \
7272
do { \
7373
assert((DATA_SIZE) == (TABLE)->data_size); \
74-
memcpy((void *)_Py_HASHTABLE_ENTRY_PDATA((TABLE), (ENTRY)), \
74+
memcpy((void *)_Py_HASHTABLE_ENTRY_PDATA(ENTRY), \
7575
(PDATA), (DATA_SIZE)); \
7676
} while (0)
7777

@@ -432,6 +432,8 @@ _Py_hashtable_t *
432432
_Py_hashtable_new_full(size_t data_size, size_t init_size,
433433
_Py_hashtable_hash_func hash_func,
434434
_Py_hashtable_compare_func compare_func,
435+
_Py_hashtable_destroy_func key_destroy_func,
436+
_Py_hashtable_value_destroy_func value_destroy_func,
435437
_Py_hashtable_allocator_t *allocator)
436438
{
437439
_Py_hashtable_t *ht;
@@ -466,6 +468,8 @@ _Py_hashtable_new_full(size_t data_size, size_t init_size,
466468
ht->get_entry_func = _Py_hashtable_get_entry_generic;
467469
ht->hash_func = hash_func;
468470
ht->compare_func = compare_func;
471+
ht->key_destroy_func = key_destroy_func;
472+
ht->value_destroy_func = value_destroy_func;
469473
ht->alloc = alloc;
470474
if (ht->hash_func == _Py_Modules/hashtable_hash_ptr
471475
&& ht->compare_func == _Py_hashtable_compare_direct)
@@ -484,7 +488,7 @@ _Py_hashtable_new(size_t data_size,
484488
{
485489
return _Py_hashtable_new_full(data_size, HASHTABLE_MIN_SIZE,
486490
hash_func, compare_func,
487-
NULL);
491+
NULL, NULL, NULL);
488492
}
489493

490494

@@ -506,16 +510,27 @@ _Py_hashtable_clear(_Py_hashtable_t *ht)
506510
}
507511

508512

513+
static void
514+
_Py_hashtable_destroy_entry(_Py_hashtable_t *ht, _Py_hashtable_entry_t *entry)
515+
{
516+
if (ht->key_destroy_func) {
517+
ht->key_destroy_func(entry->key);
518+
}
519+
if (ht->value_destroy_func) {
520+
ht->value_destroy_func(ht, entry);
521+
}
522+
ht->alloc.free(entry);
523+
}
524+
525+
509526
void
510527
_Py_hashtable_destroy(_Py_hashtable_t *ht)
511528
{
512-
size_t i;
513-
514-
for (i = 0; i < ht->num_buckets; i++) {
515-
_Py_slist_item_t *entry = ht->buckets[i].head;
529+
for (size_t i = 0; i < ht->num_buckets; i++) {
530+
_Py_hashtable_entry_t *entry = TABLE_HEAD(ht, i);
516531
while (entry) {
517-
_Py_slist_item_t *entry_next = entry->next;
518-
ht->alloc.free(entry);
532+
_Py_hashtable_entry_t *entry_next = ENTRY_NEXT(entry);
533+
_Py_hashtable_destroy_entry(ht, entry);
519534
entry = entry_next;
520535
}
521536
}
@@ -537,6 +552,8 @@ _Py_hashtable_copy(_Py_hashtable_t *src)
537552
dst = _Py_hashtable_new_full(data_size, src->num_buckets,
538553
src->hash_func,
539554
src->compare_func,
555+
src->key_destroy_func,
556+
src->value_destroy_func,
540557
&src->alloc);
541558
if (dst == NULL)
542559
return NULL;
@@ -545,7 +562,7 @@ _Py_hashtable_copy(_Py_hashtable_t *src)
545562
entry = TABLE_HEAD(src, bucket);
546563
for (; entry; entry = ENTRY_NEXT(entry)) {
547564
const void *key = entry->key;
548-
const void *pdata = _Py_HASHTABLE_ENTRY_PDATA(src, entry);
565+
const void *pdata = _Py_HASHTABLE_ENTRY_PDATA(entry);
549566
err = _Py_hashtable_set(dst, key, data_size, pdata);
550567
if (err) {
551568
_Py_hashtable_destroy(dst);

Python/marshal.c

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -570,13 +570,21 @@ w_complex_object(PyObject *v, char flag, WFILE *p)
570570
}
571571
}
572572

573+
static void
574+
w_decref_entry(void *key)
575+
{
576+
PyObject *entry_key = (PyObject *)key;
577+
Py_XDECREF(entry_key);
578+
}
579+
573580
static int
574581
w_init_refs(WFILE *wf, int version)
575582
{
576583
if (version >= 3) {
577-
wf->hashtable = _Py_hashtable_new(sizeof(int),
578-
_Py_hashtable_hash_ptr,
579-
_Py_hashtable_compare_direct);
584+
wf->hashtable = _Py_hashtable_new_full(sizeof(int), 0,
585+
_Py_hashtable_hash_ptr,
586+
_Py_hashtable_compare_direct,
587+
w_decref_entry, NULL, NULL);
580588
if (wf->hashtable == NULL) {
581589
PyErr_NoMemory();
582590
return -1;
@@ -585,20 +593,10 @@ w_init_refs(WFILE *wf, int version)
585593
return 0;
586594
}
587595

588-
static int
589-
w_decref_entry(_Py_hashtable_t *ht, _Py_hashtable_entry_t *entry,
590-
void *Py_UNUSED(data))
591-
{
592-
PyObject *entry_key = (PyObject *)entry->key;
593-
Py_XDECREF(entry_key);
594-
return 0;
595-
}
596-
597596
static void
598597
w_clear_refs(WFILE *wf)
599598
{
600599
if (wf->hashtable != NULL) {
601-
_Py_hashtable_foreach(wf->hashtable, w_decref_entry, NULL);
602600
_Py_hashtable_destroy(wf->hashtable);
603601
}
604602
}

0 commit comments

Comments
 (0)