Python threading (was: Re: global interpreter lock not working as it should)

Bengt Richter bokr at oz.net
Tue Aug 6 03:57:29 CEST 2002


On 05 Aug 2002 23:52:35 +0200, martin at v.loewis.de (Martin v. Loewis) wrote:

>bokr at oz.net (Bengt Richter) writes:
>
>> Hm. I suppose the pthread_lock is supposed to be general purpose,
>> not just for the GIL, 
>
>Yes, that is for threading.lock.acquire.
>
>> but ISTM (;-) you could easily add serial handover functionality if
>> you wanted to use that for the GIL.
>
>Not easily. If you want to add another threading primitive, you need
>to add it for all 11 threading libraries.
>
>Since nobody has simultaneous access to all 11 platforms, such a
>change is nearly impossible to implement.
I thought it might be possible to do just with changes to thread_pthread.h
(and the corresponding other  10 thread_*.h files), which I thought Python
was free to do whatever it liked with, for Python's purposes.

Here is a sketched diff OTTOMH (not tested at all, and not much
hope of working as is, but I hope it gives the idea): 
=====================================================================
--- python\thread_pthread.h     Fri Sep 01 16:29:28 2000
+++ python_x\thread_pthread.h   Mon Aug 05 18:31:55 2002
@@ -1,5 +1,6 @@

 /* Posix threads interface */
+/* Experiment for lock handover on release if waiter present */

 #include <stdlib.h>
 #include <string.h>
@@ -82,6 +83,7 @@
        /* a <cond, mutex> pair to handle an acquire of a locked lock */
        pthread_cond_t   lock_released;
        pthread_mutex_t  mut;
+       int              waiters;   /* number of threads waiting on lock_released */
 } pthread_lock;

 #define CHECK_STATUS(name)  if (status != 0) { perror(name); error = 1; }
@@ -250,6 +252,7 @@
        memset((void *)lock, '\0', sizeof(pthread_lock));
        if (lock) {
                lock->locked = 0;
+               lock->waiters = 0;

                status = pthread_mutex_init(&lock->mut,
                                            pthread_mutexattr_default);
@@ -310,9 +313,11 @@
                status = pthread_mutex_lock( &thelock->mut );
                CHECK_STATUS("pthread_mutex_lock[2]");
                while ( thelock->locked ) {
+                       ++(thelock->waiters)
                        status = pthread_cond_wait(&thelock->lock_released,
                                                   &thelock->mut);
                        CHECK_STATUS("pthread_cond_wait");
+                       --(thelock->waiters);
                }
                thelock->locked = 1;
                status = pthread_mutex_unlock( &thelock->mut );
@@ -335,7 +340,34 @@
        status = pthread_mutex_lock( &thelock->mut );
        CHECK_STATUS("pthread_mutex_lock[3]");

+       if( thelock->waiters ){
+               --(thelock->waiters);
+       }
        thelock->locked = 0;
+
+       status = pthread_mutex_unlock( &thelock->mut );
+       CHECK_STATUS("pthread_mutex_unlock[3]");
+
+       /* wake up someone (anyone, if any) waiting on the lock */
+       status = pthread_cond_signal( &thelock->lock_released );
+       CHECK_STATUS("pthread_cond_signal");
+}
+
+PyThread_handover_lock(PyThread_type_lock lock)
+{
+       pthread_lock *thelock = (pthread_lock *)lock;
+       int status, error = 0;
+
+       dprintf(("PyThread_release_lock(%p) called\n", lock));
+
+       status = pthread_mutex_lock( &thelock->mut );
+       CHECK_STATUS("pthread_mutex_lock[3]");
+
+       if( thelock->waiters ){
+               --(thelock->waiters);   /* hand over, don't unlock */
+       } else {
+               thelock->locked = 0;
+       }

        status = pthread_mutex_unlock( &thelock->mut );
        CHECK_STATUS("pthread_mutex_unlock[3]");
=====================================================================

>
>> It might be useful for other things too.
>
>Things that are useful are implemented in threading.py.
>
That looks interesting.

>> Signaling the condition variable seems to use kill(th->p_pid,
>> PTHREAD_SIG_RESTART) to start a waiting thread. Does that affect
>> scheduling order between it and the releasing thread?
>
>Usage of this mechanism to signal the condition variable, and the
>system reaction to a signal, are both highly system dependent. You'd
>have to pick a specific OS, OS release, and threading library release
>to answer this question.
>
Ok, pass on that for now ;-)

>> My impression was that it requires the mutex to be locked when called,
>> and unlocks it itself so as to wait unlocked, and then re-locks it for
>> the waiter. I.e., inside pthread_cond_wait:
>>   ...
>>   pthread_mutex_unlock(mutex);
>>   suspend_with_cancellation(self);
>>   pthread_mutex_lock(mutex);
>>   ...
>> 
>> I guess the re-lock involves trying, though -- is that a tiny crack
>> for Murphy to sneak through?
>> 
>> Ah, I guess that's why there's that 'while' in PyThread_acquire_lock:
>
>POSIX specifies that pthread_cond_wait can return spontaneously, which
When my computer acts "spontaneous" I worry ;-) What kinds of spontaneity
are anticipated?

>alone is the reason for the while loop. However, I do think that the
>woken-up thread and the thread that just signalled the condition do
>compete to lock the mutex (when ceval tries to lock the GIL again).
>
It looks that way, yes.

>This is the race condition that I was referring to earlier, and it is
>another reason for the while loop.
>
>> Thanks for your pointers. BTW, the source for the linuxthreads (or
>> whatever pthread package is actually used) is (AFAICS) not included
>> in the win32 python distribution. Might this be a good idea for
>> cross-platform documentation purposes?
>
>You want the source of linuxthreads in the Python distribution???
Yes, some kind of reference implementation to look at. I thought Linux
would be a good candidate. I guess it's expected to be part of glibc2
though, so it would just be extracted for a reference.

I guess more practical would be a few URLs, but e.g. RedHat's top site page
doesn't do it. (Although you can get web CVS access to all the sources,
so that's nice). It would just be nice to have a leg up on finding the
exact things. BTW, is there a Python module that can unpack rpms?

>Or do you mean the source of thread_pthread.h of Python? The latter
>is available only with the source code of Python (as all other 10
>thread_*.h files).
No, I have those (for 2.1 anyway).
>
>I do believe that linuxthreads is quite specific to linux, and that
>you find that condition variables are implemented quite different on
>other systems.
>
That'd be ok, so long as the same thread_*.h interface can be synthesized.

>Furthermore, Python 2.3 will use POSIX semaphores for threading.lock
>where available - which will add another dimension to this issue.
>
>This all comes back to the start of the thread:
>
>Other threads can run when the GIL is released; that does not mean
>they will run.
Well, I come back to the question of whether and/or when we want to enforce
handover of the lock, or otherwise modify the way it's held. It would be nice
to know what the design intent for the optimum was, even if that can't always
be achieved. But the clearer that is, the more likely someone around the globe
will come up with an answer.

Regards,
Bengt Richter



More information about the Python-list mailing list