bpo-38379: Don't block collection of unreachable objects when some ob… · python/cpython@466326d (original) (raw)

`@@ -301,8 +301,18 @@ gc_list_size(PyGC_Head *list)

`

301

301

`return n;

`

302

302

`}

`

303

303

``

``

304

`+

/* Walk the list and mark all objects as non-collecting */

`

``

305

`+

static inline void

`

``

306

`+

gc_list_clear_collecting(PyGC_Head *collectable)

`

``

307

`+

{

`

``

308

`+

PyGC_Head *gc;

`

``

309

`+

for (gc = GC_NEXT(collectable); gc != collectable; gc = GC_NEXT(gc)) {

`

``

310

`+

gc_clear_collecting(gc);

`

``

311

`+

}

`

``

312

`+

}

`

``

313

+

304

314

`/* Append objects in a GC list to a Python list.

`

305

``

`-

`

``

315

`+

`

306

316

` */

`

307

317

`static int

`

308

318

`append_objects(PyObject *py_list, PyGC_Head *gc_list)

`

`@@ -613,6 +623,22 @@ move_legacy_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers)

`

613

623

` }

`

614

624

`}

`

615

625

``

``

626

`+

static inline void

`

``

627

`+

clear_unreachable_mask(PyGC_Head *unreachable)

`

``

628

`+

{

`

``

629

`+

/* Check that the list head does not have the unreachable bit set */

`

``

630

`+

assert(((uintptr_t)unreachable & NEXT_MASK_UNREACHABLE) == 0);

`

``

631

+

``

632

`+

PyGC_Head *gc, *next;

`

``

633

`+

unreachable->_gc_next &= ~NEXT_MASK_UNREACHABLE;

`

``

634

`+

for (gc = GC_NEXT(unreachable); gc != unreachable; gc = next) {

`

``

635

`+

_PyObject_ASSERT((PyObject*)FROM_GC(gc), gc->_gc_next & NEXT_MASK_UNREACHABLE);

`

``

636

`+

gc->_gc_next &= ~NEXT_MASK_UNREACHABLE;

`

``

637

`+

next = (PyGC_Head*)gc->_gc_next;

`

``

638

`+

}

`

``

639

`+

validate_list(unreachable, PREV_MASK_COLLECTING);

`

``

640

`+

}

`

``

641

+

616

642

`/* A traversal callback for move_legacy_finalizer_reachable. */

`

617

643

`static int

`

618

644

`visit_move(PyObject *op, PyGC_Head *tolist)

`

`@@ -891,36 +917,6 @@ finalize_garbage(PyGC_Head *collectable)

`

891

917

`gc_list_merge(&seen, collectable);

`

892

918

`}

`

893

919

``

894

``

`-

/* Walk the collectable list and check that they are really unreachable

`

895

``

`-

from the outside (some objects could have been resurrected by a

`

896

``

`-

finalizer). */

`

897

``

`-

static int

`

898

``

`-

check_garbage(PyGC_Head *collectable)

`

899

``

`-

{

`

900

``

`-

int ret = 0;

`

901

``

`-

PyGC_Head *gc;

`

902

``

`-

for (gc = GC_NEXT(collectable); gc != collectable; gc = GC_NEXT(gc)) {

`

903

``

`-

// Use gc_refs and break gc_prev again.

`

904

``

`-

gc_set_refs(gc, Py_REFCNT(FROM_GC(gc)));

`

905

``

`-

_PyObject_ASSERT(FROM_GC(gc), gc_get_refs(gc) != 0);

`

906

``

`-

}

`

907

``

`-

subtract_refs(collectable);

`

908

``

`-

PyGC_Head *prev = collectable;

`

909

``

`-

for (gc = GC_NEXT(collectable); gc != collectable; gc = GC_NEXT(gc)) {

`

910

``

`-

_PyObject_ASSERT_WITH_MSG(FROM_GC(gc),

`

911

``

`-

gc_get_refs(gc) >= 0,

`

912

``

`-

"refcount is too small");

`

913

``

`-

if (gc_get_refs(gc) != 0) {

`

914

``

`-

ret = -1;

`

915

``

`-

}

`

916

``

`-

// Restore gc_prev here.

`

917

``

`-

_PyGCHead_SET_PREV(gc, prev);

`

918

``

`-

gc_clear_collecting(gc);

`

919

``

`-

prev = gc;

`

920

``

`-

}

`

921

``

