|
27 | 27 | /* Macro that jumps to error, if the expression `x` does not succeed. */ |
28 | 28 | #define SUCCEEDS(x) do { int r = (x); if (r != 0) goto error; } while (0) |
29 | 29 |
|
| 30 | +#define Region_Check(x) Py_IS_TYPE((x), &_PyTracingRegion_Type) |
| 31 | + |
30 | 32 | // ################################################################### |
31 | 33 | // Copied from gc.c |
32 | 34 | // ################################################################### |
@@ -296,10 +298,93 @@ get_reachable_proc(PyTypeObject *tp) |
296 | 298 | static void |
297 | 299 | gc_list_dissolve(PyGC_Head *list) { |
298 | 300 | struct _gc_runtime_state* gc_state = get_gc_state(); |
299 | | - //gc_list_merge(list, &(gc_state->young.head)); |
300 | 301 | gc_list_merge(list, &(gc_state->old[0].head)); |
301 | 302 | } |
302 | 303 |
|
| 304 | +static void detach_weak_refs(PyGC_Head *gc_list) { |
| 305 | + PyGC_Head *current = GC_NEXT(gc_list); |
| 306 | + while (current != gc_list) { |
| 307 | + PyObject *item = _Py_FROM_GC(current); |
| 308 | +#ifdef PY_DEBUG |
| 309 | + Py_ssize_t weak_ctn = _PyWeakref_GetWeakrefCount(item); |
| 310 | + if (weak_ctn) { |
| 311 | + trace("- Clearing %zd weak references to %p", weak_ctn, item); |
| 312 | + } |
| 313 | +#endif |
| 314 | + if (_PyType_SUPPORTS_WEAKREFS(Py_TYPE(item))) { |
| 315 | + _PyWeakref_ClearWeakRefsNoCallbacks(item); |
| 316 | + } |
| 317 | + |
| 318 | + current = GC_NEXT(current); |
| 319 | + } |
| 320 | +} |
| 321 | + |
| 322 | +typedef struct { |
| 323 | + PyObject_HEAD |
| 324 | + PyObject *dict; |
| 325 | + // The GC list containing all objects while the region is closed. The bridge |
| 326 | + // object is not in this GC list but in the list of the owning region or in no |
| 327 | + // list if it's owned by a released cown. |
| 328 | + PyGC_Head gc_list; |
| 329 | + // FIXME(regions): This can be inferred from the status of the gc_list |
| 330 | + // or stored in the lower bits of the GC list. For now we keep it separate |
| 331 | + // for the prototype |
| 332 | + bool open; |
| 333 | + // This is the number of references from inside the region that reference |
| 334 | + // this bridge object. |
| 335 | + Py_ssize_t internal_bridge_refs; |
| 336 | +} TracingRegionObject; |
| 337 | + |
| 338 | +const int PER_REGION_TRACE_LIMIT = 2; |
| 339 | + |
| 340 | +typedef struct { |
| 341 | + // This is the stack of pending regions needing to be closed to close |
| 342 | + // this region tree. Objects will be inqueued `PER_REGION_TRACE_LIMIT` |
| 343 | + // times. It the region is not closed when it hits the limit, the closing |
| 344 | + // will fail. |
| 345 | + PyObject *pending; |
| 346 | + // This tracks per region in the tree how often it has been traversed. |
| 347 | + // Some things require the trace to be redone, namely freezing an object |
| 348 | + // as that may create references and finding an open sub-region, as that |
| 349 | + // one needs to be traced and closed first. |
| 350 | + // |
| 351 | + // We limit the number of times we restart the trace per region. |
| 352 | + // Theoretically, this may reject some programs that would eventually |
| 353 | + // reach a fixed point, but if somebody wants to do dark magic, that's |
| 354 | + // really not our problem. |
| 355 | + _Py_hashtable_t *traceing_counts; |
| 356 | +} tree_trace_state_t; |
| 357 | + |
| 358 | +static void tree_trace_state_destroy(tree_trace_state_t* state) { |
| 359 | + if (state->traceing_counts) { |
| 360 | + _Py_hashtable_destroy(state->traceing_counts); |
| 361 | + state->traceing_counts = NULL; |
| 362 | + } |
| 363 | + if (state->pending) { |
| 364 | + Py_CLEAR(state->pending); |
| 365 | + } |
| 366 | +} |
| 367 | + |
| 368 | +static int tree_trace_state_init(tree_trace_state_t* state) { |
| 369 | + state->traceing_counts = _Py_hashtable_new( |
| 370 | + _Py_hashtable_hash_ptr, |
| 371 | + _Py_hashtable_compare_direct); |
| 372 | + if (state->traceing_counts == NULL) { |
| 373 | + goto error; |
| 374 | + } |
| 375 | + |
| 376 | + state->pending = PyList_New(0); |
| 377 | + if (state->pending == NULL) { |
| 378 | + goto error; |
| 379 | + } |
| 380 | + |
| 381 | + return 0; |
| 382 | +error: |
| 383 | + trace_state_destroy(state); |
| 384 | + return -1; |
| 385 | +} |
| 386 | + |
| 387 | +// TODO: Continue Migration |
303 | 388 |
|
304 | 389 | typedef struct { |
305 | 390 | // These are the objects with incoming references, that |
@@ -751,41 +836,26 @@ static int trace_object(PyObject* obj, trace_info_t* result, PyGC_Head *gc_list) |
751 | 836 | return res; |
752 | 837 | } |
753 | 838 |
|
754 | | -static void detach_weak_refs(PyGC_Head *gc_list) { |
755 | | - PyGC_Head *current = GC_NEXT(gc_list); |
756 | | - while (current != gc_list) { |
757 | | - PyObject *item = _Py_FROM_GC(current); |
758 | | -#ifdef PY_DEBUG |
759 | | - Py_ssize_t weak_ctn = _PyWeakref_GetWeakrefCount(item); |
760 | | - if (weak_ctn) { |
761 | | - trace("- Clearing %zd weak references to %p", weak_ctn, item); |
762 | | - } |
763 | | -#endif |
764 | | - if (_PyType_SUPPORTS_WEAKREFS(Py_TYPE(item))) { |
765 | | - _PyWeakref_ClearWeakRefsNoCallbacks(item); |
766 | | - } |
| 839 | +static int try_close_region_tree(PyObject *root) { |
| 840 | + tree_trace_state_t state; |
| 841 | + if (tree_trace_state_init(&state)) { |
| 842 | + return -1; |
| 843 | + } |
767 | 844 |
|
768 | | - current = GC_NEXT(current); |
| 845 | + while (PyList_GET_SIZE(state.pending) > 0) { |
| 846 | + // Find the next pending item: |
| 847 | + PyObject *region = list_pop(state.pending); |
| 848 | + assert(Region_Check(region)); |
| 849 | + |
| 850 | + |
| 851 | + // TODO: Continue.... |
769 | 852 | } |
770 | 853 | } |
771 | 854 |
|
772 | 855 | // ################################################################### |
773 | 856 | // Region Object |
774 | 857 | // ################################################################### |
775 | 858 |
|
776 | | -typedef struct { |
777 | | - PyObject_HEAD |
778 | | - PyObject *dict; |
779 | | - // The GC list containing all objects while the region is closed. The bridge |
780 | | - // object is not in this GC list but in the list of the owning region or in no |
781 | | - // list if it's owned by a released cown. |
782 | | - PyGC_Head gc_list; |
783 | | - // FIXME(regions): This can be inferred from the status of the gc_list |
784 | | - // or stored in the lower bits of the GC list. For now we keep it separate |
785 | | - // for the prototype |
786 | | - bool open; |
787 | | -} TracingRegionObject; |
788 | | - |
789 | 859 | static int |
790 | 860 | TracingRegion_init(TracingRegionObject *self, PyObject *args, PyObject *kwargs) { |
791 | 861 | gc_list_init(&self->gc_list); |
@@ -894,24 +964,6 @@ TracingRegion_set_dict(PyObject *op, PyObject *value, void *Py_UNUSED(context)) |
894 | 964 | return 0; |
895 | 965 | } |
896 | 966 |
|
897 | | -static PyObject* TracingRegion_trace(PyObject *op) { |
898 | | - trace_info_t result; |
899 | | - if (trace_object(op, &result, NULL)) { |
900 | | - return NULL; // propagate Python exception |
901 | | - } |
902 | | - |
903 | | - if (result.obj_table != NULL) { |
904 | | - _Py_hashtable_destroy(result.obj_table); |
905 | | - } |
906 | | - |
907 | | - PyObject *t = Py_BuildValue("(ii)", result.objs, result.incoming_refs); |
908 | | - if (t == NULL) { |
909 | | - return NULL; // propagate Python exception |
910 | | - } |
911 | | - |
912 | | - return t; |
913 | | -} |
914 | | - |
915 | 967 | // State threaded through `_report_incoming_ref` while building the |
916 | 968 | // "region could not be closed" error message. |
917 | 969 | typedef struct { |
@@ -1051,8 +1103,6 @@ int _PyTracingRegion_IsClosed(PyObject* region) { |
1051 | 1103 | } |
1052 | 1104 |
|
1053 | 1105 | static PyMethodDef TracingRegion_methods[] = { |
1054 | | - {"trace", _PyCFunction_CAST(TracingRegion_trace), METH_NOARGS, |
1055 | | - "This traces the region and returns the number of incoming references"}, |
1056 | 1106 | {NULL, NULL} /* sentinel */ |
1057 | 1107 | }; |
1058 | 1108 |
|
|
0 commit comments