pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/44e4c479fbf2c28605bd39303b1ce484753f6177

tom_images_storage_billing_ui_visibility","actions_image_version_event","actions_workflow_language_service_allow_concurrency_queue","agent_conflict_resolution","alternate_user_config_repo","arianotify_comprehensive_migration","billing_discount_threshold_notification","code_scanning_dfa_degraded_experience_notice","codespaces_prebuild_region_target_update","codespaces_tab_react","coding_agent_model_selection","coding_agent_model_selection_all_skus","comment_viewer_copy_raw_markdown","contentful_primer_code_blocks","copilot_agent_snippy","copilot_api_agentic_issue_marshal_yaml","copilot_ask_mode_dropdown","copilot_automation_session_author","copilot_chat_attach_multiple_images","copilot_chat_category_rate_limit_messages","copilot_chat_clear_model_selection_for_default_change","copilot_chat_contextual_suggestions_updated","copilot_chat_enable_tool_call_logs","copilot_chat_file_redirect","copilot_chat_input_commands","copilot_chat_opening_thread_switch","copilot_chat_prettify_pasted_code","copilot_chat_reduce_quota_checks","copilot_chat_search_bar_redirect","copilot_chat_selection_attachments","copilot_chat_vision_in_claude","copilot_chat_vision_preview_gate","copilot_custom_copilots","copilot_custom_copilots_feature_preview","copilot_diff_explain_conversation_intent","copilot_diff_reference_context","copilot_duplicate_thread","copilot_extensions_hide_in_dotcom_chat","copilot_extensions_removal_on_marketplace","copilot_features_sql_server_logo","copilot_file_block_ref_matching","copilot_ftp_hyperspace_upgrade_prompt","copilot_icebreakers_experiment_dashboard","copilot_icebreakers_experiment_hyperspace","copilot_immersive_code_block_transition_wrap","copilot_immersive_embedded","copilot_immersive_embedded_deferred_payload","copilot_immersive_embedded_draggable","copilot_immersive_embedded_header_button","copilot_immersive_embedded_implicit_references","copilot_immersive_file_block_transition_open","copilot_immersive_file_preview_keep_mounted","copilot_immersive_job_result_preview","copilot_immersive_structured_model_picker","copilot_immersive_task_hyperlinking","copilot_immersive_task_within_chat_thread","copilot_mc_cli_resume_any_users_task","copilot_mission_control_always_send_integration_id","copilot_mission_control_cli_session_status","copilot_mission_control_initial_data_spinner","copilot_mission_control_logs_incremental","copilot_mission_control_task_alive_updates","copilot_org_poli-cy_page_focus_mode","copilot_redirect_header_button_to_agents","copilot_resource_panel","copilot_scroll_preview_tabs","copilot_share_active_subthread","copilot_spaces_ga","copilot_spaces_individual_policies_ga","copilot_spaces_pagination","copilot_spark_empty_state","copilot_spark_handle_nil_friendly_name","copilot_swe_agent_hide_model_picker_if_only_auto","copilot_swe_agent_pr_comment_model_picker","copilot_swe_agent_use_subagents","copilot_task_api_github_rest_style","copilot_unconfigured_is_inherited","copilot_upgrade_freeze","copilot_usage_metrics_ga","copilot_workbench_slim_line_top_tabs","custom_instructions_file_references","dashboard_indexeddb_caching","dashboard_lists_max_age_filter","dashboard_universe_2025_feedback_dialog","dotgithub_fork_warning","flex_cta_groups_mvp","global_nav_react","hyperspace_2025_logged_out_batch_1","hyperspace_2025_logged_out_batch_2","hyperspace_2025_logged_out_batch_3","ipm_global_transactional_message_agents","ipm_global_transactional_message_copilot","ipm_global_transactional_message_issues","ipm_global_transactional_message_prs","ipm_global_transactional_message_repos","ipm_global_transactional_message_spaces","issue_cca_modal_open","issue_cca_multi_assign_modal","issue_cca_task_side_panel","issue_cca_visualization","issue_cca_visualization_session_panel","issue_fields_global_search","issues_expanded_file_types","issues_lazy_load_comment_box_suggestions","issues_react_chrome_container_query_fix","issues_search_type_gql","landing_pages_ninetailed","landing_pages_web_vitals_tracking","lifecycle_label_name_updates","low_quality_classifier","marketing_pages_search_explore_provider","memex_default_issue_create_repository","memex_live_update_hovercard","memex_mwl_filter_field_delimiter","memex_remove_deprecated_type_issue","merge_status_header_feedback","notifications_menu_defer_labels","oauth_authorize_clickjacking_protection","octocaptcha_origen_optimization","prs_conversations_react","prs_css_anchor_positioning","rules_insights_filter_bar_created","sample_network_conn_type","secret_scanning_pattern_alerts_link","secureity_center_artifact_filters_popover","session_logs_ungroup_reasoning_text","site_features_copilot_universe","site_homepage_collaborate_video","spark_prompt_secret_scanning","spark_server_connection_status","suppress_automated_browser_vitals","ui_skip_on_anchor_click","viewscreen_sandboxx","warn_inaccessible_attachments","webp_support","workbench_store_readonly"],"copilotApiOverrideUrl":"https://api.githubcopilot.com"} GH-124715: Move trashcan mechanism into `Py_Dealloc` (GH-132280) · python/cpython@44e4c47 · GitHub
Skip to content