`-

return ret;

`

922

``

`-

}

`

923

``

-

924

920

`/* Break reference cycles by clearing the containers involved. This is

`

925

921

` * tricky business as the lists can be changing and we don't know which

`

926

922

` * objects may be freed. It is possible I screwed something up here.

`

`@@ -958,6 +954,7 @@ delete_garbage(struct _gc_runtime_state *state,

`

958

954

` }

`

959

955

`if (GC_NEXT(collectable) == gc) {

`

960

956

`/* object is still alive, move it, it may die later */

`

``

957

`+

gc_clear_collecting(gc);

`

961

958

`gc_list_move(gc, old);

`

962

959

` }

`

963

960

` }

`

`@@ -1003,6 +1000,87 @@ show_stats_each_generations(struct _gc_runtime_state *state)

`

1003

1000

`buf, gc_list_size(&state->permanent_generation.head));

`

1004

1001

`}

`

1005

1002

``

``

1003

`+

/* Deduce wich objects among "base" are unreachable from outside the list

`

``

1004

`+

and move them to 'unreachable'. The process consist in the following steps:

`

``

1005

+

``

1006

`+

  1. Copy all reference counts to a different field (gc_prev is used to hold

`

``

1007

`+

this copy to save memory).

`

``

1008

`+

  1. Traverse all objects in "base" and visit all referred objects using

`

``

1009

`+

"tp_traverse" and for every visited object, substract 1 to the reference

`

``

1010

`+

count (the one that we copied in the previous step). After this step, all

`

``

1011

`+

objects that can be reached directly from outside must have strictly positive

`

``

1012

`+

reference count, while all unreachable objects must have a count of exactly 0.

`

``

1013

`+

  1. Indentify all unreachable objects (the ones with 0 reference count) and move

`

``

1014

`+

them to the "unreachable" list. This step also needs to move back to "base" all

`

``

1015

`+

objects that were initially marked as unreachable but are referred transitively

`

``

1016

`+

by the reachable objects (the ones with strictly positive reference count).

`

``

1017

+

``

1018

`+

Contracts:

`

``

1019

+

``

1020

`+

`

``

1021

+

``

1022

`+

`

``

1023

`+

gc_list_init over 'unreachable').

`

``

1024

+

``

1025

`+

IMPORTANT: This function leaves 'unreachable' with the NEXT_MASK_UNREACHABLE

`

``

1026

`+

flag set but it does not clear it to skip unnecessary iteration. Before the

`

``

1027

`+

flag is cleared (for example, by using 'clear_unreachable_mask' function or

`

``

1028

`+

by a call to 'move_legacy_finalizers'), the 'unreachable' list is not a normal

`

``

1029

`+

list and we can not use most gc_list_* functions for it. */

`

``

1030

`+

static inline void

`

``

1031

`+

deduce_unreachable(PyGC_Head *base, PyGC_Head *unreachable) {

`

``

1032

`+

validate_list(base, 0);

`

``

1033

`+

/* Using ob_refcnt and gc_refs, calculate which objects in the

`

``

1034

`+

`

``

1035

`+

`

``

1036

`+

`

``

1037

`+

*/

`

``

1038

`+

update_refs(base); // gc_prev is used for gc_refs

`

``

1039

`+

subtract_refs(base);

`

``

1040

+

``

1041

`+

/* Leave everything reachable from outside base in base, and move

`

``

1042

`+

`

``

1043

`+

`

``

1044

`+

`

``

1045

`+

`

``

1046

`+

*/

`

``

1047

`+

gc_list_init(unreachable);

`

``

1048

`+

move_unreachable(base, unreachable); // gc_prev is pointer again

`

``

1049

`+

validate_list(base, 0);

`

``

1050

`+

}

`

``

1051

+

``

1052

`+

/* Handle objects that may have resurrected after a call to 'finalize_garbage', moving

`

``

1053

`+

them to 'old_generation' and placing the rest on 'still_unreachable'.

`

``

1054

+

``

1055

`+

Contracts:

`

``

1056

`+

`

``

1057

`+

will contain the objects that did not resurrect.

`

``

1058

+

``

1059

`+

`

``

1060

`+

gc_list_init over 'still_unreachable').

`

``

1061

+

``

1062

`+

IMPORTANT: After a call to this function, the 'still_unreachable' set will have the

`

``

1063

`+

PREV_MARK_COLLECTING set, but the objects in this set are going to be removed so

`

