mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	bpo-21302: Add nanosleep() implementation for time.sleep() in Unix (GH-28545)
Co-authored-by: Livius <egyszeregy@freemail.hu>
This commit is contained in:
		
							parent
							
								
									71f8ff45c6
								
							
						
					
					
						commit
						7834ff26cb
					
				
					 7 changed files with 114 additions and 33 deletions
				
			
		|  | @ -364,16 +364,18 @@ Functions | |||
|    threads ready to run, the function returns immediately, and the thread | ||||
|    continues execution. | ||||
| 
 | ||||
|    Implementation: | ||||
|    Unix implementation: | ||||
| 
 | ||||
|    * On Unix, ``clock_nanosleep()`` is used if available (resolution: 1 ns), | ||||
|      or ``select()`` is used otherwise (resolution: 1 us). | ||||
|    * On Windows, a waitable timer is used (resolution: 100 ns). If *secs* is | ||||
|      zero, ``Sleep(0)`` is used. | ||||
|    * Use ``clock_nanosleep()`` if available (resolution: 1 ns); | ||||
|    * Or use ``nanosleep()`` if available (resolution: 1 ns); | ||||
|    * Or use ``select()`` (resolution: 1 us). | ||||
| 
 | ||||
|    On Windows, a waitable timer is used (resolution: 100 ns). If *secs* is | ||||
|    zero, ``Sleep(0)`` is used. | ||||
| 
 | ||||
|    .. versionchanged:: 3.11 | ||||
|       On Unix, the ``clock_nanosleep()`` function is now used if available. | ||||
|       On Windows, a waitable timer is now used. | ||||
|       On Unix, the ``clock_nanosleep()`` and ``nanosleep()`` functions are now | ||||
|       used if available. On Windows, a waitable timer is now used. | ||||
| 
 | ||||
|    .. versionchanged:: 3.5 | ||||
|       The function now sleeps at least *secs* even if the sleep is interrupted | ||||
|  |  | |||
|  | @ -242,9 +242,10 @@ sqlite3 | |||
| time | ||||
| ---- | ||||
| 
 | ||||
| * On Unix, :func:`time.sleep` now uses the ``clock_nanosleep()`` function, if | ||||
|   available, which has a resolution of 1 ns (10^-6 sec), rather than using | ||||
|   ``select()`` which has a resolution of 1 us (10^-9 sec). | ||||
| * On Unix, :func:`time.sleep` now uses the ``clock_nanosleep()`` or | ||||
|   ``nanosleep()`` function, if available, which has a resolution of 1 ns (10^-6 | ||||
|   sec), rather than using ``select()`` which has a resolution of 1 us (10^-9 | ||||
|   sec). | ||||
|   (Contributed by Livius and Victor Stinner in :issue:`21302`.) | ||||
| 
 | ||||
| * On Windows, :func:`time.sleep` now uses a waitable timer which has a | ||||
|  |  | |||
|  | @ -0,0 +1 @@ | |||
| In Unix operating systems, :func:`time.sleep` now uses the ``nanosleep()`` function, if ``clock_nanosleep()`` is not available but ``nanosleep()`` is available. ``nanosleep()`` allows to sleep with nanosecond precision. | ||||
|  | @ -63,7 +63,7 @@ | |||
| #define SEC_TO_NS (1000 * 1000 * 1000) | ||||
| 
 | ||||
| /* Forward declarations */ | ||||
| static int pysleep(_PyTime_t); | ||||
| static int pysleep(_PyTime_t timeout); | ||||
| 
 | ||||
| 
 | ||||
| static PyObject* | ||||
|  | @ -357,17 +357,17 @@ Return the clk_id of a thread's CPU time clock."); | |||
| #endif /* HAVE_PTHREAD_GETCPUCLOCKID */ | ||||
| 
 | ||||