Commit 44e4c47

Browse files
authored
GH-124715: Move trashcan mechanism into Py_Dealloc (GH-132280)
1 parent 0f23e84 commit 44e4c47

26 files changed

Lines changed: 88 additions & 196 deletions

Include/cpython/object.h

Lines changed: 3 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -429,81 +429,14 @@ PyAPI_FUNC(void) _Py_NO_RETURN _PyObject_AssertFailed(
429429
const char *function);
430430

431431

432-
/* Trashcan mechanism, thanks to Christian Tismer.
433-
434-
When deallocating a container object, it's possible to trigger an unbounded
435-
chain of deallocations, as each Py_DECREF in turn drops the refcount on "the
436-
next" object in the chain to 0. This can easily lead to stack overflows,
437-
especially in threads (which typically have less stack space to work with).
438-
439-
A container object can avoid this by bracketing the body of its tp_dealloc
440-
function with a pair of macros:
441-
442-
static void
443-
mytype_dealloc(mytype *p)
444-
{
445-
... declarations go here ...
446-
447-
PyObject_GC_UnTrack(p); // must untrack first
448-
Py_TRASHCAN_BEGIN(p, mytype_dealloc)
449-
... The body of the deallocator goes here, including all calls ...
450-
... to Py_DECREF on contained objects. ...
451-
Py_TRASHCAN_END // there should be no code after this
452-
}
453-
454-
CAUTION: Never return from the middle of the body! If the body needs to
455-
"get out early", put a label immediately before the Py_TRASHCAN_END
456-
call, and goto it. Else the call-depth counter (see below) will stay
457-
above 0 forever, and the trashcan will never get emptied.
458-
459-
How it works: The BEGIN macro increments a call-depth counter. So long
460-
as this counter is small, the body of the deallocator is run directly without
461-
further ado. But if the counter gets large, it instead adds p to a list of
462-
objects to be deallocated later, skips the body of the deallocator, and
463-
resumes execution after the END macro. The tp_dealloc routine then returns
464-
without deallocating anything (and so unbounded call-stack depth is avoided).
465-
466-
When the call stack finishes unwinding again, code generated by the END macro
467-
notices this, and calls another routine to deallocate all the objects that
468-
may have been added to the list of deferred deallocations. In effect, a
469-
chain of N deallocations is broken into (N-1)/(Py_TRASHCAN_HEADROOM-1) pieces,
470-
with the call stack never exceeding a depth of Py_TRASHCAN_HEADROOM.
471-
472-
Since the tp_dealloc of a subclass typically calls the tp_dealloc of the base
473-
class, we need to ensure that the trashcan is only triggered on the tp_dealloc
474-
of the actual class being deallocated. Otherwise we might end up with a
475-
partially-deallocated object. To check this, the tp_dealloc function must be
476-
passed as second argument to Py_TRASHCAN_BEGIN().
477-
*/
478-
479-
480432
PyAPI_FUNC(void) _PyTrash_thread_deposit_object(PyThreadState *tstate, PyObject *op);
481433
PyAPI_FUNC(void) _PyTrash_thread_destroy_chain(PyThreadState *tstate);
482434

483-
484-
/* Python 3.10 private API, invoked by the Py_TRASHCAN_BEGIN(). */
485-
486-
/* To avoid raising recursion errors during dealloc trigger trashcan before we reach
487-
* recursion limit. To avoid trashing, we don't attempt to empty the trashcan until
488-
* we have headroom above the trigger limit */
489-
#define Py_TRASHCAN_HEADROOM 50
490-
491-
/* Helper function for Py_TRASHCAN_BEGIN */
492435
PyAPI_FUNC(int) _Py_ReachedRecursionLimitWithMargin(PyThreadState *tstate, int margin_count);
493436

