[pypy-svn] r16213 - in pypy/dist/pypy: module/thread/rpython translator/c translator/c/src translator/c/test

arigo at codespeak.net arigo at codespeak.net
Mon Aug 22 16:56:17 CEST 2005


Author: arigo
Date: Mon Aug 22 16:55:59 2005
New Revision: 16213

Added:
   pypy/dist/pypy/translator/c/src/g_prerequisite.h   (contents, props changed)
   pypy/dist/pypy/translator/c/src/ll_thread.h   (contents, props changed)
   pypy/dist/pypy/translator/c/src/thread.h   (contents, props changed)
   pypy/dist/pypy/translator/c/src/thread_nt.h   (contents, props changed)
   pypy/dist/pypy/translator/c/src/thread_pthread.h   (contents, props changed)
Modified:
   pypy/dist/pypy/module/thread/rpython/ll_thread.py
   pypy/dist/pypy/translator/c/database.py
   pypy/dist/pypy/translator/c/extfunc.py
   pypy/dist/pypy/translator/c/genc.py
   pypy/dist/pypy/translator/c/node.py
   pypy/dist/pypy/translator/c/src/exception.h
   pypy/dist/pypy/translator/c/src/g_include.h
   pypy/dist/pypy/translator/c/test/test_extfunc.py
Log:
Starting the C back-end support for the 'thread' module.  Supported so
far: creating and destroying lock objects.

* extends database.py/node.py with an ExtTypeOpaqueDefNode corresponding to
  the inlined Opaque field.

* struct RPyOpaque_ThreadLock must be declared early, because it is the type
  of the above field.  Added a g_prerequisite.h that is included before the
  struct definitions generated by genc.

* thread support is split in several header files:
   - thread.h           selects between APIs depending on platform
   - thread_pthread.h   for pthread (Linux & co.)
   - thread_nt.h        for NT (not tested!!  copied from CPython)
   - ll_thread.h        included later (see comments in that file)



Modified: pypy/dist/pypy/module/thread/rpython/ll_thread.py
==============================================================================
--- pypy/dist/pypy/module/thread/rpython/ll_thread.py	(original)
+++ pypy/dist/pypy/module/thread/rpython/ll_thread.py	Mon Aug 22 16:55:59 2005
@@ -26,27 +26,27 @@
 ll_thread_get_ident.suggested_primitive = True
 
 
-def newlock(opaqueptr):
+def ll_newlock(opaqueptr):
     init_opaque_object(opaqueptr, thread.allocate_lock())
-newlock.suggested_primitive = True
+ll_newlock.suggested_primitive = True
 
-def acquirelock(opaqueptr, waitflag):
+def ll_acquirelock(opaqueptr, waitflag):
     lock = from_opaque_object(opaqueptr)
     return lock.acquire(waitflag)
-acquirelock.suggested_primitive = True
+ll_acquirelock.suggested_primitive = True
 
-def releaselock(opaqueptr):
+def ll_releaselock(opaqueptr):
     lock = from_opaque_object(opaqueptr)
     lock.release()
-releaselock.suggested_primitive = True
+ll_releaselock.suggested_primitive = True
 
 def ll_thread_allocate_lock():
     lockcontainer = malloc(LOCKCONTAINERTYPE)
-    newlock(lockcontainer.obj)
+    ll_newlock(lockcontainer.obj)
     return lockcontainer
 
 def ll_thread_acquire_lock(lockcontainer, waitflag):
-    return acquirelock(lockcontainer.obj, waitflag)
+    return ll_acquirelock(lockcontainer.obj, waitflag)
 
 def ll_thread_release_lock(lockcontainer):
-    releaselock(lockcontainer.obj)
+    ll_releaselock(lockcontainer.obj)

Modified: pypy/dist/pypy/translator/c/database.py
==============================================================================
--- pypy/dist/pypy/translator/c/database.py	(original)
+++ pypy/dist/pypy/translator/c/database.py	Mon Aug 22 16:55:59 2005
@@ -1,11 +1,11 @@
-from pypy.rpython.lltype import Primitive, Ptr, typeOf
+from pypy.rpython.lltype import Primitive, Ptr, typeOf, RuntimeTypeInfo
 from pypy.rpython.lltype import Struct, Array, FuncType, PyObject, Void
 from pypy.rpython.lltype import ContainerType, pyobjectptr, OpaqueType, GcStruct
 from pypy.objspace.flow.model import Constant
 from pypy.translator.c.primitive import PrimitiveName, PrimitiveType
 from pypy.translator.c.primitive import PrimitiveErrorValue
 from pypy.translator.c.node import StructDefNode, ArrayDefNode
