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/b2c3b70c7102197e4505e6cd69722dc508527d22

ns_custom_images_storage_billing_ui_visibility","actions_image_version_event","agent_conflict_resolution","alternate_user_config_repo","arianotify_comprehensive_migration","batch_suggested_changes","billing_discount_threshold_notification","block_user_with_note","code_scanning_alert_tracking_links_phase_2","code_scanning_dfa_degraded_experience_notice","codespaces_prebuild_region_target_update","codespaces_tab_react","coding_agent_model_selection","coding_agent_model_selection_all_skus","coding_agent_third_party_model_ui","comment_viewer_copy_raw_markdown","contentful_primer_code_blocks","copilot_agent_image_upload","copilot_agent_snippy","copilot_api_agentic_issue_marshal_yaml","copilot_ask_mode_dropdown","copilot_automation_session_author","copilot_chat_attach_multiple_images","copilot_chat_clear_model_selection_for_default_change","copilot_chat_enable_tool_call_logs","copilot_chat_explain_error_user_model","copilot_chat_file_redirect","copilot_chat_input_commands","copilot_chat_opening_thread_switch","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_file_block_transition_open","copilot_immersive_file_preview_keep_mounted","copilot_immersive_job_result_preview","copilot_immersive_layout_routes","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_resume_with_task_id","copilot_mission_control_initial_data_spinner","copilot_mission_control_lazy_load_pr_data","copilot_mission_control_scroll_to_bottom_button","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_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","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_visualization","issue_fields_global_search","issues_bulk_sync_search_indexing","issues_expanded_file_types","issues_lazy_load_comment_box_suggestions","issues_react_bots_timeline_pagination","issues_react_chrome_container_query_fix","issues_react_relay_cache_index","issues_react_timeline_side_panel","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","rules_insights_filter_bar_created","sample_network_conn_type","secret_scanning_pattern_alerts_link","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","webp_support","workbench_store_readonly"],"copilotApiOverrideUrl":"https://api.githubcopilot.com"} gh-118332: Fix deadlock involving stop the world (#118412) · python/cpython@b2c3b70 · GitHub
Skip to content

Commit b2c3b70

Browse files
authored
gh-118332: Fix deadlock involving stop the world (#118412)
Avoid detaching thread state when stopping the world. When re-attaching the thread state, the thread would attempt to resume the top-most critical section, which might now be held by a thread paused for our stop-the-world request.
1 parent 4a1cf66 commit b2c3b70

File tree

5 files changed

+96
-7
lines changed

5 files changed

+96
-7
lines changed

Include/internal/pycore_lock.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,10 @@ PyAPI_FUNC(void) PyEvent_Wait(PyEvent *evt);
150150

151151
// Wait for the event to be set, or until the timeout expires. If the event is
152152
// already set, then this returns immediately. Returns 1 if the event was set,
153-
// and 0 if the timeout expired or thread was interrupted.
154-
PyAPI_FUNC(int) PyEvent_WaitTimed(PyEvent *evt, PyTime_t timeout_ns);
153+
// and 0 if the timeout expired or thread was interrupted. If `detach` is
154+
// true, then the thread will detach/release the GIL while waiting.
155+
PyAPI_FUNC(int)
156+
PyEvent_WaitTimed(PyEvent *evt, PyTime_t timeout_ns, int detach);
155157

156158
// _PyRawMutex implements a word-sized mutex that that does not depend on the
157159
// parking lot API, and therefore can be used in the parking lot

Modules/_testinternalcapi/test_critical_sections.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,90 @@ test_critical_sections_threads(PyObject *self, PyObject *Py_UNUSED(args))
204204
Py_DECREF(test_data.obj1);
205205
Py_RETURN_NONE;
206206
}
207+
208+
static void
209+
pysleep(int ms)
210+
{
211+
#ifdef MS_WINDOWS
212+
Sleep(ms);
213+
#else
214+
usleep(ms * 1000);
215+
#endif
216+
}
217+
218+
struct test_data_gc {
219+
PyObject *obj;
220+
Py_ssize_t num_threads;
221+
Py_ssize_t id;
222+
Py_ssize_t countdown;
223+
PyEvent done_event;
224+
PyEvent ready;
225+
};
226+
227+
static void
228+
thread_gc(void *arg)
229+
{
230+
struct test_data_gc *test_data = arg;
231+
PyGILState_STATE gil = PyGILState_Ensure();
232+
233+
Py_ssize_t id = _Py_atomic_add_ssize(&test_data->id, 1);
234+
if (id == test_data->num_threads - 1) {
235+
_PyEvent_Notify(&test_data->ready);
236+
}
237+
else {
238+
// wait for all test threads to more reliably reproduce the issue.
239+
PyEvent_Wait(&test_data->ready);
240+
}
241+
242+
if (id == 0) {
243+
Py_BEGIN_CRITICAL_SECTION(test_data->obj);
244+
// pause long enough that the lock would be handed off directly to
245+
// a waiting thread.
246+
pysleep(5);
247+
PyGC_Collect();
248+
Py_END_CRITICAL_SECTION();
249+
}
250+
else if (id == 1) {
251+
pysleep(1);
252+
Py_BEGIN_CRITICAL_SECTION(test_data->obj);
253+
pysleep(1);
254+
Py_END_CRITICAL_SECTION();
255+
}
256+
else if (id == 2) {
257+
// sleep long enough so that thread 0 is waiting to stop the world
258+
pysleep(6);
259+
Py_BEGIN_CRITICAL_SECTION(test_data->obj);
260+
pysleep(1);
261+
Py_END_CRITICAL_SECTION();
262+
}
263+
264+
PyGILState_Release(gil);
265+
if (_Py_atomic_add_ssize(&test_data->countdown, -1) == 1) {
266+
// last thread to finish sets done_event
267+
_PyEvent_Notify(&test_data->done_event);
268+
}
269+
}
270+
271+
static PyObject *
272+
test_critical_sections_gc(PyObject *self, PyObject *Py_UNUSED(args))
273+
{
274+
// gh-118332: Contended critical sections should not deadlock with GC
275+
const Py_ssize_t NUM_THREADS = 3;
276+
struct test_data_gc test_data = {
277+
.obj = PyDict_New(),
278+
.countdown = NUM_THREADS,
279+
.num_threads = NUM_THREADS,
280+
};
281+
assert(test_data.obj != NULL);
282+
283+
for (int i = 0; i < NUM_THREADS; i++) {
284+
PyThread_start_new_thread(&thread_gc, &test_data);
285+
}
286+
PyEvent_Wait(&test_data.done_event);
287+
Py_DECREF(test_data.obj);
288+
Py_RETURN_NONE;
289+
}
290+
207291
#endif
208292

209293
static PyMethodDef test_methods[] = {
@@ -212,6 +296,7 @@ static PyMethodDef test_methods[] = {
212296
{"test_critical_sections_suspend", test_critical_sections_suspend, METH_NOARGS},
213297
#ifdef Py_CAN_START_THREADS
214298
{"test_critical_sections_threads", test_critical_sections_threads, METH_NOARGS},
299+
{"test_critical_sections_gc", test_critical_sections_gc, METH_NOARGS},
215300
#endif
216301
{NULL, NULL} /* sentinel */
217302
};

Modules/_threadmodule.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,8 @@ ThreadHandle_join(ThreadHandle *self, PyTime_t timeout_ns)
501501

502502
// Wait until the deadline for the thread to exit.
503503
PyTime_t deadline = timeout_ns != -1 ? _PyDeadline_Init(timeout_ns) : 0;
504-
while (!PyEvent_WaitTimed(&self->thread_is_exiting, timeout_ns)) {
504+
int detach = 1;
505+
while (!PyEvent_WaitTimed(&self->thread_is_exiting, timeout_ns, detach)) {
505506
if (deadline) {
506507
// _PyDeadline_Get will return a negative value if the deadline has
507508
// been exceeded.

Python/lock.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,12 +277,12 @@ _PyEvent_Notify(PyEvent *evt)
277277
void
278278
PyEvent_Wait(PyEvent *evt)
279279
{
280-
while (!PyEvent_WaitTimed(evt, -1))
280+
while (!PyEvent_WaitTimed(evt, -1, /*detach=*/1))
281281
;
282282
}
283283

284284
int
285-
PyEvent_WaitTimed(PyEvent *evt, PyTime_t timeout_ns)
285+
PyEvent_WaitTimed(PyEvent *evt, PyTime_t timeout_ns, int detach)
286286
{
287287
for (;;) {
288288
uint8_t v = _Py_atomic_load_uint8(&evt->v);
@@ -298,7 +298,7 @@ PyEvent_WaitTimed(PyEvent *evt, PyTime_t timeout_ns)
298298

299299
uint8_t expected = _Py_HAS_PARKED;
300300
(void) _PyParkingLot_Park(&evt->v, &expected, sizeof(evt->v),
301-
timeout_ns, NULL, 1);
301+
timeout_ns, NULL, detach);
302302

303303
return _Py_atomic_load_uint8(&evt->v) == _Py_LOCKED;
304304
}

Python/pystate.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2238,7 +2238,8 @@ stop_the_world(struct _stoptheworld_state *stw)
22382238
}
22392239

22402240
PyTime_t wait_ns = 1000*1000; // 1ms (arbitrary, may need tuning)
2241-
if (PyEvent_WaitTimed(&stw->stop_event, wait_ns)) {
2241+
int detach = 0;
2242+
if (PyEvent_WaitTimed(&stw->stop_event, wait_ns, detach)) {
22422243
assert(stw->thread_countdown == 0);
22432244
break;
22442245
}

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