summaryrefslogtreecommitdiff
path: root/Modules/timemodule.c
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2017-11-02 07:28:27 -0700
committerGitHub <noreply@github.com>2017-11-02 07:28:27 -0700
commitc29b585fd4b5a91d17fc5dd41d86edff28a30da3 (patch)
treeaadcf238ccc1867d7adefc079081781424e3e62c /Modules/timemodule.c
parente314853d57450b2b9523157eebd405289a795a0e (diff)
downloadcpython-git-c29b585fd4b5a91d17fc5dd41d86edff28a30da3.tar.gz
bpo-31784: Implement PEP 564: add time.time_ns() (#3989)
Add new time functions: * time.clock_gettime_ns() * time.clock_settime_ns() * time.monotonic_ns() * time.perf_counter_ns() * time.process_time_ns() * time.time_ns() Add new _PyTime functions: * _PyTime_FromTimespec() * _PyTime_FromNanosecondsObject() * _PyTime_FromTimeval() Other changes: * Add also os.times() tests to test_os. * pytime_fromtimeval() and pytime_fromtimeval() now return _PyTime_MAX or _PyTime_MIN on overflow, rather than undefined behaviour * _PyTime_FromNanoseconds() parameter type changes from long long to _PyTime_t
Diffstat (limited to 'Modules/timemodule.c')
-rw-r--r--Modules/timemodule.c426
1 files changed, 309 insertions, 117 deletions
diff --git a/Modules/timemodule.c b/Modules/timemodule.c
index 347c8282d8..37abeb9507 100644
--- a/Modules/timemodule.c
+++ b/Modules/timemodule.c
@@ -34,57 +34,90 @@
#endif /* MS_WINDOWS */
#endif /* !__WATCOMC__ || __QNX__ */
+#define SEC_TO_NS (1000 * 1000 * 1000)
+
/* Forward declarations */
static int pysleep(_PyTime_t);
-static PyObject* floattime(_Py_clock_info_t *info);
+
+
+static PyObject*
+_PyFloat_FromPyTime(_PyTime_t t)
+{
+ double d = _PyTime_AsSecondsDouble(t);
+ return PyFloat_FromDouble(d);
+}
+
static PyObject *
time_time(PyObject *self, PyObject *unused)
{
- return floattime(NULL);
+ _PyTime_t t = _PyTime_GetSystemClock();
+ return _PyFloat_FromPyTime(t);
}
+
PyDoc_STRVAR(time_doc,
"time() -> floating point number\n\
\n\
Return the current time in seconds since the Epoch.\n\
Fractions of a second may be present if the system clock provides them.");
+static PyObject *
+time_time_ns(PyObject *self, PyObject *unused)
+{
+ _PyTime_t t = _PyTime_GetSystemClock();
+ return _PyTime_AsNanosecondsObject(t);
+}
+
+PyDoc_STRVAR(time_ns_doc,
+"time_ns() -> int\n\
+\n\
+Return the current time in nanoseconds since the Epoch.");
+
#if defined(HAVE_CLOCK)
#ifndef CLOCKS_PER_SEC
-#ifdef CLK_TCK
-#define CLOCKS_PER_SEC CLK_TCK
-#else
-#define CLOCKS_PER_SEC 1000000
-#endif
+# ifdef CLK_TCK
+# define CLOCKS_PER_SEC CLK_TCK
+# else
+# define CLOCKS_PER_SEC 1000000
+# endif
#endif
-static PyObject*
-_PyFloat_FromPyTime(_PyTime_t t)
+static int
+_PyTime_GetClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
{
- double d = _PyTime_AsSecondsDouble(t);
- return PyFloat_FromDouble(d);
-}
+ static int initialized = 0;
+ clock_t ticks;
-static PyObject *
-floatclock(_Py_clock_info_t *info)
-{
- clock_t value;
- value = clock();
- if (value == (clock_t)-1) {
- PyErr_SetString(PyExc_RuntimeError,
- "the processor time used is not available "
- "or its value cannot be represented");
- return NULL;
+ if (!initialized) {
+ initialized = 1;
+
+ /* must sure that _PyTime_MulDiv(ticks, SEC_TO_NS, CLOCKS_PER_SEC)
+ above cannot overflow */
+ if ((_PyTime_t)CLOCKS_PER_SEC > _PyTime_MAX / SEC_TO_NS) {
+ PyErr_SetString(PyExc_OverflowError,
+ "CLOCKS_PER_SEC is too large");
+ return -1;
+ }
}
+
if (info) {
info->implementation = "clock()";
info->resolution = 1.0 / (double)CLOCKS_PER_SEC;
info->monotonic = 1;
info->adjustable = 0;
}
- return PyFloat_FromDouble((double)value / CLOCKS_PER_SEC);
+
+ ticks = clock();
+ if (ticks == (clock_t)-1) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "the processor time used is not available "
+ "or its value cannot be represented");
+ return -1;
+ }
+ *tp = _PyTime_MulDiv(ticks, SEC_TO_NS, (_PyTime_t)CLOCKS_PER_SEC);
+ return 0;
}
#endif /* HAVE_CLOCK */
@@ -95,8 +128,7 @@ perf_counter(_Py_clock_info_t *info)
if (_PyTime_GetPerfCounterWithInfo(&t, info) < 0) {
return NULL;
}
- double d = _PyTime_AsSecondsDouble(t);
- return PyFloat_FromDouble(d);
+ return _PyFloat_FromPyTime(t);
}
#if defined(MS_WINDOWS) || defined(HAVE_CLOCK)
@@ -111,10 +143,15 @@ pyclock(_Py_clock_info_t *info)
"instead", 1) < 0) {
return NULL;
}
+
#ifdef MS_WINDOWS
return perf_counter(info);
#else
- return floatclock(info);
+ _PyTime_t t;
+ if (_PyTime_GetClockWithInfo(&t, info) < 0) {
+ return NULL;
+ }
+ return _PyFloat_FromPyTime(t);
#endif
}
@@ -140,8 +177,9 @@ time_clock_gettime(PyObject *self, PyObject *args)
int clk_id;
struct timespec tp;
- if (!PyArg_ParseTuple(args, "i:clock_gettime", &clk_id))
+ if (!PyArg_ParseTuple(args, "i:clock_gettime", &clk_id)) {
return NULL;
+ }
ret = clock_gettime((clockid_t)clk_id, &tp);
if (ret != 0) {
@@ -152,9 +190,37 @@ time_clock_gettime(PyObject *self, PyObject *args)
}
PyDoc_STRVAR(clock_gettime_doc,
-"clock_gettime(clk_id) -> floating point number\n\
+"clock_gettime(clk_id) -> float\n\
\n\
Return the time of the specified clock clk_id.");
+
+static PyObject *
+time_clock_gettime_ns(PyObject *self, PyObject *args)
+{
+ int ret;
+ int clk_id;
+ struct timespec ts;
+ _PyTime_t t;
+
+ if (!PyArg_ParseTuple(args, "i:clock_gettime", &clk_id)) {
+ return NULL;
+ }
+
+ ret = clock_gettime((clockid_t)clk_id, &ts);
+ if (ret != 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+ if (_PyTime_FromTimespec(&t, &ts) < 0) {
+ return NULL;
+ }
+ return _PyTime_AsNanosecondsObject(t);
+}
+
+PyDoc_STRVAR(clock_gettime_ns_doc,
+"clock_gettime_ns(clk_id) -> int\n\
+\n\
+Return the time of the specified clock clk_id as nanoseconds.");
#endif /* HAVE_CLOCK_GETTIME */
#ifdef HAVE_CLOCK_SETTIME
@@ -188,6 +254,39 @@ PyDoc_STRVAR(clock_settime_doc,
"clock_settime(clk_id, time)\n\
\n\
Set the time of the specified clock clk_id.");
+
+static PyObject *
+time_clock_settime_ns(PyObject *self, PyObject *args)
+{
+ int clk_id;
+ PyObject *obj;
+ _PyTime_t t;
+ struct timespec ts;
+ int ret;
+
+ if (!PyArg_ParseTuple(args, "iO:clock_settime", &clk_id, &obj)) {
+ return NULL;
+ }
+
+ if (_PyTime_FromNanosecondsObject(&t, obj) < 0) {
+ return NULL;
+ }
+ if (_PyTime_AsTimespec(t, &ts) == -1) {
+ return NULL;
+ }
+
+ ret = clock_settime((clockid_t)clk_id, &ts);
+ if (ret != 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(clock_settime_ns_doc,
+"clock_settime_ns(clk_id, time)\n\
+\n\
+Set the time of the specified clock clk_id with nanoseconds.");
#endif /* HAVE_CLOCK_SETTIME */
#ifdef HAVE_CLOCK_GETRES
@@ -927,26 +1026,28 @@ should not be relied on.");
#endif /* HAVE_WORKING_TZSET */
static PyObject *
-pymonotonic(_Py_clock_info_t *info)
+time_monotonic(PyObject *self, PyObject *unused)
{
- _PyTime_t t;
- if (_PyTime_GetMonotonicClockWithInfo(&t, info) < 0) {
- assert(info != NULL);
- return NULL;
- }
+ _PyTime_t t = _PyTime_GetMonotonicClock();
return _PyFloat_FromPyTime(t);
}
+PyDoc_STRVAR(monotonic_doc,
+"monotonic() -> float\n\
+\n\
+Monotonic clock, cannot go backward.");
+
static PyObject *
-time_monotonic(PyObject *self, PyObject *unused)
+time_monotonic_ns(PyObject *self, PyObject *unused)
{
- return pymonotonic(NULL);
+ _PyTime_t t = _PyTime_GetMonotonicClock();
+ return _PyTime_AsNanosecondsObject(t);
}
-PyDoc_STRVAR(monotonic_doc,
-"monotonic() -> float\n\
+PyDoc_STRVAR(monotonic_ns_doc,
+"monotonic_ns() -> int\n\
\n\
-Monotonic clock, cannot go backward.");
+Monotonic clock, cannot go backward, as nanoseconds.");
static PyObject *
time_perf_counter(PyObject *self, PyObject *unused)
@@ -959,47 +1060,61 @@ PyDoc_STRVAR(perf_counter_doc,
\n\
Performance counter for benchmarking.");
-static PyObject*
-py_process_time(_Py_clock_info_t *info)
+static PyObject *
+time_perf_counter_ns(PyObject *self, PyObject *unused)
+{
+ _PyTime_t t = _PyTime_GetPerfCounter();
+ return _PyTime_AsNanosecondsObject(t);
+}
+
+PyDoc_STRVAR(perf_counter_ns_doc,
+"perf_counter_ns() -> int\n\
+\n\
+Performance counter for benchmarking as nanoseconds.");
+
+static int
+_PyTime_GetProcessTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
{
#if defined(MS_WINDOWS)
HANDLE process;
FILETIME creation_time, exit_time, kernel_time, user_time;
ULARGE_INTEGER large;
- double total;
+ _PyTime_t ktime, utime, t;
BOOL ok;
process = GetCurrentProcess();
- ok = GetProcessTimes(process, &creation_time, &exit_time, &kernel_time, &user_time);
- if (!ok)
- return PyErr_SetFromWindowsErr(0);
+ ok = GetProcessTimes(process, &creation_time, &exit_time,
+ &kernel_time, &user_time);
+ if (!ok) {
+ PyErr_SetFromWindowsErr(0);
+ return -1;
+ }
- large.u.LowPart = kernel_time.dwLowDateTime;
- large.u.HighPart = kernel_time.dwHighDateTime;
- total = (double)large.QuadPart;
- large.u.LowPart = user_time.dwLowDateTime;
- large.u.HighPart = user_time.dwHighDateTime;
- total += (double)large.QuadPart;
if (info) {
info->implementation = "GetProcessTimes()";
info->resolution = 1e-7;
info->monotonic = 1;
info->adjustable = 0;
}
- return PyFloat_FromDouble(total * 1e-7);
-#else
-#if defined(HAVE_SYS_RESOURCE_H)
- struct rusage ru;
-#endif
-#ifdef HAVE_TIMES
- struct tms t;
- static long ticks_per_second = -1;
-#endif
+ large.u.LowPart = kernel_time.dwLowDateTime;
+ large.u.HighPart = kernel_time.dwHighDateTime;
+ ktime = large.QuadPart;
+
+ large.u.LowPart = user_time.dwLowDateTime;
+ large.u.HighPart = user_time.dwHighDateTime;
+ utime = large.QuadPart;
+
+ /* ktime and utime have a resolution of 100 nanoseconds */
+ t = _PyTime_FromNanoseconds((ktime + utime) * 100);
+ *tp = t;
+ return 0;
+#else
+ /* clock_gettime */
#if defined(HAVE_CLOCK_GETTIME) \
&& (defined(CLOCK_PROCESS_CPUTIME_ID) || defined(CLOCK_PROF))
- struct timespec tp;
+ struct timespec ts;
#ifdef CLOCK_PROF
const clockid_t clk_id = CLOCK_PROF;
const char *function = "clock_gettime(CLOCK_PROF)";
@@ -1008,75 +1123,117 @@ py_process_time(_Py_clock_info_t *info)
const char *function = "clock_gettime(CLOCK_PROCESS_CPUTIME_ID)";
#endif
- if (clock_gettime(clk_id, &tp) == 0) {
+ if (clock_gettime(clk_id, &ts) == 0) {
if (info) {
struct timespec res;
info->implementation = function;
info->monotonic = 1;
info->adjustable = 0;
- if (clock_getres(clk_id, &res) == 0)
- info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
- else
- info->resolution = 1e-9;
+ if (clock_getres(clk_id, &res)) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
}
- return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
+
+ if (_PyTime_FromTimespec(tp, &ts) < 0) {
+ return -1;
+ }
+ return 0;
}
#endif
+ /* getrusage(RUSAGE_SELF) */
#if defined(HAVE_SYS_RESOURCE_H)
+ struct rusage ru;
+
if (getrusage(RUSAGE_SELF, &ru) == 0) {
- double total;
- total = ru.ru_utime.tv_sec + ru.ru_utime.tv_usec * 1e-6;
- total += ru.ru_stime.tv_sec + ru.ru_stime.tv_usec * 1e-6;
+ _PyTime_t utime, stime;
+
if (info) {
info->implementation = "getrusage(RUSAGE_SELF)";
info->monotonic = 1;
info->adjustable = 0;
info->resolution = 1e-6;
}
- return PyFloat_FromDouble(total);
+
+ if (_PyTime_FromTimeval(&utime, &ru.ru_utime) < 0) {
+ return -1;
+ }
+ if (_PyTime_FromTimeval(&stime, &ru.ru_stime) < 0) {
+ return -1;
+ }
+
+ _PyTime_t total = utime + utime;
+ *tp = total;
+ return 0;
}
#endif
+ /* times() */
#ifdef HAVE_TIMES
+ struct tms t;
+
if (times(&t) != (clock_t)-1) {
- double total;
+ static long ticks_per_second = -1;
if (ticks_per_second == -1) {
+ long freq;
#if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK)
- ticks_per_second = sysconf(_SC_CLK_TCK);
- if (ticks_per_second < 1)
- ticks_per_second = -1;
+ freq = sysconf(_SC_CLK_TCK);
+ if (freq < 1) {
+ freq = -1;
+ }
#elif defined(HZ)
- ticks_per_second = HZ;
+ freq = HZ;
#else
- ticks_per_second = 60; /* magic fallback value; may be bogus */
+ freq = 60; /* magic fallback value; may be bogus */
#endif
+
+ if (freq != -1) {
+ /* check that _PyTime_MulDiv(t, SEC_TO_NS, ticks_per_second)
+ cannot overflow below */
+ if ((_PyTime_t)freq > _PyTime_MAX / SEC_TO_NS) {
+ PyErr_SetString(PyExc_OverflowError,
+ "_SC_CLK_TCK is too large");
+ return -1;
+ }
+
+ ticks_per_second = freq;
+ }
}
if (ticks_per_second != -1) {
- total = (double)t.tms_utime / ticks_per_second;
- total += (double)t.tms_stime / ticks_per_second;
if (info) {
info->implementation = "times()";
info->monotonic = 1;
info->adjustable = 0;
- info->resolution = 1.0 / ticks_per_second;
+ info->resolution = 1.0 / (double)ticks_per_second;
}
- return PyFloat_FromDouble(total);
+
+ _PyTime_t total;
+ total = _PyTime_MulDiv(t.tms_utime, SEC_TO_NS, ticks_per_second);
+ total += _PyTime_MulDiv(t.tms_stime, SEC_TO_NS, ticks_per_second);
+ *tp = total;
+ return 0;
}
}
#endif
+ /* clock */
/* Currently, Python 3 requires clock() to build: see issue #22624 */
- return floatclock(info);
+ return _PyTime_GetClockWithInfo(tp, info);
#endif
}
static PyObject *
time_process_time(PyObject *self, PyObject *unused)
{
- return py_process_time(NULL);
+ _PyTime_t t;
+ if (_PyTime_GetProcessTimeWithInfo(&t, NULL) < 0) {
+ return NULL;
+ }
+ return _PyFloat_FromPyTime(t);
}
PyDoc_STRVAR(process_time_doc,
@@ -1084,6 +1241,22 @@ PyDoc_STRVAR(process_time_doc,
\n\
Process time for profiling: sum of the kernel and user-space CPU time.");
+static PyObject *
+time_process_time_ns(PyObject *self, PyObject *unused)
+{
+ _PyTime_t t;
+ if (_PyTime_GetProcessTimeWithInfo(&t, NULL) < 0) {
+ return NULL;
+ }
+ return _PyTime_AsNanosecondsObject(t);
+}
+
+PyDoc_STRVAR(process_time_ns_doc,
+"process_time() -> int\n\
+\n\
+Process time for profiling as nanoseconds:\n\
+sum of the kernel and user-space CPU time.");
+
static PyObject *
time_get_clock_info(PyObject *self, PyObject *args)
@@ -1091,9 +1264,11 @@ time_get_clock_info(PyObject *self, PyObject *args)
char *name;
_Py_clock_info_t info;
PyObject *obj = NULL, *dict, *ns;
+ _PyTime_t t;
- if (!PyArg_ParseTuple(args, "s:get_clock_info", &name))
+ if (!PyArg_ParseTuple(args, "s:get_clock_info", &name)) {
return NULL;
+ }
#ifdef Py_DEBUG
info.implementation = NULL;
@@ -1107,61 +1282,84 @@ time_get_clock_info(PyObject *self, PyObject *args)
info.resolution = 1.0;
#endif
- if (strcmp(name, "time") == 0)
- obj = floattime(&info);
+ if (strcmp(name, "time") == 0) {
+ if (_PyTime_GetSystemClockWithInfo(&t, &info) < 0) {
+ return NULL;
+ }
+ }
#ifdef PYCLOCK
- else if (strcmp(name, "clock") == 0)
+ else if (strcmp(name, "clock") == 0) {
obj = pyclock(&info);
+ if (obj == NULL) {
+ return NULL;
+ }
+ Py_DECREF(obj);
+ }
#endif
- else if (strcmp(name, "monotonic") == 0)
- obj = pymonotonic(&info);
- else if (strcmp(name, "perf_counter") == 0)
- obj = perf_counter(&info);
- else if (strcmp(name, "process_time") == 0)
- obj = py_process_time(&info);
+ else if (strcmp(name, "monotonic") == 0) {
+ if (_PyTime_GetMonotonicClockWithInfo(&t, &info) < 0) {
+ return NULL;
+ }
+ }
+ else if (strcmp(name, "perf_counter") == 0) {
+ if (_PyTime_GetPerfCounterWithInfo(&t, &info) < 0) {
+ return NULL;
+ }
+ }
+ else if (strcmp(name, "process_time") == 0) {
+ if (_PyTime_GetProcessTimeWithInfo(&t, &info) < 0) {
+ return NULL;
+ }
+ }
else {
PyErr_SetString(PyExc_ValueError, "unknown clock");
return NULL;
}
- if (obj == NULL)
- return NULL;
- Py_DECREF(obj);
dict = PyDict_New();
- if (dict == NULL)
+ if (dict == NULL) {
return NULL;
+ }
assert(info.implementation != NULL);
obj = PyUnicode_FromString(info.implementation);
- if (obj == NULL)
+ if (obj == NULL) {
goto error;
- if (PyDict_SetItemString(dict, "implementation", obj) == -1)
+ }
+ if (PyDict_SetItemString(dict, "implementation", obj) == -1) {
goto error;
+ }
Py_CLEAR(obj);
assert(info.monotonic != -1);
obj = PyBool_FromLong(info.monotonic);
- if (obj == NULL)
+ if (obj == NULL) {
goto error;
- if (PyDict_SetItemString(dict, "monotonic", obj) == -1)
+ }
+ if (PyDict_SetItemString(dict, "monotonic", obj) == -1) {
goto error;
+ }
Py_CLEAR(obj);
assert(info.adjustable != -1);
obj = PyBool_FromLong(info.adjustable);
- if (obj == NULL)
+ if (obj == NULL) {
goto error;
- if (PyDict_SetItemString(dict, "adjustable", obj) == -1)
+ }
+ if (PyDict_SetItemString(dict, "adjustable", obj) == -1) {
goto error;
+ }
Py_CLEAR(obj);
assert(info.resolution > 0.0);
assert(info.resolution <= 1.0);
obj = PyFloat_FromDouble(info.resolution);
- if (obj == NULL)
+ if (obj == NULL) {
goto error;
- if (PyDict_SetItemString(dict, "resolution", obj) == -1)
+ }
+ if (PyDict_SetItemString(dict, "resolution", obj) == -1) {
goto error;
+ }
Py_CLEAR(obj);
ns = _PyNamespace_New(dict);
@@ -1284,14 +1482,17 @@ PyInit_timezone(PyObject *m) {
static PyMethodDef time_methods[] = {
{"time", time_time, METH_NOARGS, time_doc},
+ {"time_ns", time_time_ns, METH_NOARGS, time_ns_doc},
#ifdef PYCLOCK
{"clock", time_clock, METH_NOARGS, clock_doc},
#endif
#ifdef HAVE_CLOCK_GETTIME
{"clock_gettime", time_clock_gettime, METH_VARARGS, clock_gettime_doc},
+ {"clock_gettime_ns",time_clock_gettime_ns, METH_VARARGS, clock_gettime_ns_doc},
#endif
#ifdef HAVE_CLOCK_SETTIME
{"clock_settime", time_clock_settime, METH_VARARGS, clock_settime_doc},
+ {"clock_settime_ns",time_clock_settime_ns, METH_VARARGS, clock_settime_ns_doc},
#endif
#ifdef HAVE_CLOCK_GETRES
{"clock_getres", time_clock_getres, METH_VARARGS, clock_getres_doc},
@@ -1315,8 +1516,11 @@ static PyMethodDef time_methods[] = {
{"tzset", time_tzset, METH_NOARGS, tzset_doc},
#endif
{"monotonic", time_monotonic, METH_NOARGS, monotonic_doc},
+ {"monotonic_ns", time_monotonic_ns, METH_NOARGS, monotonic_ns_doc},
{"process_time", time_process_time, METH_NOARGS, process_time_doc},
+ {"process_time_ns", time_process_time_ns, METH_NOARGS, process_time_ns_doc},
{"perf_counter", time_perf_counter, METH_NOARGS, perf_counter_doc},
+ {"perf_counter_ns", time_perf_counter_ns, METH_NOARGS, perf_counter_ns_doc},
{"get_clock_info", time_get_clock_info, METH_VARARGS, get_clock_info_doc},
{NULL, NULL} /* sentinel */
};
@@ -1411,18 +1615,6 @@ PyInit_time(void)
return m;
}
-static PyObject*
-floattime(_Py_clock_info_t *info)
-{
- _PyTime_t t;
- if (_PyTime_GetSystemClockWithInfo(&t, info) < 0) {
- assert(info != NULL);
- return NULL;
- }
- return _PyFloat_FromPyTime(t);
-}
-
-
/* Implement pysleep() for various platforms.
When interrupted (or when another error occurs), return -1 and
set an exception; else return 0. */