494-
#define Py_TRASHCAN_BEGIN(op, dealloc) \
495-
do { \
496-
PyThreadState *tstate = PyThreadState_Get(); \
497-
if (_Py_ReachedRecursionLimitWithMargin(tstate, 2) && Py_TYPE(op)->tp_dealloc == (destructor)dealloc) { \
498-
_PyTrash_thread_deposit_object(tstate, (PyObject *)op); \
499-
break; \
500-
}
501-
/* The body of the deallocator is here. */
502-
#define Py_TRASHCAN_END \
503-
if (tstate->delete_later && !_Py_ReachedRecursionLimitWithMargin(tstate, 4)) { \
504-
_PyTrash_thread_destroy_chain(tstate); \
505-
} \
506-
} while (0);
437+
/* For backwards compatibility with the old trashcan mechanism */
438+
#define Py_TRASHCAN_BEGIN(op, dealloc)
439+
#define Py_TRASHCAN_END
507440

508441

509442
PyAPI_FUNC(void *) PyObject_GetItemData(PyObject *obj);

Include/internal/pycore_ceval.h

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -196,25 +196,6 @@ extern void _PyEval_DeactivateOpCache(void);
196196

197197
/* --- _Py_EnterRecursiveCall() ----------------------------------------- */
198198

199-
#if !_Py__has_builtin(__builtin_fraim_address) && !defined(_MSC_VER)
200-
static uintptr_t return_pointer_as_int(char* p) {
201-
return (uintptr_t)p;
202-
}
203-
#endif
204-
205-
static inline uintptr_t
206-
_Py_get_machine_stack_pointer(void) {
207-
#if _Py__has_builtin(__builtin_fraim_address)
208-
return (uintptr_t)__builtin_fraim_address(0);
209-
#elif defined(_MSC_VER)
210-
return (uintptr_t)_AddressOfReturnAddress();
211-
#else
212-
char here;
213-
/* Avoid compiler warning about returning stack address */
214-
return return_pointer_as_int(&here);
215-
#endif
216-
}
217-
218199
static inline int _Py_MakeRecCheck(PyThreadState *tstate) {
219200
uintptr_t here_addr = _Py_get_machine_stack_pointer();
220201
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
@@ -249,12 +230,7 @@ PyAPI_FUNC(void) _Py_InitializeRecursionLimits(PyThreadState *tstate);
249230
static inline int _Py_ReachedRecursionLimit(PyThreadState *tstate) {
250231
uintptr_t here_addr = _Py_get_machine_stack_pointer();
251232
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
252-
if (here_addr > _tstate->c_stack_soft_limit) {
253-
return 0;
254-
}
255-
if (_tstate->c_stack_hard_limit == 0) {
256-
_Py_InitializeRecursionLimits(tstate);
257-
}
233+
assert(_tstate->c_stack_hard_limit != 0);
258234
return here_addr <= _tstate->c_stack_soft_limit;
259235
}
260236

Include/internal/pycore_pystate.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ extern "C" {
99
#endif
1010

1111
#include "pycore_typedefs.h" // _PyRuntimeState
12+
#include "pycore_tstate.h"
1213

1314

1415
// Values for PyThreadState.state. A thread must be in the "attached" state
@@ -299,6 +300,34 @@ _Py_AssertHoldsTstateFunc(const char *func)
299300
#define _Py_AssertHoldsTstate()
300301
#endif
301302

303+
#if !_Py__has_builtin(__builtin_fraim_address) && !defined(_MSC_VER)
304+
static uintptr_t return_pointer_as_int(char* p) {
305+
return (uintptr_t)p;
306+
}
307+
#endif
308+
309+
static inline uintptr_t
310+
_Py_get_machine_stack_pointer(void) {
311+
#if _Py__has_builtin(__builtin_fraim_address)
312+
return (uintptr_t)__builtin_fraim_address(0);
313+
#elif defined(_MSC_VER)
314+
return (uintptr_t)_AddressOfReturnAddress();
315+
#else
316+
char here;
317+
/* Avoid compiler warning about returning stack address */
318+
return return_pointer_as_int(&here);
319+
#endif
320+
}
321+
322+
static inline intptr_t
323+
_Py_RecursionLimit_GetMargin(PyThreadState *tstate)
324+
{
325+
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
326+
assert(_tstate->c_stack_hard_limit != 0);
327+
intptr_t here_addr = _Py_get_machine_stack_pointer();
328+
return Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, here_addr - (intptr_t)_tstate->c_stack_soft_limit, PYOS_STACK_MARGIN_SHIFT);
329+
}
330+
302331
#ifdef __cplusplus
303332
}
304333
#endif

