Skip to content

Commit 25764b9

Browse files
committed
add _thread.stack_size
1 parent 8aabab7 commit 25764b9

File tree

7 files changed

+109
-120
lines changed

7 files changed

+109
-120
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_thread.py

Lines changed: 30 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3737
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3838
# SOFTWARE.
39-
import os
4039
import random
4140
import unittest
4241
from test import support
@@ -53,6 +52,7 @@
5352

5453
_print_mutex = thread.allocate_lock()
5554

55+
5656
def verbose_print(arg):
5757
"""Helper function for printing out debugging output."""
5858
if support.verbose:
@@ -111,42 +111,13 @@ def test_stack_size(self):
111111
thread.stack_size(0)
112112
self.assertEqual(thread.stack_size(), 0, "stack_size not reset to default")
113113

114-
@unittest.skipIf(os.name not in ("nt", "posix"), 'test meant for nt and posix')
115-
def test_nt_and_posix_stack_size(self):
116-
try:
117-
thread.stack_size(4096)
118-
except ValueError:
119-
verbose_print("caught expected ValueError setting "
120-
"stack_size(4096)")
121-
except thread.error:
122-
self.skipTest("platform does not support changing thread stack "
123-
"size")
124-
125-
fail_msg = "stack_size(%d) failed - should succeed"
126-
for tss in (262144, 0x100000, 0):
127-
thread.stack_size(tss)
128-
self.assertEqual(thread.stack_size(), tss, fail_msg % tss)
129-
verbose_print("successfully set stack_size(%d)" % tss)
130-
131-
for tss in (262144, 0x100000):
132-
verbose_print("trying stack_size = (%d)" % tss)
133-
self.next_ident = 0
134-
self.created = 0
135-
for i in range(NUMTASKS):
136-
self.newtask()
137-
138-
verbose_print("waiting for all tasks to complete")
139-
self.done_mutex.acquire()
140-
verbose_print("all tasks done")
141-
142-
thread.stack_size(0)
143-
144114
def test__count(self):
145115
# Test the _count() function.
146116
orig = thread._count()
147117
mut = thread.allocate_lock()
148118
mut.acquire()
149119
started = []
120+
150121
def task():
151122
started.append(None)
152123
mut.acquire()
@@ -167,35 +138,36 @@ def task():
167138
time.sleep(POLL_SLEEP)
168139
self.assertEqual(thread._count(), orig)
169140

170-
def test_save_exception_state_on_error(self):
171-
# See issue #14474
172-
def task():
173-
started.release()
174-
raise SyntaxError
175-
def mywrite(self, *args):
176-
try:
177-
raise ValueError
178-
except ValueError:
179-
pass
180-
real_write(self, *args)
181-
c = thread._count()
182-
started = thread.allocate_lock()
183-
with support.captured_output("stderr") as stderr:
184-
real_write = stderr.write
185-
stderr.write = mywrite
186-
started.acquire()
187-
thread.start_new_thread(task, ())
188-
started.acquire()
189-
while thread._count() > c:
190-
time.sleep(POLL_SLEEP)
191-
self.assertIn("Traceback", stderr.getvalue())
192-
193-
194-
class Barrier:
141+
# def test_save_exception_state_on_error(self):
142+
# # See issue #14474
143+
# def task():
144+
# started.release()
145+
# raise SyntaxError
146+
#
147+
# def mywrite(self, *args):
148+
# try:
149+
# raise ValueError
150+
# except ValueError:
151+
# pass
152+
# real_write(self, *args)
153+
# c = thread._count()
154+
# started = thread.allocate_lock()
155+
# with support.captured_output("stderr") as stderr:
156+
# real_write = stderr.write
157+
# stderr.write = mywrite
158+
# started.acquire()
159+
# thread.start_new_thread(task, ())
160+
# started.acquire()
161+
# while thread._count() > c:
162+
# time.sleep(POLL_SLEEP)
163+
# self.assertIn("Traceback", stderr.getvalue())
164+
165+
166+
class Barrier(object):
195167
def __init__(self, num_threads):
196168
self.num_threads = num_threads
197169
self.waiting = 0
198-
self.checkin_mutex = thread.allocate_lock()
170+
self.checkin_mutex = thread.allocate_lock()
199171
self.checkout_mutex = thread.allocate_lock()
200172
self.checkout_mutex.acquire()
201173

@@ -252,56 +224,6 @@ def task2(self, ident):
252224
if finished:
253225
self.done_mutex.release()
254226

