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/Python/thread_pthread.h b/Python/thread_pthread.h
index 4305a19..6088c71 100644
--- a/Python/thread_pthread.h
+++ b/Python/thread_pthread.h
@@ -83,6 +83,26 @@
 #endif
 
 
+/* We assume all modern POSIX systems have gettimeofday() */
+#ifdef GETTIMEOFDAY_NO_TZ
+#define GETTIMEOFDAY(ptv) gettimeofday(ptv)
+#else
+#define GETTIMEOFDAY(ptv) gettimeofday(ptv, (struct timezone *)NULL)
+#endif
+
+#define MICROSECONDS_TO_TIMESPEC(microseconds, ts) \
+do { \
+	struct timeval tv; \
+	GETTIMEOFDAY(&tv); \
+	tv.tv_usec += microseconds % 1000000; \
+	tv.tv_sec += microseconds / 1000000; \
+	tv.tv_sec += tv.tv_usec / 1000000; \
+	tv.tv_usec %= 1000000; \
+	ts.tv_sec = tv.tv_sec; \
+	ts.tv_nsec = tv.tv_usec * 1000; \
+} while(0)
+
+
 /* A pthread mutex isn't sufficient to model the Python lock type
  * because, according to Draft 5 of the docs (P1003.4a/D5), both of the
  * following are undefined:
@@ -295,34 +315,53 @@
 	return (status == -1) ? errno : status;
 }
 
-int 
-PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
+int
+PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds)
 {
 	int success;
 	sem_t *thelock = (sem_t *)lock;
 	int status, error = 0;
+	struct timespec ts;
 
-	dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, waitflag));
+	dprintf(("PyThread_acquire_lock_timed(%p, %lld) called\n",
+		 lock, microseconds));
 
+	if (microseconds > 0)
+		MICROSECONDS_TO_TIMESPEC(microseconds, ts);
 	do {
-		if (waitflag)
-			status = fix_status(sem_wait(thelock));
-		else
+		if (microseconds > 0)
+			status = fix_status(sem_timedwait(thelock, &ts));
+		else if (microseconds == 0)
 			status = fix_status(sem_trywait(thelock));
+		else
+			status = fix_status(sem_wait(thelock));
 	} while (status == EINTR); /* Retry if interrupted by a signal */
 
-	if (waitflag) {
+	if (microseconds > 0) {
+		if (status != ETIMEDOUT)
+			CHECK_STATUS("sem_timedwait");
+	}
+	else if (microseconds == 0) {
+		if (status != EAGAIN)
+			CHECK_STATUS("sem_trywait");
+	}
+	else {
 		CHECK_STATUS("sem_wait");
-	} else if (status != EAGAIN) {
-		CHECK_STATUS("sem_trywait");
 	}
 	
 	success = (status == 0) ? 1 : 0;
 
-	dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success));
+	dprintf(("PyThread_acquire_lock_timed(%p, %lld) -> %d\n",
+		 lock, microseconds, success));
 	return success;
 }
 
+int 
+PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
+{
+	return PyThread_acquire_lock_timed(lock, waitflag ? -1 : 0);
+}
+
 void 
 PyThread_release_lock(PyThread_type_lock lock)
 {
@@ -390,40 +429,62 @@
 	free((void *)thelock);
 }
 
-int 
-PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
+int
+PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds)
 {
 	int success;
 	pthread_lock *thelock = (pthread_lock *)lock;
 	int status, error = 0;
 
-	dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, waitflag));
+	dprintf(("PyThread_acquire_lock_timed(%p, %lld) called\n",
+		 lock, microseconds));
 
 	status = pthread_mutex_lock( &thelock->mut );
 	CHECK_STATUS("pthread_mutex_lock[1]");
 	success = thelock->locked == 0;
 
-	if ( !success && waitflag ) {
+	if (!success && microseconds != 0) {
+		struct timespec ts;
+		if (microseconds > 0)
+			MICROSECONDS_TO_TIMESPEC(microseconds, ts);
 		/* continue trying until we get the lock */
 
 		/* mut must be locked by me -- part of the condition
 		 * protocol */
-		while ( thelock->locked ) {
-			status = pthread_cond_wait(&thelock->lock_released,
-						   &thelock->mut);
-			CHECK_STATUS("pthread_cond_wait");
+		while (thelock->locked) {
+			if (microseconds > 0) {
+				status = pthread_cond_timedwait(
+					&thelock->lock_released,
+					&thelock->mut, &ts);
+				if (status == ETIMEDOUT)
+					break;
+				CHECK_STATUS("pthread_cond_timed_wait");
+			}
+			else {
+				status = pthread_cond_wait(
+					&thelock->lock_released,
+					&thelock->mut);
+				CHECK_STATUS("pthread_cond_wait");
+			}
 		}
-		success = 1;
+		success = (status == 0);
 	}
 	if (success) thelock->locked = 1;
 	status = pthread_mutex_unlock( &thelock->mut );
 	CHECK_STATUS("pthread_mutex_unlock[1]");
 
 	if (error) success = 0;
-	dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success));
+	dprintf(("PyThread_acquire_lock_timed(%p, %lld) -> %d\n",
+		 lock, microseconds, success));
 	return success;
 }
 
+int 
+PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
+{
+	return PyThread_acquire_lock_timed(lock, waitflag ? -1 : 0);
+}
+
 void 
 PyThread_release_lock(PyThread_type_lock lock)
 {