-from pypy.translator.c.node import ContainerNodeClass
+from pypy.translator.c.node import ContainerNodeClass, ExtTypeOpaqueDefNode
 from pypy.translator.c.support import cdecl, CNameManager, ErrorValue
 from pypy.translator.c.pyobj import PyObjMaker
 
@@ -37,6 +37,8 @@
                 node = StructDefNode(self, T, varlength)
             elif isinstance(T, Array):
                 node = ArrayDefNode(self, T, varlength)
+            elif isinstance(T, OpaqueType) and hasattr(T, '_exttypeinfo'):
+                node = ExtTypeOpaqueDefNode(self, T)
             else:
                 raise Exception("don't know about %r" % (T,))
             self.structdefnodes[key] = node
@@ -70,8 +72,14 @@
             argtypes = ', '.join(argtypes) or 'void'
             return resulttype.replace('@', '(@)(%s)' % argtypes)
         elif isinstance(T, OpaqueType):
-            if T.tag == 'RuntimeTypeInfo':
+            if T == RuntimeTypeInfo:
                 return 'void (@)(void *)'   # void dealloc_xx(struct xx *)
+            elif hasattr(T, '_exttypeinfo'):
+                # for external types (pypy.rpython.extfunctable.declaretype())
+                node = self.gettypedefnode(T, varlength=varlength)
+                if who_asks is not None:
+                    who_asks.dependencies[node] = True
+                return 'struct %s @' % node.name
             else:
                 raise Exception("don't know about opaque type %r" % (T,))
         else:

Modified: pypy/dist/pypy/translator/c/extfunc.py
==============================================================================
--- pypy/dist/pypy/translator/c/extfunc.py	(original)
+++ pypy/dist/pypy/translator/c/extfunc.py	Mon Aug 22 16:55:59 2005
@@ -5,6 +5,7 @@
 from pypy.rpython.rstr import STR
 from pypy.rpython import rlist
 from pypy.rpython.module import ll_os, ll_time, ll_math, ll_strtod
+from pypy.module.thread.rpython import ll_thread
 
 
 # table of functions hand-written in src/ll_*.h
@@ -35,6 +36,7 @@
         'LL_strtod_parts_to_float',
     ll_strtod.ll_strtod_formatd:
         'LL_strtod_formatd',
+    ll_thread.ll_newlock:  'LL_thread_newlock',
     }
 
 #______________________________________________________
@@ -113,7 +115,10 @@
             lltype.pyobjectptr(pyexccls))
         # strange naming here because the macro name must be
         # a substring of PyExc_%s
-        yield ('RPyExc_%s' % pyexccls.__name__, exc_llvalue)
+        name = pyexccls.__name__
+        if pyexccls.__module__ != 'exceptions':
+            name = '%s_%s' % (pyexccls.__module__, name)
+        yield ('RPyExc_%s' % name, exc_llvalue)
 
 
 def predeclare_all(db, rtyper):

Modified: pypy/dist/pypy/translator/c/genc.py
==============================================================================
--- pypy/dist/pypy/translator/c/genc.py	(original)
+++ pypy/dist/pypy/translator/c/genc.py	Mon Aug 22 16:55:59 2005
@@ -200,7 +200,7 @@
     #
     for key, value in defines.items():
         print >> f, '#define %s %s' % (key, value)
-    print >> f, '#include "Python.h"'
+    print >> f, '#include "src/g_prerequisite.h"'
     includes = {}
     for node in database.globalcontainers():
         for include in node.includes:

Modified: pypy/dist/pypy/translator/c/node.py
==============================================================================
--- pypy/dist/pypy/translator/c/node.py	(original)
+++ pypy/dist/pypy/translator/c/node.py	Mon Aug 22 16:55:59 2005
@@ -131,7 +131,7 @@
             if self.static_deallocator:
                 yield 'void %s(struct %s *p) {' % (self.static_deallocator,
                                                    self.name)