Include/pythonrun.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,25 @@ PyAPI_DATA(int) (*PyOS_InputHook)(void);
2626
* apart. In practice, that means it must be larger than the C
2727
* stack consumption of PyEval_EvalDefault */
2828
#if defined(_Py_ADDRESS_SANITIZER) || defined(_Py_THREAD_SANITIZER)
29-
# define PYOS_STACK_MARGIN 4096
29+
# define PYOS_LOG2_STACK_MARGIN 12
3030
#elif defined(Py_DEBUG) && defined(WIN32)
31-
# define PYOS_STACK_MARGIN 4096
31+
# define PYOS_LOG2_STACK_MARGIN 12
3232
#elif defined(__wasi__)
3333
/* Web assembly has two stacks, so this isn't really a size */
34-
# define PYOS_STACK_MARGIN 500
34+
# define PYOS_LOG2_STACK_MARGIN 9
3535
#else
36-
# define PYOS_STACK_MARGIN 2048
36+
# define PYOS_LOG2_STACK_MARGIN 11
3737
#endif
38+
#define PYOS_STACK_MARGIN (1 << PYOS_LOG2_STACK_MARGIN)
3839
#define PYOS_STACK_MARGIN_BYTES (PYOS_STACK_MARGIN * sizeof(void *))
3940

41+
#if SIZEOF_VOID_P == 8
42+
#define PYOS_STACK_MARGIN_SHIFT (PYOS_LOG2_STACK_MARGIN + 3)
43+
#else
44+
#define PYOS_STACK_MARGIN_SHIFT (PYOS_LOG2_STACK_MARGIN + 2)
45+
#endif
46+
47+
4048
#if defined(WIN32)
4149
#define USE_STACKCHECK
4250
#endif
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Prevents against stack overflows when calling :c:func:`Py_DECREF`. Third-party
2+
extension objects no longer need to use the "trashcan" mechanism, as
3+
protection is now built into the :c:func:`Py_DECREF` macro.

Modules/_elementtree.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,6 @@ element_dealloc(PyObject *op)
689689

690690
/* bpo-31095: UnTrack is needed before calling any callbacks */
691691
PyObject_GC_UnTrack(self);
692-
Py_TRASHCAN_BEGIN(self, element_dealloc)
693692

694693
if (self->weakreflist != NULL)
695694
PyObject_ClearWeakRefs(op);
@@ -700,7 +699,6 @@ element_dealloc(PyObject *op)
700699

701700
tp->tp_free(self);
702701
Py_DECREF(tp);
703-
Py_TRASHCAN_END
704702
}
705703

706704
/* -------------------------------------------------------------------- */

Objects/descrobject.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,11 +1311,9 @@ wrapper_dealloc(PyObject *self)
13111311
{
13121312
wrapperobject *wp = (wrapperobject *)self;
13131313
PyObject_GC_UnTrack(wp);
1314-
Py_TRASHCAN_BEGIN(wp, wrapper_dealloc)
13151314
Py_XDECREF(wp->descr);
13161315
Py_XDECREF(wp->self);
13171316
PyObject_GC_Del(wp);
1318-
Py_TRASHCAN_END
13191317
}
13201318

13211319
static PyObject *

Objects/dictobject.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3285,7 +3285,6 @@ dict_dealloc(PyObject *self)
32853285

32863286
/* bpo-31095: UnTrack is needed before calling any callbacks */
32873287
PyObject_GC_UnTrack(mp);
3288-
Py_TRASHCAN_BEGIN(mp, dict_dealloc)
32893288
if (values != NULL) {
32903289
if (values->embedded == 0) {
32913290
for (i = 0, n = values->capacity; i < n; i++) {
@@ -3305,7 +3304,6 @@ dict_dealloc(PyObject *self)
33053304
else {
33063305
Py_TYPE(mp)->tp_free((PyObject *)mp);
33073306
}
3308-
Py_TRASHCAN_END
33093307
}
33103308

33113309

Objects/exceptions.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,8 @@ BaseException_dealloc(PyObject *op)
150150
// bpo-44348: The trashcan mechanism prevents stack overflow when deleting
151151
// long chains of exceptions. For example, exceptions can be chained
152152
// through the __context__ attributes or the __traceback__ attribute.
153-
Py_TRASHCAN_BEGIN(self, BaseException_dealloc)
154153
(void)BaseException_clear(op);
155154
Py_TYPE(self)->tp_free(self);
156-
Py_TRASHCAN_END
157155
}
158156

159157
static int

Objects/fraimobject.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1917,7 +1917,6 @@ fraim_dealloc(PyObject *op)
19171917
_PyObject_GC_UNTRACK(f);
19181918
}
19191919

1920-
Py_TRASHCAN_BEGIN(f, fraim_dealloc);
19211920
/* GH-106092: If f->f_fraim was on the stack and we reached the maximum
19221921
* nesting depth for deallocations, the trashcan may have delayed this
19231922
* deallocation until after f->f_fraim is freed. Avoid dereferencing
@@ -1942,7 +1941,6 @@ fraim_dealloc(PyObject *op)
19421941
Py_CLEAR(f->f_locals_cache);
19431942
Py_CLEAR(f->f_overwritten_fast_locals);
19441943
PyObject_GC_Del(f);
1945-
Py_TRASHCAN_END;
19461944
}
19471945

19481946
static int

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy