Skip to content

Commit a9e288a

Browse files
author
andrew.macintyre
committed
Patch #1454481: Make thread stack size runtime tunable.
git-svn-id: http://svn.python.org/projects/python/trunk@46640 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent c379fe3 commit a9e288a

14 files changed

Lines changed: 332 additions & 5 deletions

File tree

Doc/lib/libthread.tex

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,25 @@ \section{\module{thread} ---
7474
another thread is created.
7575
\end{funcdesc}
7676

77+
\begin{funcdesc}{stack_size}{\optional{size}}
78+
Return the thread stack size used when creating new threads. The
79+
optional \var{size} argument specifies the stack size to be used for
80+
subsequently created threads, and must be 0 (use platform or
81+
configured default) or a positive integer value of at least 32,768 (32kB).
82+
If changing the thread stack size is unsupported, or the specified size
83+
is invalid, a RuntimeWarning is issued and the stack size is unmodified.
84+
32kB is currently the minimum supported stack size value, to guarantee
85+
sufficient stack space for the interpreter itself.
86+
Note that some platforms may have particular restrictions on values for
87+
the stack size, such as requiring allocation in multiples of the system
88+
memory page size - platform documentation should be referred to for more
89+
information (4kB pages are common; using multiples of 4096 for the
90+
stack size is the suggested approach in the absence of more specific
91+
information).
92+
Availability: Windows, systems with \POSIX{} threads.
93+
\versionadded{2.5}
94+
\end{funcdesc}
95+
7796

7897
Lock objects have the following methods:
7998

Doc/lib/libthreading.tex

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,25 @@ \section{\module{threading} ---
125125
\versionadded{2.3}
126126
\end{funcdesc}
127127

128+
\begin{funcdesc}{stack_size}{\optional{size}}
129+
Return the thread stack size used when creating new threads. The
130+
optional \var{size} argument specifies the stack size to be used for
131+
subsequently created threads, and must be 0 (use platform or
132+
configured default) or a positive integer value of at least 32,768 (32kB).
133+
If changing the thread stack size is unsupported, or the specified size
134+
is invalid, a RuntimeWarning is issued and the stack size is unmodified.
135+
32kB is currently the minimum supported stack size value, to guarantee
136+
sufficient stack space for the interpreter itself.
137+
Note that some platforms may have particular restrictions on values for
138+
the stack size, such as requiring allocation in multiples of the system
139+
memory page size - platform documentation should be referred to for more
140+
information (4kB pages are common; using multiples of 4096 for the
141+
stack size is the suggested approach in the absence of more specific
142+
information).
143+
Availability: Windows, systems with \POSIX{} threads.
144+
\versionadded{2.5}
145+
\end{funcdesc}
146+
128147
Detailed interfaces for the objects are documented below.
129148

130149
The design of this module is loosely based on Java's threading model.

Include/pythread.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ PyAPI_FUNC(int) PyThread_acquire_lock(PyThread_type_lock, int);
2525
#define NOWAIT_LOCK 0
2626
PyAPI_FUNC(void) PyThread_release_lock(PyThread_type_lock);
2727

28+
PyAPI_FUNC(size_t) PyThread_get_stacksize(void);
29+
PyAPI_FUNC(int) PyThread_set_stacksize(size_t);
30+
2831
#ifndef NO_EXIT_PROG
2932
PyAPI_FUNC(void) PyThread_exit_prog(int);
3033
PyAPI_FUNC(void) PyThread__PyThread_exit_prog(int);

Lib/dummy_thread.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
'interrupt_main', 'LockType']
2121

2222
import traceback as _traceback
23+
import warnings
2324

2425
class error(Exception):
2526
"""Dummy implementation of thread.error."""
@@ -75,6 +76,13 @@ def allocate_lock():
7576
"""Dummy implementation of thread.allocate_lock()."""
7677
return LockType()
7778

79+
def stack_size(size=None):
80+
"""Dummy implementation of thread.stack_size()."""
81+
if size is not None:
82+
msg = "setting thread stack size not supported on this platform"
83+
warnings.warn(msg, RuntimeWarning)
84+
return 0
85+
7886
class LockType(object):
7987
"""Class implementing dummy implementation of thread.LockType.
8088

Lib/test/output/test_thread

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,11 @@ all tasks done
44

55
*** Barrier Test ***
66
all tasks done
7+
8+
*** Changing thread stack size ***
9+
trying stack_size = 32768
10+
waiting for all tasks to complete
11+
all tasks done
12+
trying stack_size = 4194304
13+
waiting for all tasks to complete
14+
all tasks done

Lib/test/test_thread.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,38 @@ def task2(ident):
115115
thread.start_new_thread(task2, (i,))
116116
done.acquire()
117117
print 'all tasks done'
118+
119+
# not all platforms support changing thread stack size
120+
print '\n*** Changing thread stack size ***'
121+
if thread.stack_size() != 0:
122+
raise ValueError, "initial stack_size not 0"
123+
124+
thread.stack_size(0)
125+
if thread.stack_size() != 0:
126+
raise ValueError, "stack_size not reset to default"
127+
128+
from os import name as os_name
129+
if os_name in ("nt", "os2", "posix"):
130+
131+
for tss, ok in ((4096, 0), (32768, 1), (0x400000, 1), (0, 1)):
132+
if ok:
133+
failed = lambda s, e: s != e
134+
fail_msg = "stack_size(%d) failed - should succeed"
135+
else:
136+
failed = lambda s, e: s == e
137+
fail_msg = "stack_size(%d) succeeded - should fail"
138+
thread.stack_size(tss)
139+
if failed(thread.stack_size(), tss):
140+
raise ValueError, fail_msg % tss
141+
142+
for tss in (32768, 0x400000):
143+
print 'trying stack_size = %d' % tss
144+
next_ident = 0
145+
for i in range(numtasks):
146+
newtask()
147+
148+
print 'waiting for all tasks to complete'
149+
done.acquire()
150+
print 'all tasks done'
151+
152+
thread.stack_size(0)

Lib/test/test_threading.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,22 @@ def test_various_ops(self):
8585
print 'all tasks done'
8686
self.assertEqual(numrunning.get(), 0)
8787

88+
# run with a minimum thread stack size (32kB)
89+
def test_various_ops_small_stack(self):
90+
if verbose:
91+
print 'with 32kB thread stack size...'
92+
threading.stack_size(0x8000)
93+
self.test_various_ops()
94+
threading.stack_size(0)
95+
96+
# run with a large thread stack size (16MB)
97+
def test_various_ops_large_stack(self):
98+
if verbose:
99+
print 'with 16MB thread stack size...'
100+
threading.stack_size(0x1000000)
101+
self.test_various_ops()
102+
threading.stack_size(0)
103+
88104
def test_foreign_thread(self):
89105
# Check that a "foreign" thread can use the threading module.
90106
def f(mutex):

Lib/threading.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# Rename some stuff so "from threading import *" is safe
1616
__all__ = ['activeCount', 'Condition', 'currentThread', 'enumerate', 'Event',
1717
'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread',
18-
'Timer', 'setprofile', 'settrace', 'local']
18+
'Timer', 'setprofile', 'settrace', 'local', 'stack_size']
1919

2020
_start_new_thread = thread.start_new_thread
2121
_allocate_lock = thread.allocate_lock
@@ -713,6 +713,8 @@ def enumerate():
713713
_active_limbo_lock.release()
714714
return active
715715

716+
from thread import stack_size
717+
716718
# Create the main thread object
717719

718720
_MainThread()

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ Extension Modules
8484
- Patch #1435422: zlib's compress and decompress objects now have a
8585
copy() method.
8686

87+
- Patch #1454481: thread stack size is now tunable at runtime for thread
88+
enabled builds on Windows and systems with Posix threads support.
89+
8790
- On Win32, os.listdir now supports arbitrarily-long Unicode path names
8891
(up to the system limit of 32K characters).
8992

Modules/threadmodule.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,51 @@ allocated consecutive numbers starting at 1, this behavior should not\n\
586586
be relied upon, and the number should be seen purely as a magic cookie.\n\
587587
A thread's identity may be reused for another thread after it exits.");
588588

589+
static PyObject *
590+
thread_stack_size(PyObject *self, PyObject *args)
591+
{
592+
size_t old_size, new_size;
593+
PyObject *set_size = NULL;
594+
595+
if (!PyArg_UnpackTuple(args, "stack_size", 0, 1, &set_size))
596+
return NULL;
597+
598+
old_size = PyThread_get_stacksize();
599+
600+
if (set_size != NULL) {
601+
if (PyInt_Check(set_size))
602+
new_size = (size_t) PyInt_AsLong(set_size);
603+
else {
604+
PyErr_SetString(PyExc_TypeError,
605+
"size must be an integer");
606+
return NULL;
607+
}
608+
if (PyThread_set_stacksize(new_size))
609+
return NULL;
610+
}
611+
612+
return PyInt_FromLong((long) old_size);
613+
}
614+
615+
PyDoc_STRVAR(stack_size_doc,
616+
"stack_size([size]) -> size\n\
617+
\n\
618+
Return the thread stack size used when creating new threads. The\n\
619+
optional size argument specifies the stack size (in bytes) to be used\n\
620+
for subsequently created threads, and must be 0 (use platform or\n\
621+
configured default) or a positive integer value of at least 32,768 (32kB).\n\
622+
If changing the thread stack size is unsupported, or the specified size\n\
623+
is invalid, a RuntimeWarning is issued and the stack size is unmodified.\n\
624+
32kB is currently the minimum supported stack size value, to guarantee\n\
625+
sufficient stack space for the interpreter itself.\n\
626+
\n\
627+
Note that some platforms may have particular restrictions on values for\n\
628+
the stack size, such as requiring allocation in multiples of the system\n\
629+
memory page size - platform documentation should be referred to for more\n\
630+
information (4kB pages are common; using multiples of 4096 for the\n\
631+
stack size is the suggested approach in the absence of more specific\n\
632+
information).");
633+
589634
static PyMethodDef thread_methods[] = {
590635
{"start_new_thread", (PyCFunction)thread_PyThread_start_new_thread,
591636
METH_VARARGS,
@@ -605,6 +650,9 @@ static PyMethodDef thread_methods[] = {
605650
METH_NOARGS, interrupt_doc},
606651
{"get_ident", (PyCFunction)thread_get_ident,
607652
METH_NOARGS, get_ident_doc},
653+
{"stack_size", (PyCFunction)thread_stack_size,
654+
METH_VARARGS,
655+
stack_size_doc},
608656
#ifndef NO_EXIT_PROG
609657
{"exit_prog", (PyCFunction)thread_PyThread_exit_prog,
610658
METH_VARARGS},

0 commit comments

Comments
 (0)