-                for line in self.deallocator_lines('p->'):
+                for line in self.deallocator_lines('(*p)'):
                     yield '\t' + line
                 yield '\tOP_FREE(p);'
                 yield '}'
@@ -154,7 +154,7 @@
             FIELD_T = self.c_struct_field_type(name)
             cname = self.c_struct_field_name(name)
             for line in generic_dealloc(self.db,
-                                        '%s%s' % (prefix, cname),
+                                        '%s.%s' % (prefix, cname),
                                         FIELD_T):
                 yield line
 
@@ -227,7 +227,7 @@
 
         elif phase == 2 and self.deallocator:
             yield 'void %s(struct %s *a) {' % (self.deallocator, self.name)
-            for line in self.deallocator_lines('a->'):
+            for line in self.deallocator_lines('(*a)'):
                 yield '\t' + line
             yield '\tOP_FREE(a);'
             yield '}'
@@ -244,12 +244,12 @@
         body = list(generic_dealloc(self.db, '(*%s)' % varname, ARRAY.OF))
         if body:
             yield '{'
-            yield '\t%s = %sitems;' % (cdecl(self.itemtypename, '*' + varname),
-                                       prefix)
-            yield '\t%s = %s + %slength;' % (cdecl(self.itemtypename,
-                                                   '*%s_end' % varname),
-                                             varname,
-                                             prefix)
+            yield '\t%s = %s.items;' % (cdecl(self.itemtypename, '*' + varname),
+                                        prefix)
+            yield '\t%s = %s + %s.length;' % (cdecl(self.itemtypename,
+                                                    '*%s_end' % varname),
+                                              varname,
+                                              prefix)
             yield '\twhile (%s != %s_end) {' % (varname, varname)
             for line in body:
                 yield '\t\t' + line
@@ -268,6 +268,25 @@
             yield '-1'
 
 
+class ExtTypeOpaqueDefNode:
+    "For OpaqueTypes created by pypy.rpython.extfunctable.ExtTypeInfo."
+
+    def __init__(self, db, T):
+        self.db = db
+        self.T = T
+        self.dependencies = {}
+        self.name = 'RPyOpaque_%s' % (T.tag,)
+
+    def setup(self):
+        pass
+
+    def deallocator_lines(self, prefix):
+        yield 'RPyOpaqueDealloc_%s(&(%s));' % (self.T.tag, prefix)
+
+    def definition(self, phase):
+        return []
+
+
 def generic_dealloc(db, expr, T):
     if isinstance(T, Ptr) and T._needsgc():
         line = db.cdecrefstmt(expr, T)
@@ -275,7 +294,7 @@
             yield line
     elif isinstance(T, ContainerType):
         defnode = db.gettypedefnode(T)
-        for line in defnode.deallocator_lines('%s.' % expr):
+        for line in defnode.deallocator_lines(expr):
             yield line
 
 # ____________________________________________________________

Modified: pypy/dist/pypy/translator/c/src/exception.h
==============================================================================
--- pypy/dist/pypy/translator/c/src/exception.h	(original)
+++ pypy/dist/pypy/translator/c/src/exception.h	Mon Aug 22 16:55:59 2005
@@ -97,7 +97,7 @@
 #define RPyConvertExceptionToCPython(vanishing) vanishing = NULL  
 
 #define RPyRaiseSimpleException(exc, msg) \
-		PyErr_SetString(exc, msg)    /* pun */
+		PyErr_SetString(exc, msg)
 
 /******************************************************************/
 #endif                                             /* HAVE_RTYPER */

Modified: pypy/dist/pypy/translator/c/src/g_include.h
==============================================================================
--- pypy/dist/pypy/translator/c/src/g_include.h	(original)
+++ pypy/dist/pypy/translator/c/src/g_include.h	Mon Aug 22 16:55:59 2005
@@ -36,6 +36,7 @@
 #  include "src/ll_time.h"
 #  include "src/ll_math.h"
 #  include "src/ll_strtod.h"
+#  include "src/ll_thread.h"
 #endif
 
 #ifdef PYPY_STANDALONE

Added: pypy/dist/pypy/translator/c/src/g_prerequisite.h
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/c/src/g_prerequisite.h	Mon Aug 22 16:55:59 2005
@@ -0,0 +1,8 @@
+
+/**************************************************************/
+/***  this is included before any code produced by genc.py  ***/
+
+#include "Python.h"
+
+#include "thread.h"   /* needs to be included early to define the
+                         struct RPyOpaque_ThreadLock */

