[pypy-svn] r64145 - in pypy/trunk/pypy: module/thread module/thread/test translator/c/src translator/c/test

arigo at codespeak.net arigo at codespeak.net
Thu Apr 16 12:10:11 CEST 2009


Author: arigo
Date: Thu Apr 16 12:10:10 2009
New Revision: 64145

Modified:
   pypy/trunk/pypy/module/thread/__init__.py
   pypy/trunk/pypy/module/thread/app_thread.py
   pypy/trunk/pypy/module/thread/ll_thread.py
   pypy/trunk/pypy/module/thread/os_thread.py
   pypy/trunk/pypy/module/thread/test/test_thread.py
   pypy/trunk/pypy/translator/c/src/thread_nt.h
   pypy/trunk/pypy/translator/c/src/thread_pthread.h
   pypy/trunk/pypy/translator/c/test/test_standalone.py
Log:
(cfbolz, arigo)
Implement thread.stack_size().  (NT implementation not tested)


Modified: pypy/trunk/pypy/module/thread/__init__.py
==============================================================================
--- pypy/trunk/pypy/module/thread/__init__.py	(original)
+++ pypy/trunk/pypy/module/thread/__init__.py	Thu Apr 16 12:10:10 2009
@@ -7,13 +7,13 @@
         'exit':                   'app_thread.exit',
         'exit_thread':            'app_thread.exit',   # obsolete synonym
         'error':                  'app_thread.error',
-        'stack_size':             'app_thread.stack_size',
     }
 
     interpleveldefs = {
         'start_new_thread':       'os_thread.start_new_thread',
         'start_new':              'os_thread.start_new_thread', # obsolete syn.
         'get_ident':              'os_thread.get_ident',
+        'stack_size':             'os_thread.stack_size',
         'allocate_lock':          'os_lock.allocate_lock',
         'allocate':               'os_lock.allocate_lock',  # obsolete synonym
         'LockType':               'os_lock.getlocktype(space)',

Modified: pypy/trunk/pypy/module/thread/app_thread.py
==============================================================================
--- pypy/trunk/pypy/module/thread/app_thread.py	(original)
+++ pypy/trunk/pypy/module/thread/app_thread.py	Thu Apr 16 12:10:10 2009
@@ -5,8 +5,3 @@
     """This is synonymous to ``raise SystemExit''.  It will cause the current
 thread to exit silently unless the exception is caught."""
     raise SystemExit
-
-def stack_size(size=0):
-    if size == 0:
-        return 0
-    raise error("not implemented")

Modified: pypy/trunk/pypy/module/thread/ll_thread.py
==============================================================================
--- pypy/trunk/pypy/module/thread/ll_thread.py	(original)
+++ pypy/trunk/pypy/module/thread/ll_thread.py	Thu Apr 16 12:10:10 2009
@@ -119,6 +119,14 @@
 
 # ____________________________________________________________
 #
+# Stack size
+
+get_stacksize = llexternal('RPyThreadGetStackSize', [], lltype.Signed)
+set_stacksize = llexternal('RPyThreadSetStackSize', [lltype.Signed],
+                           lltype.Signed)
+
+# ____________________________________________________________
+#
 # GIL support wrappers
 
 null_ll_lock = lltype.nullptr(TLOCKP.TO)

Modified: pypy/trunk/pypy/module/thread/os_thread.py
==============================================================================
--- pypy/trunk/pypy/module/thread/os_thread.py	(original)
+++ pypy/trunk/pypy/module/thread/os_thread.py	Thu Apr 16 12:10:10 2009
@@ -170,3 +170,35 @@
 A thread's identity may be reused for another thread after it exits."""
     ident = thread.get_ident()
     return space.wrap(ident)
+
+def stack_size(space, size=0):
+    """stack_size([size]) -> size
+
+Return the thread stack size used when creating new threads.  The
+optional size argument specifies the stack size (in bytes) to be used
+for subsequently created threads, and must be 0 (use platform or
+configured default) or a positive integer value of at least 32,768 (32k).
+If changing the thread stack size is unsupported, a ThreadError
+exception is raised.  If the specified size is invalid, a ValueError
+exception is raised, and the stack size is unmodified.  32k bytes
+is currently the minimum supported stack size value to guarantee
+sufficient stack space for the interpreter itself.
+
+Note that some platforms may have particular restrictions on values for
+the stack size, such as requiring a minimum stack size larger than 32kB or
+requiring allocation in multiples of the system memory page size
+- platform documentation should be referred to for more information
+(4kB pages are common; using multiples of 4096 for the stack size is
+the suggested approach in the absence of more specific information)."""
+    if size < 0:
+        raise OperationError(space.w_ValueError,
+                             space.wrap("size must be 0 or a positive value"))
+    old_size = thread.get_stacksize()
+    error = thread.set_stacksize(size)
+    if error == -1:
+        raise OperationError(space.w_ValueError,
+                             space.wrap("size not valid: %d bytes" % size))
+    if error == -2:
+        raise wrap_thread_error(space, "setting stack size not supported")
+    return space.wrap(old_size)
+stack_size.unwrap_spec = [ObjSpace, int]

Modified: pypy/trunk/pypy/module/thread/test/test_thread.py
==============================================================================
--- pypy/trunk/pypy/module/thread/test/test_thread.py	(original)
+++ pypy/trunk/pypy/module/thread/test/test_thread.py	Thu Apr 16 12:10:10 2009
@@ -187,3 +187,15 @@
             pass
         else:
             raise Exception("could unexpectedly start 1000 threads")
+
+    def test_stack_size(self):
+        import thread
+        thread.stack_size(0)
+        res = thread.stack_size(0)
+        assert res == 0
+        res = thread.stack_size(1024*1024)
+        assert res == 0
+        res = thread.stack_size(2*1024*1024)
+        assert res == 1024*1024
+        res = thread.stack_size(0)
+        assert res == 2*1024*1024

Modified: pypy/trunk/pypy/translator/c/src/thread_nt.h
==============================================================================
--- pypy/trunk/pypy/translator/c/src/thread_nt.h	(original)
+++ pypy/trunk/pypy/translator/c/src/thread_nt.h	Thu Apr 16 12:10:10 2009
@@ -48,6 +48,8 @@
 
 #ifndef PYPY_NOT_MAIN_FILE
 
+static long _pypythread_stacksize = 0;
+
 /*
  * Return the thread Id instead of an handle. The Id is said to uniquely
    identify the thread in the system
@@ -81,7 +83,7 @@
 	if (obj.done == NULL)
 		return -1;
 
-	rv = _beginthread(bootstrap, 0, &obj); /* use default stack size */
+	rv = _beginthread(bootstrap, _pypythread_stacksize, &obj);
 	if (rv == (unsigned long)-1) {
 		/* I've seen errno == EAGAIN here, which means "there are
 		 * too many threads".
@@ -99,6 +101,32 @@
 
 /************************************************************/
 
+/* minimum/maximum thread stack sizes supported */
+#define THREAD_MIN_STACKSIZE    0x8000      /* 32kB */
+#define THREAD_MAX_STACKSIZE    0x10000000  /* 256MB */
+
+long RPyThreadGetStackSize(void)
+{
+	return _pypythread_stacksize;
+}
+
+long RPyThreadSetStackSize(long newsize)
+{
+	if (newsize == 0) {    /* set to default */
+		_pypythread_stacksize = 0;
+		return 0;
+	}
+
+	/* check the range */
+	if (newsize >= THREAD_MIN_STACKSIZE && size < THREAD_MAX_STACKSIZE) {
+		_pypythread_stacksize = newsize;
+		return 0;
+	}
+	return -1;
+}
+
+/************************************************************/
+
 
 typedef PVOID WINAPI interlocked_cmp_xchg_t(PVOID *dest, PVOID exc, PVOID comperand) ;
 

Modified: pypy/trunk/pypy/translator/c/src/thread_pthread.h
==============================================================================
--- pypy/trunk/pypy/translator/c/src/thread_pthread.h	(original)
+++ pypy/trunk/pypy/translator/c/src/thread_pthread.h	Thu Apr 16 12:10:10 2009
@@ -80,12 +80,28 @@
 void RPyOpaqueDealloc_ThreadLock(struct RPyOpaque_ThreadLock *lock);
 int RPyThreadAcquireLock(struct RPyOpaque_ThreadLock *lock, int waitflag);
 void RPyThreadReleaseLock(struct RPyOpaque_ThreadLock *lock);
+long RPyThreadGetStackSize(void);
+long RPyThreadSetStackSize(long);
 
 
 /* implementations */
 
 #ifndef PYPY_NOT_MAIN_FILE
 
+/* The POSIX spec requires that use of pthread_attr_setstacksize
+   be conditional on _POSIX_THREAD_ATTR_STACKSIZE being defined. */
+#ifdef _POSIX_THREAD_ATTR_STACKSIZE
+# ifndef THREAD_STACK_SIZE
+#  define THREAD_STACK_SIZE   0   /* use default stack size */
+# endif
+/* for safety, ensure a viable minimum stacksize */
+# define THREAD_STACK_MIN    0x8000  /* 32kB */
+#else  /* !_POSIX_THREAD_ATTR_STACKSIZE */
+# ifdef THREAD_STACK_SIZE
+#  error "THREAD_STACK_SIZE defined but _POSIX_THREAD_ATTR_STACKSIZE undefined"
+# endif
+#endif
+
 /* XXX This implementation is considered (to quote Tim Peters) "inherently
    hosed" because:
      - It does not guarantee the promise that a non-zero integer is returned.
@@ -105,6 +121,8 @@
 #endif
 }
 
+static long _pypythread_stacksize = 0;
+
 static void *bootstrap_pthread(void *func)
 {
   ((void(*)(void))func)();
@@ -118,12 +136,18 @@
 #if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED)
 	pthread_attr_t attrs;
 #endif
+#if defined(THREAD_STACK_SIZE)
+	size_t tss;
+#endif
 
 #if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED)
 	pthread_attr_init(&attrs);
 #endif
 #ifdef THREAD_STACK_SIZE
-	pthread_attr_setstacksize(&attrs, THREAD_STACK_SIZE);
+	tss = (_pypythread_stacksize != 0) ? _pypythread_stacksize
+		: THREAD_STACK_SIZE;
+	if (tss != 0)
+		pthread_attr_setstacksize(&attrs, tss);
 #endif
 #if defined(PTHREAD_SYSTEM_SCHED_SUPPORTED) && !defined(__FreeBSD__)
         pthread_attr_setscope(&attrs, PTHREAD_SCOPE_SYSTEM);
@@ -154,6 +178,47 @@
 #endif
 }
 
+long RPyThreadGetStackSize(void)
+{
+	return _pypythread_stacksize;
+}
+
+long RPyThreadSetStackSize(long newsize)
+{
+#if defined(THREAD_STACK_SIZE)
+	pthread_attr_t attrs;
+	size_t tss_min;
+	int rc;
+#endif
+
+	if (newsize == 0) {    /* set to default */
+		_pypythread_stacksize = 0;
+		return 0;
+	}
+
+#if defined(THREAD_STACK_SIZE)
+# if defined(PTHREAD_STACK_MIN)
+	tss_min = PTHREAD_STACK_MIN > THREAD_STACK_MIN ? PTHREAD_STACK_MIN
+		: THREAD_STACK_MIN;
+# else
+	tss_min = THREAD_STACK_MIN;
+# endif
+	if (newsize >= tss_min) {
+		/* validate stack size by setting thread attribute */
+		if (pthread_attr_init(&attrs) == 0) {
+			rc = pthread_attr_setstacksize(&attrs, newsize);
+			pthread_attr_destroy(&attrs);
+			if (rc == 0) {
+				_pypythread_stacksize = newsize;
+				return 0;
+			}
+		}
+	}
+	return -1;
+#else
+	return -2;
+#endif
+}
 
 /************************************************************/
 #ifdef USE_SEMAPHORES

