Skip to content

Commit 2d0a3d6

Browse files
authored
bpo-40609: Add destroy functions to _Py_hashtable (GH-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 f9b3b58 commit 2d0a3d6

File tree

4 files changed

+73
-52
lines changed

4 files changed

+73
-52
lines changed

Include/internal/pycore_hashtable.h

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

37-
#define _Py_HASHTABLE_ENTRY_PDATA(TABLE, ENTRY) \
37+
#define _Py_HASHTABLE_ENTRY_PDATA(ENTRY) \
3838
((const void *)((char *)(ENTRY) \
3939
+ sizeof(_Py_hashtable_entry_t)))
4040

4141
#define _Py_HASHTABLE_ENTRY_READ_DATA(TABLE, ENTRY, DATA) \
4242
do { \
4343
assert(sizeof(DATA) == (TABLE)->data_size); \
44-
memcpy(&(DATA), _Py_HASHTABLE_ENTRY_PDATA(TABLE, (ENTRY)), \
44+
memcpy(&(DATA), _Py_HASHTABLE_ENTRY_PDATA((ENTRY)), \
4545
sizeof(DATA)); \
4646
} while (0)
4747

4848
#define _Py_HASHTABLE_ENTRY_WRITE_DATA(TABLE, ENTRY, DATA) \
4949
do { \
5050
assert(sizeof(DATA) == (TABLE)->data_size); \
51-
memcpy((void *)_Py_HASHTABLE_ENTRY_PDATA((TABLE), (ENTRY)), \
51+
memcpy((void *)_Py_HASHTABLE_ENTRY_PDATA(ENTRY), \
5252
&(DATA), sizeof(DATA)); \
5353
} while (0)
5454

@@ -61,6 +61,9 @@ typedef struct _Py_hashtable_t _Py_hashtable_t;
6161

6262
typedef Py_uhash_t (*_Py_hashtable_hash_func) (const void *key);
6363
typedef int (*_Py_hashtable_compare_func) (const void *key1, const void *key2);
64+
typedef void (*_Py_hashtable_destroy_func) (void *key);
65+
typedef void (*_Py_hashtable_value_destroy_func) (_Py_hashtable_t *ht,
66+
_Py_hashtable_entry_t *entry);
6467
typedef _Py_hashtable_entry_t* (*_Py_hashtable_get_entry_func)(_Py_hashtable_t *ht,
6568
const void *key);
6669
typedef int (*_Py_hashtable_get_func) (_Py_hashtable_t *ht,
@@ -86,6 +89,8 @@ struct _Py_hashtable_t {
8689
_Py_hashtable_get_entry_func get_entry_func;
8790
_Py_hashtable_hash_func hash_func;
8891
_Py_hashtable_compare_func compare_func;
92+
_Py_hashtable_destroy_func key_destroy_func;
93+
_Py_hashtable_value_destroy_func value_destroy_func;
8994
_Py_hashtable_allocator_t alloc;
9095
};
9196

@@ -107,6 +112,8 @@ PyAPI_FUNC(_Py_hashtable_t *) _Py_hashtable_new_full(
107112
size_t init_size,
108113
_Py_hashtable_hash_func hash_func,
109114
_Py_hashtable_compare_func compare_func,
115+
_Py_hashtable_destroy_func key_destroy_func,
116+
_Py_hashtable_value_destroy_func value_destroy_func,
110117
_Py_hashtable_allocator_t *allocator);
111118

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

Modules/_tracemalloc.c

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -238,12 +238,13 @@ hashtable_hash_uint(const void *key_raw)
238238
static _Py_hashtable_t *
239239
hashtable_new(size_t data_size,
240240
_Py_hashtable_hash_func hash_func,
241-
_Py_hashtable_compare_func compare_func)
241+
_Py_hashtable_compare_func compare_func,
242+
_Py_hashtable_value_destroy_func value_destroy_fun)
242243
{
243244
_Py_hashtable_allocator_t hashtable_alloc = {malloc, free};
244245
return _Py_hashtable_new_full(data_size, 0,
245246
hash_func, compare_func,
246-
&hashtable_alloc);
247+
NULL, value_destroy_fun, &hashtable_alloc);
247248
}
248249

249250

@@ -471,35 +472,34 @@ tracemalloc_create_traces_table(void)
471472
{
472473
return hashtable_new(sizeof(trace_t),
473474
_Py_hashtable_hash_ptr,
474-
_Py_hashtable_compare_direct);
475+
_Py_hashtable_compare_direct,
476+
NULL);
475477
}
476478

477479

478-
static _Py_hashtable_t*
479-
tracemalloc_create_domains_table(void)
480+
static void
481+
tracemalloc_destroy_domain_table(_Py_hashtable_t *domains,
482+
_Py_hashtable_entry_t *entry)
480483
{
481-
return hashtable_new(sizeof(_Py_hashtable_t *),
482-
hashtable_hash_uint,
483-
_Py_hashtable_compare_direct);
484+
_Py_hashtable_t *traces;
485+
_Py_HASHTABLE_ENTRY_READ_DATA(domains, entry, traces);
486+
_Py_hashtable_destroy(traces);
484487
}
485488

486489

487-
static int
488-
tracemalloc_destroy_domains_cb(_Py_hashtable_t *domains,
489-
_Py_hashtable_entry_t *entry,
490-
void *user_data)
490+
static _Py_hashtable_t*
491+
tracemalloc_create_domains_table(void)
491492
{
492-
_Py_hashtable_t *traces;
493-
_Py_HASHTABLE_ENTRY_READ_DATA(domains, entry, traces);
494-
_Py_hashtable_destroy(traces);
495-
return 0;
493+
return hashtable_new(sizeof(_Py_hashtable_t *),
494+
hashtable_hash_uint,
495+
_Py_hashtable_compare_direct,
496+
tracemalloc_destroy_domain_table);
496497
}
497498

498499

499500
static void
500501
tracemalloc_destroy_domains(_Py_hashtable_t *domains)
501502
{
502-
_Py_hashtable_foreach(domains, tracemalloc_destroy_domains_cb, NULL);
503503
_Py_hashtable_destroy(domains);
504504
}
505505

@@ -924,11 +924,13 @@ tracemalloc_init(void)
924924

925925
tracemalloc_filenames = hashtable_new(0,
926926
hashtable_hash_pyobject,
927-
hashtable_compare_unicode);
927+
hashtable_compare_unicode,
928+
NULL);
928929