227+
255228
class LockTests(lock_tests.LockTests):
256229
locktype = thread.allocate_lock
257-
258-
259-
class TestForkInThread(unittest.TestCase):
260-
def setUp(self):
261-
self.read_fd, self.write_fd = os.pipe()
262-
263-
@unittest.skipUnless(hasattr(os, 'fork'), 'need os.fork')
264-
@support.reap_threads
265-
def test_forkinthread(self):
266-
running = True
267-
status = "not set"
268-
269-
def thread1():
270-
nonlocal running, status
271-
272-
# fork in a thread
273-
pid = os.fork()
274-
if pid == 0:
275-
# child
276-
try:
277-
os.close(self.read_fd)
278-
os.write(self.write_fd, b"OK")
279-
finally:
280-
os._exit(0)
281-
else:
282-
# parent
283-
os.close(self.write_fd)
284-
pid, status = os.waitpid(pid, 0)
285-
running = False
286-
287-
thread.start_new_thread(thread1, ())
288-
self.assertEqual(os.read(self.read_fd, 2), b"OK",
289-
"Unable to fork() in thread")
290-
while running:
291-
time.sleep(POLL_SLEEP)
292-
self.assertEqual(status, 0)
293-
294-
def tearDown(self):
295-
try:
296-
os.close(self.read_fd)
297-
except OSError:
298-
pass
299-
300-
try:
301-
os.close(self.write_fd)
302-
except OSError:
303-
pass
304-
305-
306-
if __name__ == "__main__":
307-
unittest.main()

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ThreadModuleBuiltins.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,15 @@
4040
*/
4141
package com.oracle.graal.python.builtins.modules;
4242

43+
import static com.oracle.graal.python.runtime.exception.PythonErrorType.ValueError;
44+
4345
import java.util.List;
4446

4547
import com.oracle.graal.python.builtins.Builtin;
4648
import com.oracle.graal.python.builtins.CoreFunctions;
4749
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
4850
import com.oracle.graal.python.builtins.PythonBuiltins;
51+
import com.oracle.graal.python.builtins.objects.PNone;
4952
import com.oracle.graal.python.builtins.objects.thread.PLock;
5053
import com.oracle.graal.python.builtins.objects.thread.PThread;
5154
import com.oracle.graal.python.builtins.objects.type.PythonClass;
@@ -59,6 +62,7 @@
5962
import com.oracle.truffle.api.dsl.NodeFactory;
6063
import com.oracle.truffle.api.dsl.Specialization;
6164
import com.oracle.truffle.api.frame.VirtualFrame;
65+
import com.oracle.truffle.api.profiles.ConditionProfile;
6266

6367
@CoreFunctions(defineModule = "_thread")
6468
public class ThreadModuleBuiltins extends PythonBuiltins {
@@ -86,6 +90,33 @@ long getId() {
8690
}
8791
}
8892

