Issue #7316: the acquire() method of lock objects in the :mod:`threading`
module now takes an optional timeout argument in seconds.  Timeout support
relies on the system threading library, so as to avoid a semi-busy wait
loop.
diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c
index 58aa231..5a7cf79 100644
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -40,18 +40,47 @@
 }
 
 static PyObject *
-lock_PyThread_acquire_lock(lockobject *self, PyObject *args)
+lock_PyThread_acquire_lock(lockobject *self, PyObject *args, PyObject *kwds)
 {
-	int i = 1;
+	char *kwlist[] = {"blocking", "timeout", NULL};
+	int blocking = 1;
+	double timeout = -1;
+	PY_TIMEOUT_T microseconds;
+	int r;
 
-	if (!PyArg_ParseTuple(args, "|i:acquire", &i))
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|id:acquire", kwlist,
+					 &blocking, &timeout))
 		return NULL;
 
+	if (!blocking && timeout != -1) {
+		PyErr_SetString(PyExc_ValueError, "can't specify a timeout "
+				"for a non-blocking call");
+		return NULL;
+	}
+	if (timeout < 0 && timeout != -1) {
+		PyErr_SetString(PyExc_ValueError, "timeout value must be "
+				"strictly positive");
+		return NULL;
+	}
+	if (!blocking)
+		microseconds = 0;
+	else if (timeout == -1)
+		microseconds = -1;
+	else {
+		timeout *= 1e6;
+		if (timeout >= (double) PY_TIMEOUT_MAX) {
+			PyErr_SetString(PyExc_OverflowError,
+					"timeout value is too large");
+			return NULL;
+		}
+		microseconds = (PY_TIMEOUT_T) timeout;
+	}
+	
 	Py_BEGIN_ALLOW_THREADS
-	i = PyThread_acquire_lock(self->lock_lock, i);
+	r = PyThread_acquire_lock_timed(self->lock_lock, microseconds);
 	Py_END_ALLOW_THREADS
 
-	return PyBool_FromLong((long)i);
+	return PyBool_FromLong(r);
 }
 
 PyDoc_STRVAR(acquire_doc,
@@ -106,9 +135,9 @@
 
 static PyMethodDef lock_methods[] = {
 	{"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock, 
-	 METH_VARARGS, acquire_doc},
+	 METH_VARARGS | METH_KEYWORDS, acquire_doc},
 	{"acquire",      (PyCFunction)lock_PyThread_acquire_lock, 
-	 METH_VARARGS, acquire_doc},
+	 METH_VARARGS | METH_KEYWORDS, acquire_doc},
 	{"release_lock", (PyCFunction)lock_PyThread_release_lock, 
 	 METH_NOARGS, release_doc},
 	{"release",      (PyCFunction)lock_PyThread_release_lock, 
@@ -118,7 +147,7 @@
 	{"locked",       (PyCFunction)lock_locked_lock,  
 	 METH_NOARGS, locked_doc},
 	{"__enter__",    (PyCFunction)lock_PyThread_acquire_lock,
-	 METH_VARARGS, acquire_doc},
+	 METH_VARARGS | METH_KEYWORDS, acquire_doc},
 	{"__exit__",    (PyCFunction)lock_PyThread_release_lock,
 	 METH_VARARGS, release_doc},
 	{NULL,           NULL}		/* sentinel */
@@ -183,15 +212,41 @@
 static PyObject *
 rlock_acquire(rlockobject *self, PyObject *args, PyObject *kwds)
 {
-	char *kwlist[] = {"blocking", NULL};
+	char *kwlist[] = {"blocking", "timeout", NULL};
 	int blocking = 1;
+	double timeout = -1;
+	PY_TIMEOUT_T microseconds;
 	long tid;
 	int r = 1;
 
-	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i:acquire", kwlist,
-				         &blocking))
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|id:acquire", kwlist,
+					 &blocking, &timeout))
 		return NULL;
 
+	if (!blocking && timeout != -1) {
+		PyErr_SetString(PyExc_ValueError, "can't specify a timeout "
+				"for a non-blocking call");
+		return NULL;
+	}
+	if (timeout < 0 && timeout != -1) {
+		PyErr_SetString(PyExc_ValueError, "timeout value must be "
+				"strictly positive");
+		return NULL;
+	}
+	if (!blocking)
+		microseconds = 0;
+	else if (timeout == -1)
+		microseconds = -1;
+	else {
+		timeout *= 1e6;
+		if (timeout >= (double) PY_TIMEOUT_MAX) {
+			PyErr_SetString(PyExc_OverflowError,
+					"timeout value is too large");
+			return NULL;
+		}
+		microseconds = (PY_TIMEOUT_T) timeout;
+	}
+	
 	tid = PyThread_get_thread_ident();
 	if (self->rlock_count > 0 && tid == self->rlock_owner) {
 		unsigned long count = self->rlock_count + 1;
@@ -206,11 +261,11 @@
 
 	if (self->rlock_count > 0 ||
 	    !PyThread_acquire_lock(self->rlock_lock, 0)) {
-		if (!blocking) {
+		if (microseconds == 0) {
 			Py_RETURN_FALSE;
 		}
 		Py_BEGIN_ALLOW_THREADS
-		r = PyThread_acquire_lock(self->rlock_lock, blocking);
+		r = PyThread_acquire_lock_timed(self->rlock_lock, microseconds);
 		Py_END_ALLOW_THREADS
 	}
 	if (r) {
@@ -1005,7 +1060,7 @@
 PyMODINIT_FUNC
 PyInit__thread(void)
 {
-	PyObject *m, *d;
+	PyObject *m, *d, *timeout_max;
 	
 	/* Initialize types: */
 	if (PyType_Ready(&localtype) < 0)
@@ -1020,6 +1075,12 @@
 	if (m == NULL)
 		return NULL;
 
+	timeout_max = PyFloat_FromDouble(PY_TIMEOUT_MAX / 1000000);
+	if (!timeout_max)
+		return NULL;
+	if (PyModule_AddObject(m, "TIMEOUT_MAX", timeout_max) < 0)
+		return NULL;
+
 	/* Add a symbolic constant */
 	d = PyModule_GetDict(m);
 	ThreadError = PyErr_NewException("_thread.error", NULL, NULL);