929930
tracemalloc_tracebacks = hashtable_new(0,
930931
hashtable_hash_traceback,
931-
hashtable_compare_traceback);
932+
hashtable_compare_traceback,
933+
NULL);
932934

933935
tracemalloc_traces = tracemalloc_create_traces_table();
934936
tracemalloc_domains = tracemalloc_create_domains_table();
@@ -1285,15 +1287,13 @@ tracemalloc_get_traces_domain(_Py_hashtable_t *domains,
12851287
}
12861288

12871289

1288-
static int
1290+
static void
12891291
tracemalloc_pyobject_decref_cb(_Py_hashtable_t *tracebacks,
1290-
_Py_hashtable_entry_t *entry,
1291-
void *user_data)
1292+
_Py_hashtable_entry_t *entry)
12921293
{
12931294
PyObject *obj;
12941295
_Py_HASHTABLE_ENTRY_READ_DATA(tracebacks, entry, obj);
12951296
Py_DECREF(obj);
1296-
return 0;
12971297
}
12981298

12991299

@@ -1329,7 +1329,8 @@ _tracemalloc__get_traces_impl(PyObject *module)
13291329
of (filename, lineno) tuples */
13301330
get_traces.tracebacks = hashtable_new(sizeof(PyObject *),
13311331
_Py_hashtable_hash_ptr,
1332-
_Py_hashtable_compare_direct);
1332+
_Py_hashtable_compare_direct,
1333+
tracemalloc_pyobject_decref_cb);
13331334
if (get_traces.tracebacks == NULL) {
13341335
goto no_memory;
13351336
}
@@ -1381,8 +1382,6 @@ _tracemalloc__get_traces_impl(PyObject *module)
13811382

13821383
finally:
13831384
if (get_traces.tracebacks != NULL) {
1384-
_Py_hashtable_foreach(get_traces.tracebacks,
1385-
tracemalloc_pyobject_decref_cb, NULL);
13861385
_Py_hashtable_destroy(get_traces.tracebacks);
13871386
}
13881387
if (get_traces.traces != NULL) {

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_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
@@ -545,13 +545,21 @@ w_complex_object(PyObject *v, char flag, WFILE *p)
545545
}
546546
}
547547

548+
static void
549+
w_decref_entry(void *key)
550+
{
551+
PyObject *entry_key = (PyObject *)key;
552+
Py_XDECREF(entry_key);
553+
}
554+
548555
static int
549556
w_init_refs(WFILE *wf, int version)
550557
{
551558
if (version >= 3) {
552-
wf->hashtable = _Py_hashtable_new(sizeof(int),
553-
_Py_hashtable_hash_ptr,
554-
_Py_hashtable_compare_direct);
559+
wf->hashtable = _Py_hashtable_new_full(sizeof(int), 0,
560+
_Py_hashtable_hash_ptr,
561+
_Py_hashtable_compare_direct,
562+
w_decref_entry, NULL, NULL);
555563
if (wf->hashtable == NULL) {
556564
PyErr_NoMemory();
557565
return -1;
@@ -560,20 +568,10 @@ w_init_refs(WFILE *wf, int version)
560568
return 0;
561569
}
562570

563-
static int
564-
w_decref_entry(_Py_hashtable_t *ht, _Py_hashtable_entry_t *entry,
565-
void *Py_UNUSED(data))
566-
{
567-
PyObject *entry_key = (PyObject *)entry->key;
568-
Py_XDECREF(entry_key);
569-
return 0;
570-
}
571-
572571
static void
573572
w_clear_refs(WFILE *wf)
574573
{
575574
if (wf->hashtable != NULL) {
576-
_Py_hashtable_foreach(wf->hashtable, w_decref_entry, NULL);
577575
_Py_hashtable_destroy(wf->hashtable);
578576
}
579577
}

0 commit comments

Comments
 (0)