93+
@Builtin(name = "stack_size", minNumOfPositionalArgs = 0, maxNumOfPositionalArgs = 1)
94+
@GenerateNodeFactory
95+
abstract static class GetThreadStackSizeNode extends PythonUnaryBuiltinNode {
96+
@Specialization
97+
long getStackSize(@SuppressWarnings("unused") PNone stackSize) {
98+
return getContext().getThreadStackSize();
99+
}
100+
101+
@Specialization
102+
long getStackSize(int stackSize,
103+
@Cached("createBinaryProfile()") ConditionProfile invalidSizeProfile) {
104+
if (invalidSizeProfile.profile(stackSize < 0)) {
105+
throw raise(ValueError, "size must be 0 or a positive value");
106+
}
107+
return getContext().getAndSetThreadStackSize(stackSize);
108+
}
109+
110+
@Specialization
111+
long getStackSize(long stackSize,
112+
@Cached("createBinaryProfile()") ConditionProfile invalidSizeProfile) {
113+
if (invalidSizeProfile.profile(stackSize < 0)) {
114+
throw raise(ValueError, "size must be 0 or a positive value");
115+
}
116+
return getContext().getAndSetThreadStackSize(stackSize);
117+
}
118+
}
119+
89120
@Builtin(name = "start_new_thread", minNumOfPositionalArgs = 3, maxNumOfPositionalArgs = 4, constructsClass = PythonBuiltinClassType.PThread)
90121
@GenerateNodeFactory
91122
abstract static class StartNewThreadNode extends PythonBuiltinNode {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/PThread.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,16 @@
4444
import com.oracle.graal.python.builtins.objects.type.PythonClass;
4545

4646
public class PThread extends PythonBuiltinObject {
47+
public final static String GRAALPYTHON_THREADS = "GRAALPYTHON_THREADS";
4748
private final Thread thread;
4849

49-
public PThread(PythonClass cls, Runnable runnable) {
50+
public PThread(PythonClass cls, ThreadGroup group, Runnable runnable) {
51+
this(cls, group, 0, runnable);
52+
}
53+
54+
public PThread(PythonClass cls, ThreadGroup group, long stackSize, Runnable runnable) {
5055
super(cls);
51-
this.thread = new Thread(runnable);
56+
this.thread = new Thread(group, runnable, "graalpython-thread-" + group.activeCount(), stackSize);
5257
}
5358

5459
public void start() {
@@ -60,4 +65,8 @@ public void start() {
6065
public long getId() {
6166
return thread.getId();
6267
}
68+
69+
public String getName() {
70+
return thread.getName();
71+
}
6372
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/thread/CreateThreadNode.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import com.oracle.graal.python.nodes.argument.positional.ExecutePositionalStarargsNode;
5151
import com.oracle.graal.python.nodes.call.CallNode;
5252
import com.oracle.graal.python.nodes.truffle.PythonTypes;
53+
import com.oracle.graal.python.runtime.PythonContext;
5354
import com.oracle.truffle.api.TruffleContext;
5455
import com.oracle.truffle.api.dsl.ImportStatic;
5556
import com.oracle.truffle.api.dsl.TypeSystemReference;
@@ -62,13 +63,11 @@ public class CreateThreadNode extends PNodeWithContext {
6263
@Child private ExecutePositionalStarargsNode getArgsNode = ExecutePositionalStarargsNode.create();
6364
@Child private ExecuteKeywordStarargsNode getKwArgsNode = ExecuteKeywordStarargsNode.create();
6465

65-
private TruffleContext getTruffleContext() {
66-
return getContext().getEnv().getContext();
67-
}
68-
6966
public PThread execute(VirtualFrame frame, PythonClass cls, Object callable, Object args, Object kwargs) {
70-
TruffleContext truffleContext = getTruffleContext();
71-
return factory().createThread(cls, () -> {
67+
PythonContext context = getContext();
68+
TruffleContext truffleContext = context.getEnv().getContext();
69+
70+
return factory().createThread(cls, context.getThreadGroup(), context.getThreadStackSize(), () -> {
7271
Object previous = truffleContext.enter();
7372
Object[] arguments = getArgsNode.executeWith(args);
7473
PKeyword[] keywords = getKwArgsNode.executeWith(kwargs);

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
*/
2626
package com.oracle.graal.python.runtime;
2727

28+
import static com.oracle.graal.python.builtins.objects.thread.PThread.GRAALPYTHON_THREADS;
2829
import static com.oracle.graal.python.nodes.BuiltinNames.__BUILTINS__;
2930
import static com.oracle.graal.python.nodes.BuiltinNames.__MAIN__;
3031
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__FILE__;
@@ -62,6 +63,9 @@ public final class PythonContext {
6263
private final PythonCore core;
6364
private final HashMap<Object, CallTarget> atExitHooks = new HashMap<>();
6465
private final AtomicLong globalId = new AtomicLong(Integer.MAX_VALUE * 2L + 4L);
66+
private final ThreadGroup threadGroup = new ThreadGroup(GRAALPYTHON_THREADS);
67+
private final AtomicLong threadStackSize = new AtomicLong(0); // the VM will set it to whatever
68+
// it likes
6569

6670
@CompilationFinal private TruffleLanguage.Env env;
6771

@@ -99,6 +103,19 @@ public PythonContext(PythonLanguage language, TruffleLanguage.Env env, PythonCor
99103
}
100104
}
101105

106+
public ThreadGroup getThreadGroup() {
107+
return threadGroup;
108+
}
109+
110+
@TruffleBoundary(allowInlining = true)
111+
public long getThreadStackSize() {
112+
return threadStackSize.get();
113+
}
114+
115+
public long getAndSetThreadStackSize(long value) {
116+
return threadStackSize.getAndSet(value);
117+
}
118+
102119
@TruffleBoundary(allowInlining = true)
103120
public long getNextGlobalId() {
104121
return globalId.incrementAndGet();

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PythonObjectFactory.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -759,11 +759,11 @@ public PLock createLock(PythonClass cls) {
759759
return trace(new PLock(cls));
760760
}
761761

762-
public PThread createThread(Runnable runnable) {
763-
return trace(new PThread(PythonBuiltinClassType.PThread, runnable));
762+
public PThread createThread(ThreadGroup group, long stackSize, Runnable runnable) {
763+
return trace(new PThread(PythonBuiltinClassType.PThread, group, stackSize, runnable));
764764
}
765765

766-
public PThread createThread(PythonClass cls, Runnable runnable) {
767-
return trace(new PThread(cls, runnable));
766+
public PThread createThread(PythonClass cls, ThreadGroup group, long stackSize, Runnable runnable) {
767+
return trace(new PThread(cls, group, stackSize, runnable));
768768
}
769769
}

graalpython/lib-graalpython/_thread.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,14 @@
3737
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3838
# SOFTWARE.
3939

40+
error = RuntimeError
41+
TIMEOUT_MAX = 2**31
42+
43+
_lock = allocate_lock()
44+
LockType = type(_lock)
45+
del _lock
46+
47+
48+
def _set_sentinel():
49+
"""Dummy implementation of _thread._set_sentinel()."""
50+
return LockType()

0 commit comments

Comments
 (0)