Added: pypy/dist/pypy/translator/c/src/ll_thread.h
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/c/src/ll_thread.h	Mon Aug 22 16:55:59 2005
@@ -0,0 +1,17 @@
+/************************************************************/
+ /***  C header subsection: OS-level threads               ***/
+
+
+/* The core comes from thread.h, which is included from g_prerequisite.h.
+   The functions below are declared later (from g_include.h). */
+
+/* this cannot be moved to thread_*.h because:
+ *  - RPyRaiseSimpleException and PyExc_thread_error are not declared yet
+ *  - the macro redefining LL_thread_newlock (produced by genc) is not defined
+ *    yet
+ */
+void LL_thread_newlock(struct RPyOpaque_ThreadLock *lock)
+{
+	if (!RPyThreadLockInit(lock))
+		RPyRaiseSimpleException(PyExc_thread_error, "out of resources");
+}

Added: pypy/dist/pypy/translator/c/src/thread.h
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/c/src/thread.h	Mon Aug 22 16:55:59 2005
@@ -0,0 +1,19 @@
+
+/* #ifdef logic from CPython */
+
+#ifndef _POSIX_THREADS
+/* This means pthreads are not implemented in libc headers, hence the macro
+   not present in unistd.h. But they still can be implemented as an external
+   library (e.g. gnu pth in pthread emulation) */
+# ifdef HAVE_PTHREAD_H
+#  include <pthread.h> /* _POSIX_THREADS */
+# endif
+#endif
+
+#ifdef _POSIX_THREADS
+#include "thread_pthread.h"
+#endif
+
+#ifdef NT_THREADS
+#include "thread_nt.h"
+#endif