``

1064

`+

we can skip the expense of clearing the flag to avoid extra iteration. */

`

``

1065

`+

static inline void

`

``

1066

`+

handle_resurrected_objects(PyGC_Head unreachable, PyGC_Head still_unreachable,

`

``

1067

`+

PyGC_Head *old_generation)

`

``

1068

`+

{

`

``

1069

`+

// Remove the PREV_MASK_COLLECTING from unreachable

`

``

1070

`+

// to prepare it for a new call to 'deduce_unreachable'

`

``

1071

`+

gc_list_clear_collecting(unreachable);

`

``

1072

+

``

1073

`+

// After the call to deduce_unreachable, the 'still_unreachable' set will

`

``

1074

`+

// have the PREV_MARK_COLLECTING set, but the objects are going to be

`

``

1075

`+

// removed so we can skip the expense of clearing the flag.

`

``

1076

`+

PyGC_Head* resurrected = unreachable;

`

``

1077

`+

deduce_unreachable(resurrected, still_unreachable);

`

``

1078

`+

clear_unreachable_mask(still_unreachable);

`

``

1079

+

``

1080

`+

// Move the resurrected objects to the old generation for future collection.

`

``

1081

`+

gc_list_merge(resurrected, old_generation);

`

``

1082

`+

}

`

``

1083

+

1006

1084

`/* This is the main function. Read this to understand how the

`

1007

1085

` * collection process works. */

`

1008

1086

`static Py_ssize_t

`

`@@ -1045,26 +1123,9 @@ collect(struct _gc_runtime_state *state, int generation,

`

1045

1123

`old = GEN_HEAD(state, generation+1);

`

1046

1124

`else

`

1047

1125

`old = young;

`

1048

``

-

1049

``

`-

validate_list(young, 0);

`

1050

1126

`validate_list(old, 0);

`

1051

``

`-

/* Using ob_refcnt and gc_refs, calculate which objects in the

`

1052

``

`-

`

1053

``

`-

`

1054

``

`-

`

1055

``

`-

*/

`

1056

``

`-

update_refs(young); // gc_prev is used for gc_refs

`

1057

``

`-

subtract_refs(young);

`

1058

1127

``

1059

``

`-

/* Leave everything reachable from outside young in young, and move

`

1060

``

`-

`

1061

``

`-

`

1062

``

`-

`

1063

``

`-

`

1064

``

`-

*/

`

1065

``

`-

gc_list_init(&unreachable);

`

1066

``

`-

move_unreachable(young, &unreachable); // gc_prev is pointer again

`

1067

``

`-

validate_list(young, 0);

`

``

1128

`+

deduce_unreachable(young, &unreachable);

`

1068

1129

``

1069

1130

`untrack_tuples(young);

`

1070

1131

`/* Move reachable objects to next generation. */

`

`@@ -1114,17 +1175,18 @@ collect(struct _gc_runtime_state *state, int generation,

`

1114

1175

`/* Call tp_finalize on objects which have one. */

`

1115

1176

`finalize_garbage(&unreachable);

`

1116

1177

``

1117

``

`-

if (check_garbage(&unreachable)) { // clear PREV_MASK_COLLECTING here

`

1118

``

`-

gc_list_merge(&unreachable, old);

`

1119

``

`-

}

`

1120

``

`-

else {

`

1121

``

`-

/* Call tp_clear on objects in the unreachable set. This will cause

`

1122

``

`-

`

1123

``

`-

`

1124

``

`-

*/

`

1125

``

`-

m += gc_list_size(&unreachable);

`

1126

``

`-

delete_garbage(state, &unreachable, old);

`

1127

``

`-

}

`

``

1178

`+

/* Handle any objects that may have resurrected after the call

`

``

1179

`+

`

``

1180

`+

`

``

1181

`+

PyGC_Head final_unreachable;

`

``

1182

`+

handle_resurrected_objects(&unreachable, &final_unreachable, old);

`

``

1183

+

``

1184

`+

/* Call tp_clear on objects in the final_unreachable set. This will cause

`

``

1185

`+

`

``

1186

`+

`

``

1187

`+

*/

`

``

1188

`+

m += gc_list_size(&final_unreachable);

`

``

1189

`+

delete_garbage(state, &final_unreachable, old);

`

1128

1190

``

1129

1191

`/* Collect statistics on uncollectable objects found and print

`

1130

1192

` * debugging information. */

`