@@ -113,7 +113,7 @@ _PyTime_Init(void)
113113
114114
115115/* Forward declarations */
116- static int pysleep (_PyTime_t timeout );
116+ static int pysleep (_PyTime_t timeout , int absolute );
117117
118118
119119typedef struct {
@@ -420,7 +420,7 @@ time_sleep(PyObject *self, PyObject *timeout_obj)
420420 "sleep length must be non-negative" );
421421 return NULL ;
422422 }
423- if (pysleep (timeout ) != 0 ) {
423+ if (pysleep (timeout , 0 ) != 0 ) {
424424 return NULL ;
425425 }
426426 Py_RETURN_NONE ;
@@ -432,6 +432,28 @@ PyDoc_STRVAR(sleep_doc,
432432Delay execution for a given number of seconds. The argument may be\n\
433433a floating point number for subsecond precision." );
434434
435+ static PyObject *
436+ time_sleep_until (PyObject * self , PyObject * deadline_obj )
437+ {
438+ _PyTime_t deadline ;
439+ if (_PyTime_FromSecondsObject (& deadline , deadline_obj , _PyTime_ROUND_TIMEOUT ))
440+ return NULL ;
441+ if (deadline < 0 ) {
442+ PyErr_SetString (PyExc_ValueError ,
443+ "sleep_until deadline must be non-negative" );
444+ return NULL ;
445+ }
446+ if (pysleep (deadline , 1 ) != 0 ) {
447+ return NULL ;
448+ }
449+ Py_RETURN_NONE ;
450+ }
451+
452+ PyDoc_STRVAR (sleep_until_doc ,
453+ "sleep_until(seconds)\n\
454+ \n\
455+ Delay execution until the specified system clock time." );
456+
435457static PyStructSequence_Field struct_time_type_fields [] = {
436458 {"tm_year" , "year, for example, 1993" },
437459 {"tm_mon" , "month of year, range [1, 12]" },
@@ -1862,6 +1884,7 @@ static PyMethodDef time_methods[] = {
18621884 {"pthread_getcpuclockid" , time_pthread_getcpuclockid , METH_VARARGS , pthread_getcpuclockid_doc },
18631885#endif
18641886 {"sleep" , time_sleep , METH_O , sleep_doc },
1887+ {"sleep_until" , time_sleep_until , METH_O , sleep_until_doc },
18651888 {"gmtime" , time_gmtime , METH_VARARGS , gmtime_doc },
18661889 {"localtime" , time_localtime , METH_VARARGS , localtime_doc },
18671890 {"asctime" , time_asctime , METH_VARARGS , asctime_doc },
@@ -2126,8 +2149,9 @@ PyInit_time(void)
21262149// time.sleep() implementation.
21272150// On error, raise an exception and return -1.
21282151// On success, return 0.
2152+ // If absolute==0, timeout is relative; otherwise timeout is absolute.
21292153static int
2130- pysleep (_PyTime_t timeout )
2154+ pysleep (_PyTime_t timeout , int absolute )
21312155{
21322156 assert (timeout >= 0 );
21332157
@@ -2139,13 +2163,27 @@ pysleep(_PyTime_t timeout)
21392163#else
21402164 struct timeval timeout_tv ;
21412165#endif
2142- _PyTime_t deadline , monotonic ;
2166+ _PyTime_t deadline , reference ;
21432167 int err = 0 ;
21442168
2145- if (get_monotonic (& monotonic ) < 0 ) {
2146- return -1 ;
2169+ if (absolute ) {
2170+ deadline = timeout ;
2171+ #ifndef HAVE_CLOCK_NANOSLEEP
2172+ if (get_system_time (& reference ) < 0 ) {
2173+ return -1 ;
2174+ }
2175+ timeout = deadline - reference ;
2176+ if (timeout < 0 ) {
2177+ return 0 ;
2178+ }
2179+ #endif
2180+ }
2181+ else {
2182+ if (get_monotonic (& reference ) < 0 ) {
2183+ return -1 ;
2184+ }
2185+ deadline = reference + timeout ;
21472186 }
2148- deadline = monotonic + timeout ;
21492187#ifdef HAVE_CLOCK_NANOSLEEP
21502188 if (_PyTime_AsTimespec (deadline , & timeout_abs ) < 0 ) {
21512189 return -1 ;
@@ -2168,7 +2206,8 @@ pysleep(_PyTime_t timeout)
21682206 int ret ;
21692207 Py_BEGIN_ALLOW_THREADS
21702208#ifdef HAVE_CLOCK_NANOSLEEP
2171- ret = clock_nanosleep (CLOCK_MONOTONIC , TIMER_ABSTIME , & timeout_abs , NULL );
2209+ ret = clock_nanosleep (absolute ? CLOCK_REALTIME : CLOCK_MONOTONIC ,
2210+ TIMER_ABSTIME , & timeout_abs , NULL );
21722211 err = ret ;
21732212#elif defined(HAVE_NANOSLEEP )
21742213 ret = nanosleep (& timeout_ts , NULL );
@@ -2195,10 +2234,17 @@ pysleep(_PyTime_t timeout)
21952234 }
21962235
21972236#ifndef HAVE_CLOCK_NANOSLEEP
2198- if (get_monotonic (& monotonic ) < 0 ) {
2199- return -1 ;
2237+ if (absolute ) {
2238+ if (get_system_time (& reference ) < 0 ) {
2239+ return -1 ;
2240+ }
2241+ }
2242+ else {
2243+ if (get_monotonic (& reference ) < 0 ) {
2244+ return -1 ;
2245+ }
22002246 }
2201- timeout = deadline - monotonic ;
2247+ timeout = deadline - reference ;
22022248 if (timeout < 0 ) {
22032249 break ;
22042250 }
@@ -2223,11 +2269,18 @@ pysleep(_PyTime_t timeout)
22232269 return 0 ;
22242270 }
22252271
2226- LARGE_INTEGER relative_timeout ;
2272+ LARGE_INTEGER due_time ;
22272273 // No need to check for integer overflow, both types are signed
2228- assert (sizeof (relative_timeout ) == sizeof (timeout_100ns ));
2229- // SetWaitableTimer(): a negative due time indicates relative time
2230- relative_timeout .QuadPart = - timeout_100ns ;
2274+ assert (sizeof (due_time ) == sizeof (timeout_100ns ));
2275+ if (absolute ) {
2276+ // Adjust from Unix time (1970-01-01) to Windows time (1601-01-01)
2277+ // (the inverse of what is done in py_get_system_clock)
2278+ due_time .QuadPart = timeout_100ns + 116444736000000000 ;
2279+ }
2280+ else {
2281+ // SetWaitableTimer(): a negative due time indicates relative time
2282+ due_time .QuadPart = - timeout_100ns ;
2283+ }
22312284
22322285 HANDLE timer = CreateWaitableTimerExW (NULL , NULL , timer_flags ,
22332286 TIMER_ALL_ACCESS );
@@ -2236,7 +2289,7 @@ pysleep(_PyTime_t timeout)
22362289 return -1 ;
22372290 }
22382291
2239- if (!SetWaitableTimerEx (timer , & relative_timeout ,
2292+ if (!SetWaitableTimerEx (timer , & due_time ,
22402293 0 , // no period; the timer is signaled once
22412294 NULL , NULL , // no completion routine
22422295 NULL , // no wake context; do not resume from suspend
0 commit comments