Added: pypy/dist/pypy/translator/c/src/thread_nt.h
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/c/src/thread_nt.h	Mon Aug 22 16:55:59 2005
@@ -0,0 +1,125 @@
+/* Copy-and-pasted from CPython */
+
+/* This code implemented by Dag.Gruneau at elsa.preseco.comm.se */
+/* Fast NonRecursiveMutex support by Yakov Markovitch, markovitch at iso.ru */
+/* Eliminated some memory leaks, gsw at agere.com */
+
+#include <windows.h>
+#include <limits.h>
+#include <process.h>
+
+typedef struct RPyOpaque_ThreadLock {
+	LONG   owned ;
+	DWORD  thread_id ;
+	HANDLE hevent ;
+} NRMUTEX, *PNRMUTEX ;
+
+typedef PVOID WINAPI interlocked_cmp_xchg_t(PVOID *dest, PVOID exc, PVOID comperand) ;
+
+/* Sorry mate, but we haven't got InterlockedCompareExchange in Win95! */
+static PVOID WINAPI interlocked_cmp_xchg(PVOID *dest, PVOID exc, PVOID comperand)
+{
+	static LONG spinlock = 0 ;
+	PVOID result ;
+	DWORD dwSleep = 0;
+
+	/* Acqire spinlock (yielding control to other threads if cant aquire for the moment) */
+	while(InterlockedExchange(&spinlock, 1))
+	{
+		// Using Sleep(0) can cause a priority inversion.
+		// Sleep(0) only yields the processor if there's
+		// another thread of the same priority that's
+		// ready to run.  If a high-priority thread is
+		// trying to acquire the lock, which is held by
+		// a low-priority thread, then the low-priority
+		// thread may never get scheduled and hence never
+		// free the lock.  NT attempts to avoid priority
+		// inversions by temporarily boosting the priority
+		// of low-priority runnable threads, but the problem
+		// can still occur if there's a medium-priority
+		// thread that's always runnable.  If Sleep(1) is used,
+		// then the thread unconditionally yields the CPU.  We
+		// only do this for the second and subsequent even
+		// iterations, since a millisecond is a long time to wait
+		// if the thread can be scheduled in again sooner
+		// (~100,000 instructions).
+		// Avoid priority inversion: 0, 1, 0, 1,...
+		Sleep(dwSleep);
+		dwSleep = !dwSleep;
+	}
+	result = *dest ;
+	if (result == comperand)
+		*dest = exc ;
+	/* Release spinlock */
+	spinlock = 0 ;
+	return result ;
+} ;
+
+static interlocked_cmp_xchg_t *ixchg ;
+BOOL InitializeNonRecursiveMutex(PNRMUTEX mutex)
+{
+	if (!ixchg)
+	{
+		/* Sorely, Win95 has no InterlockedCompareExchange API (Win98 has), so we have to use emulation */
+		HANDLE kernel = GetModuleHandle("kernel32.dll") ;
+		if (!kernel || (ixchg = (interlocked_cmp_xchg_t *)GetProcAddress(kernel, "InterlockedCompareExchange")) == NULL)
+			ixchg = interlocked_cmp_xchg ;
+	}
+
+	mutex->owned = -1 ;  /* No threads have entered NonRecursiveMutex */
+	mutex->thread_id = 0 ;
+	mutex->hevent = CreateEvent(NULL, FALSE, FALSE, NULL) ;
+	return mutex->hevent != NULL ;	/* TRUE if the mutex is created */
+}
+
+#ifdef InterlockedCompareExchange
+#undef InterlockedCompareExchange
+#endif
+#define InterlockedCompareExchange(dest,exchange,comperand) (ixchg((dest), (exchange), (comperand)))
+
+VOID DeleteNonRecursiveMutex(PNRMUTEX mutex)
+{
+	/* No in-use check */
+	CloseHandle(mutex->hevent) ;
+	mutex->hevent = NULL ; /* Just in case */
+}
+
+DWORD EnterNonRecursiveMutex(PNRMUTEX mutex, BOOL wait)
+{
+	/* Assume that the thread waits successfully */
+	DWORD ret ;
+
+	/* InterlockedIncrement(&mutex->owned) == 0 means that no thread currently owns the mutex */
+	if (!wait)
+	{
+		if (InterlockedCompareExchange((PVOID *)&mutex->owned, (PVOID)0, (PVOID)-1) != (PVOID)-1)
+			return WAIT_TIMEOUT ;
+		ret = WAIT_OBJECT_0 ;
+	}
+	else
+		ret = InterlockedIncrement(&mutex->owned) ?
+			/* Some thread owns the mutex, let's wait... */
+			WaitForSingleObject(mutex->hevent, INFINITE) : WAIT_OBJECT_0 ;
+
+	mutex->thread_id = GetCurrentThreadId() ; /* We own it */
+	return ret ;
+}
+
+BOOL LeaveNonRecursiveMutex(PNRMUTEX mutex)
+{
+	/* We don't own the mutex */
+	mutex->thread_id = 0 ;
+	return
+		InterlockedDecrement(&mutex->owned) < 0 ||
+		SetEvent(mutex->hevent) ; /* Other threads are waiting, wake one on them up */
+}
+
+/************************************************************/
+
+#define RPyThreadLockInit(lock)  InitializeNonRecursiveMutex(lock)
+
+void RPyOpaqueDealloc_ThreadLock(struct RPyOpaque_ThreadLock *lock)
+{
+	if (lock->hevent != NULL)
+		DeleteNonRecursiveMutex(lock);
+}

