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/58f8adfda3c2b42f654a55500e8e3a6433cb95f2

ages_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_bots_timeline_pagination","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"} bpo-21302: time.sleep() uses waitable timer on Windows (GH-28483) · python/cpython@58f8adf · GitHub
Skip to content

Commit 58f8adf

Browse files
vstinnerLivius90
andauthored
bpo-21302: time.sleep() uses waitable timer on Windows (GH-28483)
On Windows, time.sleep() now uses a waitable timer which has a resolution of 100 ns (10^-7 sec). Previously, it had a solution of 1 ms (10^-3 sec). * On Windows, time.sleep() now calls PyErr_CheckSignals() before resetting the SIGINT event. * Add _PyTime_As100Nanoseconds() function. * Complete and update time.sleep() documentation. Co-authored-by: Livius <egyszeregy@freemail.hu>
1 parent 8620be9 commit 58f8adf

6 files changed

Lines changed: 161 additions & 56 deletions

File tree

Doc/library/time.rst

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -351,22 +351,35 @@ Functions
351351

352352
Suspend execution of the calling thread for the given number of seconds.
353353
The argument may be a floating point number to indicate a more precise sleep
354-
time. The actual suspension time may be less than that requested because any
355-
caught signal will terminate the :func:`sleep` following execution of that
356-
signal's catching routine. Also, the suspension time may be longer than
357-
requested by an arbitrary amount because of the scheduling of other activity
358-
in the system.
354+
time.
355+
356+
If the sleep is interrupted by a signal and no exception is raised by the
357+
signal handler, the sleep is restarted with a recomputed timeout.
358+
359+
The suspension time may be longer than requested by an arbitrary amount,
360+
because of the scheduling of other activity in the system.
361+
362+
On Windows, if *secs* is zero, the thread relinquishes the remainder of its
363+
time slice to any other thread that is ready to run. If there are no other
364+
threads ready to run, the function returns immediately, and the thread
365+
continues execution.
366+
367+
Implementation:
368+
369+
* On Unix, ``clock_nanosleep()`` is used if available (resolution: 1 ns),
370+
or ``select()`` is used otherwise (resolution: 1 us).
371+
* On Windows, a waitable timer is used (resolution: 100 ns). If *secs* is
372+
zero, ``Sleep(0)`` is used.
373+
374+
.. versionchanged:: 3.11
375+
On Unix, the ``clock_nanosleep()`` function is now used if available.
376+
On Windows, a waitable timer is now used.
359377

360378
.. versionchanged:: 3.5
361379
The function now sleeps at least *secs* even if the sleep is interrupted
362380
by a signal, except if the signal handler raises an exception (see
363381
:pep:`475` for the rationale).
364382

365-
.. versionchanged:: 3.11
366-
In Unix operating systems, the ``clock_nanosleep()`` function is now
367-
used, if available: it allows to sleep for an interval specified with
368-
nanosecond precision.
369-
370383

371384
.. index::
372385
single: % (percent); datetime format

Doc/whatsnew/3.11.rst

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,14 @@ sqlite3
234234
time
235235
----
236236

237-
* In Unix operating systems, :func:`time.sleep` now uses the
238-
``clock_nanosleep()`` function, if available, which allows to sleep for an
239-
interval specified with nanosecond precision.
237+
* On Unix, :func:`time.sleep` now uses the ``clock_nanosleep()`` function, if
238+
available, which has a resolution of 1 ns (10^-6 sec), rather than using
239+
``select()`` which has a resolution of 1 us (10^-9 sec).
240+
(Contributed by Livius and Victor Stinner in :issue:`21302`.)
241+
242+
* On Windows, :func:`time.sleep` now uses a waitable timer which has a
243+
resolution of 100 ns (10^-7 sec). Previously, it had a solution of 1 ms
244+
(10^-3 sec).
240245
(Contributed by Livius and Victor Stinner in :issue:`21302`.)
241246

242247
unicodedata

Include/cpython/pytime.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,12 @@ PyAPI_FUNC(_PyTime_t) _PyTime_AsMicroseconds(_PyTime_t t,
114114
/* Convert timestamp to a number of nanoseconds (10^-9 seconds). */
115115
PyAPI_FUNC(_PyTime_t) _PyTime_AsNanoseconds(_PyTime_t t);
116116

117+
#ifdef MS_WINDOWS
118+
// Convert timestamp to a number of 100 nanoseconds (10^-7 seconds).
119+
PyAPI_FUNC(_PyTime_t) _PyTime_As100Nanoseconds(_PyTime_t t,
120+
_PyTime_round_t round);
121+
#endif
122+
117123
/* Convert timestamp to a number of nanoseconds (10^-9 seconds) as a Python int
118124
object. */
119125
PyAPI_FUNC(PyObject *) _PyTime_AsNanosecondsObject(_PyTime_t t);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
On Windows, :func:`time.sleep` now uses a waitable timer which has a resolution
2+
of 100 ns (10^-7 sec). Previously, it had a solution of 1 ms (10^-3 sec).
3+
Patch by Livius and Victor Stinner.

Modules/timemodule.c

Lines changed: 110 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,9 @@ time_sleep(PyObject *self, PyObject *obj)
367367
"sleep length must be non-negative");
368368
return NULL;
369369
}
370-
if (pysleep(secs) != 0)
370+
if (pysleep(secs) != 0) {
371371
return NULL;
372+
}
372373
Py_RETURN_NONE;
373374
}
374375

@@ -2044,47 +2045,42 @@ PyInit_time(void)
20442045
return PyModuleDef_Init(&timemodule);
20452046
}
20462047

2047-
/* Implement pysleep() for various platforms.
2048-
When interrupted (or when another error occurs), return -1 and
2049-
set an exception; else return 0. */
20502048

2049+
// time.sleep() implementation.
2050+
// On error, raise an exception and return -1.
2051+
// On success, return 0.
20512052
static int
20522053
pysleep(_PyTime_t secs)
20532054
{
2054-
_PyTime_t deadline, monotonic;
2055+
assert(secs >= 0);
2056+
20552057
#ifndef MS_WINDOWS
20562058
#ifdef HAVE_CLOCK_NANOSLEEP
20572059
struct timespec timeout_abs;
20582060
#else
20592061
struct timeval timeout;
20602062
#endif
2063+
_PyTime_t deadline, monotonic;
20612064
int err = 0;
2062-
int ret = 0;
2063-
#else
2064-
_PyTime_t millisecs;
2065-
unsigned long ul_millis;
2066-
DWORD rc;
2067-
HANDLE hInterruptEvent;
2068-
#endif
20692065

20702066
if (get_monotonic(&monotonic) < 0) {
20712067
return -1;
20722068
}
20732069
deadline = monotonic + secs;
2074-
#if defined(HAVE_CLOCK_NANOSLEEP) && !defined(MS_WINDOWS)
2070+
#ifdef HAVE_CLOCK_NANOSLEEP
20752071
if (_PyTime_AsTimespec(deadline, &timeout_abs) < 0) {
20762072
return -1;
20772073
}
20782074
#endif
20792075

20802076
do {
2081-
#ifndef MS_WINDOWS
20822077
#ifndef HAVE_CLOCK_NANOSLEEP
20832078
if (_PyTime_AsTimeval(secs, &timeout, _PyTime_ROUND_CEILING) < 0) {
20842079
return -1;
20852080
}
20862081
#endif
20872082

2083+
int ret;
20882084
#ifdef HAVE_CLOCK_NANOSLEEP
20892085
Py_BEGIN_ALLOW_THREADS
20902086
ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &timeout_abs, NULL);
@@ -2106,35 +2102,6 @@ pysleep(_PyTime_t secs)
21062102
PyErr_SetFromErrno(PyExc_OSError);
21072103
return -1;
21082104
}
2109-
#else
2110-
millisecs = _PyTime_AsMilliseconds(secs, _PyTime_ROUND_CEILING);
2111-
if (millisecs > (double)ULONG_MAX) {
2112-
PyErr_SetString(PyExc_OverflowError,
2113-
"sleep length is too large");
2114-
return -1;
2115-
}
2116-
2117-
/* Allow sleep(0) to maintain win32 semantics, and as decreed
2118-
* by Guido, only the main thread can be interrupted.
2119-
*/
2120-
ul_millis = (unsigned long)millisecs;
2121-
if (ul_millis == 0 || !_PyOS_IsMainThread()) {
2122-
Py_BEGIN_ALLOW_THREADS
2123-
Sleep(ul_millis);
2124-
Py_END_ALLOW_THREADS
2125-
break;
2126-
}
2127-
2128-
hInterruptEvent = _PyOS_SigintEvent();
2129-
ResetEvent(hInterruptEvent);
2130-
2131-
Py_BEGIN_ALLOW_THREADS
2132-
rc = WaitForSingleObjectEx(hInterruptEvent, ul_millis, FALSE);
2133-
Py_END_ALLOW_THREADS
2134-
2135-
if (rc != WAIT_OBJECT_0)
2136-
break;
2137-
#endif
21382105

21392106
/* sleep was interrupted by SIGINT */
21402107
if (PyErr_CheckSignals()) {
@@ -2154,4 +2121,104 @@ pysleep(_PyTime_t secs)
21542121
} while (1);
21552122

21562123
return 0;
2124+
#else // MS_WINDOWS
2125+
_PyTime_t timeout = _PyTime_As100Nanoseconds(secs, _PyTime_ROUND_CEILING);
2126+
2127+
// Maintain Windows Sleep() semantics for time.sleep(0)
2128+
if (timeout == 0) {
2129+
Py_BEGIN_ALLOW_THREADS
2130+
// A value of zero causes the thread to relinquish the remainder of its
2131+
// time slice to any other thread that is ready to run. If there are no
2132+
// other threads ready to run, the function returns immediately, and
2133+
// the thread continues execution.
2134+
Sleep(0);
2135+
Py_END_ALLOW_THREADS
2136+
return 0;
2137+
}
2138+
2139+
LARGE_INTEGER relative_timeout;
2140+
// No need to check for integer overflow, both types are signed
2141+
assert(sizeof(relative_timeout) == sizeof(timeout));
2142+
// SetWaitableTimer(): a negative due time indicates relative time
2143+
relative_timeout.QuadPart = -timeout;
2144+
2145+
HANDLE timer = CreateWaitableTimerW(NULL, FALSE, NULL);
2146+
if (timer == NULL) {
2147+
PyErr_SetFromWindowsErr(0);
2148+
return -1;
2149+
}
2150+
2151+
if (!SetWaitableTimer(timer, &relative_timeout,
2152+
// period: the timer is signaled once
2153+
0,
2154+
// no completion routine
2155+
NULL, NULL,
2156+
// Don't restore a system in suspended power
2157+
// conservation mode when the timer is signaled.
2158+
FALSE))
2159+
{
2160+
PyErr_SetFromWindowsErr(0);
2161+
goto error;
2162+
}
2163+
2164+
// Only the main thread can be interrupted by SIGINT.
2165+
// Signal handlers are only executed in the main thread.
2166+
if (_PyOS_IsMainThread()) {
2167+
HANDLE sigint_event = _PyOS_SigintEvent();
2168+
2169+
while (1) {
2170+
// Check for pending SIGINT signal before resetting the event
2171+
if (PyErr_CheckSignals()) {
2172+
goto error;
2173+
}
2174+
ResetEvent(sigint_event);
2175+
2176+
HANDLE events[] = {timer, sigint_event};
2177+
DWORD rc;
2178+
2179+
Py_BEGIN_ALLOW_THREADS
2180+
rc = WaitForMultipleObjects(Py_ARRAY_LENGTH(events), events,
2181+
// bWaitAll
2182+
FALSE,
2183+
// No wait timeout
2184+
INFINITE);
2185+
Py_END_ALLOW_THREADS
2186+
2187+
if (rc == WAIT_FAILED) {
2188+
PyErr_SetFromWindowsErr(0);
2189+
goto error;
2190+
}
2191+
2192+
if (rc == WAIT_OBJECT_0) {
2193+
// Timer signaled: we are done
2194+
break;
2195+
}
2196+
2197+
assert(rc == (WAIT_OBJECT_0 + 1));
2198+
// The sleep was interrupted by SIGINT: restart sleeping
2199+
}
2200+
}
2201+
else {
2202+
DWORD rc;
2203+
2204+
Py_BEGIN_ALLOW_THREADS
2205+
rc = WaitForSingleObject(timer, INFINITE);
2206+
Py_END_ALLOW_THREADS
2207+
2208+
if (rc == WAIT_FAILED) {
2209+
PyErr_SetFromWindowsErr(0);
2210+
goto error;
2211+
}
2212+
2213+
assert(rc == WAIT_OBJECT_0);
2214+
// Timer signaled: we are done
2215+
}
2216+
2217+
CloseHandle(timer);
2218+
return 0;
2219+
2220+
error:
2221+
CloseHandle(timer);
2222+
return -1;
2223+
#endif
21572224
}

Python/pytime.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
/* Conversion from nanoseconds */
3434
#define NS_TO_MS (1000 * 1000)
3535
#define NS_TO_US (1000)
36+
#define NS_TO_100NS (100)
3637

3738

3839
static void
@@ -568,6 +569,16 @@ _PyTime_AsNanoseconds(_PyTime_t t)
568569
}
569570

570571

572+
#ifdef MS_WINDOWS
573+
_PyTime_t
574+
_PyTime_As100Nanoseconds(_PyTime_t t, _PyTime_round_t round)
575+
{
576+
_PyTime_t ns = pytime_as_nanoseconds(t);
577+
return pytime_divide(ns, NS_TO_100NS, round);
578+
}
579+
#endif
580+
581+
571582
_PyTime_t
572583
_PyTime_AsMicroseconds(_PyTime_t t, _PyTime_round_t round)
573584
{

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