Modified: pypy/trunk/pypy/translator/c/test/test_standalone.py
==============================================================================
--- pypy/trunk/pypy/translator/c/test/test_standalone.py	(original)
+++ pypy/trunk/pypy/translator/c/test/test_standalone.py	Thu Apr 16 12:10:10 2009
@@ -268,3 +268,54 @@
 
     def test_prof_inline(self):
         py.test.skip("Unsupported")
+
+
+class TestThread(object):
+    def test_stack_size(self):
+        from pypy.module.thread import ll_thread
+
+        class State:
+            pass
+        state = State()
+
+        def recurse(n):
+            if n > 0:
+                return recurse(n-1)+1
+            else:
+                return 0
+
+        def bootstrap():
+            # recurse a lot, like 10000 times
+            recurse(10000)
+            state.ll_lock.release()
+
+        def entry_point(argv):
+            os.write(1, "hello world\n")
+            error = ll_thread.set_stacksize(int(argv[1]))
+            assert error == 0
+            # start a new thread
+            state.ll_lock = ll_thread.Lock(ll_thread.allocate_ll_lock())
+            state.ll_lock.acquire(True)
+            ident = ll_thread.start_new_thread(bootstrap, ())
+            # wait for the thread to finish
+            state.ll_lock.acquire(True)
+            os.write(1, "done\n")
+            return 0
+
+        t = TranslationContext()
+        t.buildannotator().build_types(entry_point, [s_list_of_strings])
+        t.buildrtyper().specialize()
+
+        cbuilder = CStandaloneBuilder(t, entry_point, t.config)
+        cbuilder.generate_source()
+        cbuilder.compile()
+
+        # this should work
+        data = cbuilder.cmdexec(str(2*1024*1024))   # 2 MB: should work
+        assert data == 'hello world\ndone\n'
+
+        # this should crash
+        exc_info = py.test.raises(Exception,
+                                  cbuilder.cmdexec,
+                                  str(32*1024))   # 32 KB: crash
+        assert exc_info.type is Exception    # segfault!



More information about the Pypy-commit mailing list