Added: pypy/dist/pypy/translator/c/src/thread_pthread.h
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/c/src/thread_pthread.h	Mon Aug 22 16:55:59 2005
@@ -0,0 +1,129 @@
+
+/* Posix threads interface (from CPython) */
+
+#include <pthread.h>
+#include <errno.h>
+
+/* The POSIX spec says that implementations supporting the sem_*
+   family of functions must indicate this by defining
+   _POSIX_SEMAPHORES. */   
+#ifdef _POSIX_SEMAPHORES
+/* On FreeBSD 4.x, _POSIX_SEMAPHORES is defined empty, so 
+   we need to add 0 to make it work there as well. */
+#if (_POSIX_SEMAPHORES+0) == -1
+#define HAVE_BROKEN_POSIX_SEMAPHORES
+#else
+#include <semaphore.h>
+#endif
+#endif
+
+#if !defined(pthread_attr_default)
+#  define pthread_attr_default ((pthread_attr_t *)NULL)
+#endif
+#if !defined(pthread_mutexattr_default)
+#  define pthread_mutexattr_default ((pthread_mutexattr_t *)NULL)
+#endif
+#if !defined(pthread_condattr_default)
+#  define pthread_condattr_default ((pthread_condattr_t *)NULL)
+#endif
+
+/* Whether or not to use semaphores directly rather than emulating them with
+ * mutexes and condition variables:
+ */
+#if defined(_POSIX_SEMAPHORES) && !defined(HAVE_BROKEN_POSIX_SEMAPHORES)
+#  define USE_SEMAPHORES
+#else
+#  undef USE_SEMAPHORES
+#endif
+
+
+#define CHECK_STATUS(name)  if (status != 0) { perror(name); error = 1; }
+
+
+/************************************************************/
+#ifdef USE_SEMAPHORES
+/************************************************************/
+
+#include <semaphore.h>
+
+struct RPyOpaque_ThreadLock {
+	sem_t sem;
+	int initialized;
+};
+
+int RPyThreadLockInit(struct RPyOpaque_ThreadLock *lock)
+{
+	int status, error = 0;
+	lock->initialized = 0;
+	status = sem_init(&lock->sem, 0, 1);
+	CHECK_STATUS("sem_init");
+	if (error)
+		return 0;
+	lock->initialized = 1;
+	return 1;
+}
+
+void RPyOpaqueDealloc_ThreadLock(struct RPyOpaque_ThreadLock *lock)
+{
+	int status, error = 0;
+	if (lock->initialized) {
+		status = sem_destroy(&lock->sem);
+		CHECK_STATUS("sem_destroy");
+		/* 'error' is ignored;
+		   CHECK_STATUS already printed an error message */
+	}
+}
+
+/************************************************************/
+#else                                      /* no semaphores */
+/************************************************************/
+
+/* A pthread mutex isn't sufficient to model the Python lock type
+   (see explanations in CPython's Python/thread_pthread.h */
+struct RPyOpaque_ThreadLock {
+	char             locked; /* 0=unlocked, 1=locked */
+	char             initialized;
+	/* a <cond, mutex> pair to handle an acquire of a locked lock */
+	pthread_cond_t   lock_released;
+	pthread_mutex_t  mut;
+};
+
+int RPyThreadLockInit(struct RPyOpaque_ThreadLock *lock)
+{
+	int status, error = 0;
+
+	lock->initialized = 0;
+	lock->locked = 0;
+
+	status = pthread_mutex_init(&lock->mut,
+				    pthread_mutexattr_default);
+	CHECK_STATUS("pthread_mutex_init");
+
+	status = pthread_cond_init(&lock->lock_released,
+				   pthread_condattr_default);
+	CHECK_STATUS("pthread_cond_init");
+
+	if (error)
+		return 0;
+	lock->initialized = 1;
+	return 1;
+}
+
+void RPyOpaqueDealloc_ThreadLock(struct RPyOpaque_ThreadLock *lock)
+{
+	int status, error = 0;
+	if (lock->initialized) {
+		status = pthread_mutex_destroy(&lock->mut);
+		CHECK_STATUS("pthread_mutex_destroy");
+
+		status = pthread_cond_destroy(&lock->lock_released);
+		CHECK_STATUS("pthread_cond_destroy");
+
+		/* 'error' is ignored;
+		   CHECK_STATUS already printed an error message */
+	}
+}
+
+/************************************************************/
+#endif                                     /* no semaphores */
+/************************************************************/

Modified: pypy/dist/pypy/translator/c/test/test_extfunc.py
==============================================================================
--- pypy/dist/pypy/translator/c/test/test_extfunc.py	(original)
+++ pypy/dist/pypy/translator/c/test/test_extfunc.py	Mon Aug 22 16:55:59 2005
@@ -302,3 +302,18 @@
     assert f(0.0) == "0.00"
     assert f(1.5) == "1.50"
     assert f(2.0) == "2.00"
+
+def test_lock():
+    import thread
+    import pypy.module.thread.rpython.exttable   # for declare()/declaretype()
+    def fn():
+        l = thread.allocate_lock()
+        #ok1 = l.acquire(True)
+        #ok2 = l.acquire(False)
+        #l.release()
+        #ok3 = l.acquire(False)
+        #return ok1 and not ok2 and ok3
+        return True
+    f = compile(fn, [])
+    res = f()
+    assert res is True



More information about the Pypy-commit mailing list