| static PyObject * | ||||
| time_sleep(PyObject *self, PyObject *obj) | ||||
| time_sleep(PyObject *self, PyObject *timeout_obj) | ||||
| { | ||||
|     _PyTime_t secs; | ||||
|     if (_PyTime_FromSecondsObject(&secs, obj, _PyTime_ROUND_TIMEOUT)) | ||||
|     _PyTime_t timeout; | ||||
|     if (_PyTime_FromSecondsObject(&timeout, timeout_obj, _PyTime_ROUND_TIMEOUT)) | ||||
|         return NULL; | ||||
|     if (secs < 0) { | ||||
|     if (timeout < 0) { | ||||
|         PyErr_SetString(PyExc_ValueError, | ||||
|                         "sleep length must be non-negative"); | ||||
|         return NULL; | ||||
|     } | ||||
|     if (pysleep(secs) != 0) { | ||||
|     if (pysleep(timeout) != 0) { | ||||
|         return NULL; | ||||
|     } | ||||
|     Py_RETURN_NONE; | ||||
|  | @ -2050,15 +2050,17 @@ PyInit_time(void) | |||
| // On error, raise an exception and return -1.
 | ||||
| // On success, return 0.
 | ||||
| static int | ||||
| pysleep(_PyTime_t secs) | ||||
| pysleep(_PyTime_t timeout) | ||||
| { | ||||
|     assert(secs >= 0); | ||||
|     assert(timeout >= 0); | ||||
| 
 | ||||
| #ifndef MS_WINDOWS | ||||
| #ifdef HAVE_CLOCK_NANOSLEEP | ||||
|     struct timespec timeout_abs; | ||||
| #elif defined(HAVE_NANOSLEEP) | ||||
|     struct timespec timeout_ts; | ||||
| #else | ||||
|     struct timeval timeout; | ||||
|     struct timeval timeout_tv; | ||||
| #endif | ||||
|     _PyTime_t deadline, monotonic; | ||||
|     int err = 0; | ||||
|  | @ -2066,7 +2068,7 @@ pysleep(_PyTime_t secs) | |||
|     if (get_monotonic(&monotonic) < 0) { | ||||
|         return -1; | ||||
|     } | ||||
|     deadline = monotonic + secs; | ||||
|     deadline = monotonic + timeout; | ||||
| #ifdef HAVE_CLOCK_NANOSLEEP | ||||
|     if (_PyTime_AsTimespec(deadline, &timeout_abs) < 0) { | ||||
|         return -1; | ||||
|  | @ -2074,24 +2076,31 @@ pysleep(_PyTime_t secs) | |||
| #endif | ||||
| 
 | ||||
|     do { | ||||
| #ifndef HAVE_CLOCK_NANOSLEEP | ||||
|         if (_PyTime_AsTimeval(secs, &timeout, _PyTime_ROUND_CEILING) < 0) { | ||||
| #ifdef HAVE_CLOCK_NANOSLEEP | ||||
|         // use timeout_abs
 | ||||
| #elif defined(HAVE_NANOSLEEP) | ||||
|         if (_PyTime_AsTimespec(timeout, &timeout_ts) < 0) { | ||||
|             return -1; | ||||
|         } | ||||
| #else | ||||
|         if (_PyTime_AsTimeval(timeout, &timeout_tv, _PyTime_ROUND_CEILING) < 0) { | ||||
|             return -1; | ||||
|         } | ||||
| #endif | ||||
| 
 | ||||
|         int ret; | ||||
|         Py_BEGIN_ALLOW_THREADS | ||||
| #ifdef HAVE_CLOCK_NANOSLEEP | ||||
|         Py_BEGIN_ALLOW_THREADS | ||||
|         ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &timeout_abs, NULL); | ||||
|         Py_END_ALLOW_THREADS | ||||
|         err = ret; | ||||
| #elif defined(HAVE_NANOSLEEP) | ||||
|         ret = nanosleep(&timeout_ts, NULL); | ||||
|         err = errno; | ||||
| #else | ||||
|         Py_BEGIN_ALLOW_THREADS | ||||
|         ret = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout); | ||||
|         Py_END_ALLOW_THREADS | ||||
|         ret = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout_tv); | ||||
|         err = errno; | ||||
| #endif | ||||
|         Py_END_ALLOW_THREADS | ||||
| 
 | ||||
|         if (ret == 0) { | ||||
|             break; | ||||
|  | @ -2112,8 +2121,8 @@ pysleep(_PyTime_t secs) | |||
|         if (get_monotonic(&monotonic) < 0) { | ||||
|             return -1; | ||||
|         } | ||||
|         secs = deadline - monotonic; | ||||
|         if (secs < 0) { | ||||
|         timeout = deadline - monotonic; | ||||
|         if (timeout < 0) { | ||||
|             break; | ||||
|         } | ||||
|         /* retry with the recomputed delay */ | ||||
|  | @ -2122,10 +2131,11 @@ pysleep(_PyTime_t secs) | |||
| 
 | ||||
|     return 0; | ||||
| #else  // MS_WINDOWS
 | ||||
|     _PyTime_t timeout = _PyTime_As100Nanoseconds(secs, _PyTime_ROUND_CEILING); | ||||
|     _PyTime_t timeout_100ns = _PyTime_As100Nanoseconds(timeout, | ||||
|                                                        _PyTime_ROUND_CEILING); | ||||
| 
 | ||||
|     // Maintain Windows Sleep() semantics for time.sleep(0)
 | ||||
|     if (timeout == 0) { | ||||
|     if (timeout_100ns == 0) { | ||||
|         Py_BEGIN_ALLOW_THREADS | ||||
|         // A value of zero causes the thread to relinquish the remainder of its
 | ||||
|         // time slice to any other thread that is ready to run. If there are no
 | ||||
|  | @ -2138,9 +2148,9 @@ pysleep(_PyTime_t secs) | |||
| 
 | ||||
|     LARGE_INTEGER relative_timeout; | ||||
|     // No need to check for integer overflow, both types are signed
 | ||||
|     assert(sizeof(relative_timeout) == sizeof(timeout)); | ||||
|     assert(sizeof(relative_timeout) == sizeof(timeout_100ns)); | ||||
|     // SetWaitableTimer(): a negative due time indicates relative time
 | ||||
|     relative_timeout.QuadPart = -timeout; | ||||
|     relative_timeout.QuadPart = -timeout_100ns; | ||||
| 
 | ||||
|     HANDLE timer = CreateWaitableTimerW(NULL, FALSE, NULL); | ||||
|     if (timer == NULL) { | ||||
|  |  | |||
							
								
								
									
										58
									
								
								configure
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										58
									
								
								configure
									
										
									
									
										vendored
									
									
								
							|  | @ -13310,6 +13310,64 @@ fi | |||
| done | ||||
| 
 | ||||
| 
 | ||||
| for ac_func in nanosleep | ||||
| do : | ||||
|   ac_fn_c_check_func "$LINENO" "nanosleep" "ac_cv_func_nanosleep" | ||||
| if test "x$ac_cv_func_nanosleep" = xyes; then : | ||||
|   cat >>confdefs.h <<_ACEOF | ||||
| #define HAVE_NANOSLEEP 1 | ||||
| _ACEOF | ||||
| 
 | ||||
| else | ||||
| 
 | ||||
|     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for nanosleep in -lrt" >&5 | ||||
| $as_echo_n "checking for nanosleep in -lrt... " >&6; } | ||||
| if ${ac_cv_lib_rt_nanosleep+:} false; then : | ||||
|   $as_echo_n "(cached) " >&6 | ||||
| else | ||||
|   ac_check_lib_save_LIBS=$LIBS | ||||
| LIBS="-lrt  $LIBS" | ||||
| cat confdefs.h - <<_ACEOF >conftest.$ac_ext | ||||
| /* end confdefs.h.  */ | ||||
| 
 | ||||
| /* Override any GCC internal prototype to avoid an error. | ||||
|    Use char because int might match the return type of a GCC | ||||
|    builtin and then its argument prototype would still apply.  */ | ||||
| #ifdef __cplusplus | ||||
| extern "C" | ||||
| #endif | ||||
| char nanosleep (); | ||||
| int | ||||
| main () | ||||
| { | ||||
| return nanosleep (); | ||||
|   ; | ||||
|   return 0; | ||||
| } | ||||
| _ACEOF | ||||
| if ac_fn_c_try_link "$LINENO"; then : | ||||
|   ac_cv_lib_rt_nanosleep=yes | ||||
| else | ||||
|   ac_cv_lib_rt_nanosleep=no | ||||
| fi | ||||
| rm -f core conftest.err conftest.$ac_objext \ | ||||
|     conftest$ac_exeext conftest.$ac_ext | ||||
| LIBS=$ac_check_lib_save_LIBS | ||||
| fi | ||||
| { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_nanosleep" >&5 | ||||
| $as_echo "$ac_cv_lib_rt_nanosleep" >&6; } | ||||
| if test "x$ac_cv_lib_rt_nanosleep" = xyes; then : | ||||
| 
 | ||||
|         $as_echo "#define HAVE_NANOSLEEP 1" >>confdefs.h | ||||
| 
 | ||||
| 
 | ||||
| fi | ||||
| 
 | ||||
| 
 | ||||
| fi | ||||
| done | ||||
| 
 | ||||
| 
 | ||||
| for ac_func in clock_getres | ||||
| do : | ||||
|   ac_fn_c_check_func "$LINENO" "clock_getres" "ac_cv_func_clock_getres" | ||||
|  |  | |||
|  | @ -4121,6 +4121,12 @@ AC_CHECK_FUNCS(clock_nanosleep, [], [ | |||
|     ]) | ||||
| ]) | ||||
| 
 | ||||
| AC_CHECK_FUNCS(nanosleep, [], [ | ||||
|     AC_CHECK_LIB(rt, nanosleep, [ | ||||
|         AC_DEFINE(HAVE_NANOSLEEP, 1) | ||||
|     ]) | ||||
| ]) | ||||
| 
 | ||||
| AC_MSG_CHECKING(for major, minor, and makedev) | ||||
| AC_LINK_IFELSE([AC_LANG_PROGRAM([[ | ||||
| #if defined(MAJOR_IN_MKDEV) | ||||
|  |  | |||
|  | @ -736,6 +736,9 @@ | |||
| /* Define to 1 if you have the `mremap' function. */ | ||||
| #undef HAVE_MREMAP | ||||
| 
 | ||||
| /* Define to 1 if you have the `nanosleep' function. */ | ||||
| #undef HAVE_NANOSLEEP | ||||
| 
 | ||||
| /* Define to 1 if you have the <ncurses.h> header file. */ | ||||
| #undef HAVE_NCURSES_H | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Victor Stinner
						Victor Stinner