bpo-30807: signal.setitimer() now uses _PyTime API (GH-3865)

The _PyTime API handles detects overflow and is well tested.

Document that the signal will only be sent once if internal is equal
to zero.
diff --git a/Doc/library/signal.rst b/Doc/library/signal.rst
index 46d71de..2bc39d9 100644
--- a/Doc/library/signal.rst
+++ b/Doc/library/signal.rst
@@ -273,13 +273,14 @@
    .. versionadded:: 3.3
 
 
-.. function:: setitimer(which, seconds[, interval])
+.. function:: setitimer(which, seconds, interval=0.0)
 
    Sets given interval timer (one of :const:`signal.ITIMER_REAL`,
    :const:`signal.ITIMER_VIRTUAL` or :const:`signal.ITIMER_PROF`) specified
    by *which* to fire after *seconds* (float is accepted, different from
-   :func:`alarm`) and after that every *interval* seconds. The interval
-   timer specified by *which* can be cleared by setting seconds to zero.
+   :func:`alarm`) and after that every *interval* seconds (if *interval*
+   is non-zero). The interval timer specified by *which* can be cleared by
+   setting *seconds* to zero.
 
    When an interval timer fires, a signal is sent to the process.
    The signal sent is dependent on the timer being used;
diff --git a/Modules/clinic/signalmodule.c.h b/Modules/clinic/signalmodule.c.h
index a4542cc..cc3e640 100644
--- a/Modules/clinic/signalmodule.c.h
+++ b/Modules/clinic/signalmodule.c.h
@@ -182,18 +182,18 @@
     {"setitimer", (PyCFunction)signal_setitimer, METH_FASTCALL, signal_setitimer__doc__},
 
 static PyObject *
-signal_setitimer_impl(PyObject *module, int which, double seconds,
-                      double interval);
+signal_setitimer_impl(PyObject *module, int which, PyObject *seconds,
+                      PyObject *interval);
 
 static PyObject *
 signal_setitimer(PyObject *module, PyObject **args, Py_ssize_t nargs)
 {
     PyObject *return_value = NULL;
     int which;
-    double seconds;
-    double interval = 0.0;
+    PyObject *seconds;
+    PyObject *interval = NULL;
 
-    if (!_PyArg_ParseStack(args, nargs, "id|d:setitimer",
+    if (!_PyArg_ParseStack(args, nargs, "iO|O:setitimer",
         &which, &seconds, &interval)) {
         goto exit;
     }
@@ -440,4 +440,4 @@
 #ifndef SIGNAL_PTHREAD_KILL_METHODDEF
     #define SIGNAL_PTHREAD_KILL_METHODDEF
 #endif /* !defined(SIGNAL_PTHREAD_KILL_METHODDEF) */
-/*[clinic end generated code: output=3f6e6298696f1b75 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=a003d3fea9a33daa input=a9049054013a1b77]*/
diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c
index 6633108..d470727 100644
--- a/Modules/signalmodule.c
+++ b/Modules/signalmodule.c
@@ -131,16 +131,21 @@
 #ifdef HAVE_GETITIMER
 static PyObject *ItimerError;
 
-/* auxiliary functions for setitimer/getitimer */
-static void
-timeval_from_double(double d, struct timeval *tv)
+/* auxiliary functions for setitimer */
+static int
+timeval_from_double(PyObject *obj, struct timeval *tv)
 {
-    tv->tv_sec = floor(d);
-    tv->tv_usec = fmod(d, 1.0) * 1000000.0;
-    /* Don't disable the timer if the computation above rounds down to zero. */
-    if (d > 0.0 && tv->tv_sec == 0 && tv->tv_usec == 0) {
-        tv->tv_usec = 1;
+    if (obj == NULL) {
+        tv->tv_sec = 0;
+        tv->tv_usec = 0;
+        return 0;
     }
+
+    _PyTime_t t;
+    if (_PyTime_FromSecondsObject(&t, obj, _PyTime_ROUND_CEILING) < 0) {
+        return -1;
+    }
+    return _PyTime_AsTimeval(t, tv, _PyTime_ROUND_CEILING);
 }
 
 Py_LOCAL_INLINE(double)
@@ -677,8 +682,8 @@
 signal.setitimer
 
     which:    int
-    seconds:  double
-    interval: double = 0.0
+    seconds:  object
+    interval: object(c_default="NULL") = 0.0
     /
 
 Sets given itimer (one of ITIMER_REAL, ITIMER_VIRTUAL or ITIMER_PROF).
@@ -690,14 +695,19 @@
 [clinic start generated code]*/
 
 static PyObject *
-signal_setitimer_impl(PyObject *module, int which, double seconds,
-                      double interval)
-/*[clinic end generated code: output=6f51da0fe0787f2c input=0d27d417cfcbd51a]*/
+signal_setitimer_impl(PyObject *module, int which, PyObject *seconds,
+                      PyObject *interval)
+/*[clinic end generated code: output=65f9dcbddc35527b input=de43daf194e6f66f]*/
 {
     struct itimerval new, old;
 
-    timeval_from_double(seconds, &new.it_value);
-    timeval_from_double(interval, &new.it_interval);
+    if (timeval_from_double(seconds, &new.it_value) < 0) {
+        return NULL;
+    }
+    if (timeval_from_double(interval, &new.it_interval) < 0) {
+        return NULL;
+    }
+
     /* Let OS check "which" value */
     if (setitimer(which, &new, &old) != 0) {
         PyErr_SetFromErrno(ItimerError);