Skip to content

Commit 0d09ad7

Browse files
committed
ShutdownTest
1 parent 2ec2361 commit 0d09ad7

File tree

1 file changed

+169
-0
lines changed
  • graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced

1 file changed

+169
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/*
2+
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.graal.python.test.integration.advanced;
42+
43+
import org.graalvm.polyglot.Context;
44+
import org.graalvm.polyglot.PolyglotException;
45+
import org.junit.Assert;
46+
import org.junit.Test;
47+
48+
import com.oracle.graal.python.test.integration.PythonTests;
49+
50+
import java.util.concurrent.CountDownLatch;
51+
import java.util.concurrent.TimeUnit;
52+
import java.util.concurrent.atomic.AtomicReference;
53+
54+
public class ShutdownTest extends PythonTests {
55+
@Test
56+
public void testCloseWithBackgroundThreadsRunningSucceeds() {
57+
Context context = Context.newBuilder().allowExperimentalOptions(true).allowAllAccess(true).build();
58+
try {
59+
loadNativeExtension(context);
60+
asyncStartPythonThreadsThatSleep(context);
61+
} finally {
62+
context.close(true);
63+
}
64+
}
65+
66+
@Test
67+
public void testCloseFromAnotherThreadThrowsCancelledEx() {
68+
Context context = Context.newBuilder().allowExperimentalOptions(true).allowAllAccess(true).build();
69+
PolyglotException thrownEx = null;
70+
try {
71+
loadNativeExtension(context);
72+
asyncStartPythonThreadsThatSleep(context);
73+
CountDownLatch latch = new CountDownLatch(1);
74+
new Thread(() -> {
75+
try {
76+
latch.await();
77+
Thread.sleep(10);
78+
} catch (InterruptedException e) {
79+
throw new RuntimeException(e);
80+
}
81+
// close from another thread
82+
context.close(true);
83+
}).start();
84+
countDownLatchAndSleepInPython(context, latch);
85+
} catch (PolyglotException ex) {
86+
thrownEx = ex;
87+
} finally {
88+
context.close(true);
89+
Assert.assertNotNull("PolyglotException was not thrown upon cancellation ", thrownEx);
90+
Assert.assertTrue(thrownEx.toString(), thrownEx.isCancelled());
91+
}
92+
}
93+
94+
@Test
95+
public void testJavaThreadGetsCancelledException() throws InterruptedException {
96+
Context context = Context.newBuilder().allowExperimentalOptions(true).allowAllAccess(true).build();
97+
AtomicReference<PolyglotException> thrownEx = new AtomicReference<>();
98+
CountDownLatch gotException = new CountDownLatch(1);
99+
try {
100+
asyncStartPythonThreadsThatSleep(context);
101+
CountDownLatch latch = new CountDownLatch(1);
102+
new Thread(() -> {
103+
try {
104+
// this thread will get stuck sleeping in python
105+
countDownLatchAndSleepInPython(context, latch);
106+
} catch (PolyglotException ex) {
107+
thrownEx.set(ex);
108+
gotException.countDown();
109+
}
110+
}).start();
111+
try {
112+
loadNativeExtension(context);
113+
latch.await();
114+
Thread.sleep(10); // make sure the other thread really starts sleeping
115+
} catch (InterruptedException e) {
116+
throw new RuntimeException(e);
117+
}
118+
} finally {
119+
context.close(true);
120+
}
121+
Assert.assertTrue("other thread did not get any exception in time limit", gotException.await(2, TimeUnit.SECONDS));
122+
Assert.assertNotNull("PolyglotException was not thrown upon cancellation ", thrownEx.get());
123+
Assert.assertTrue(thrownEx.toString(), thrownEx.get().isCancelled());
124+
}
125+
126+
@Test
127+
public void testJavaThreadNotExecutingPythonAnymore() throws InterruptedException {
128+
Context context = Context.newBuilder().allowExperimentalOptions(true).allowAllAccess(true).build();
129+
var javaThreadDone = new CountDownLatch(1);
130+
var javaThreadCanEnd = new CountDownLatch(1);
131+
var javaThread = new Thread(() -> {
132+
Assert.assertEquals(42, context.eval("python", "42").asInt());
133+
javaThreadDone.countDown();
134+
try {
135+
javaThreadCanEnd.await();
136+
} catch (InterruptedException e) {
137+
throw new RuntimeException(e);
138+
}
139+
});
140+
AtomicReference<Throwable> uncaughtEx = new AtomicReference<>();
141+
javaThread.setUncaughtExceptionHandler((t, e) -> uncaughtEx.set(e));
142+
try {
143+
javaThread.start();
144+
loadNativeExtension(context);
145+
javaThreadDone.await();
146+
} finally {
147+
// we can close although the Java thread is still running
148+
context.close(true);
149+
}
150+
javaThreadCanEnd.countDown();
151+
javaThread.join();
152+
Assert.assertNull(uncaughtEx.get());
153+
}
154+
155+
private static void loadNativeExtension(Context context) {
156+
context.eval("python", "import _cpython_sre\nassert _cpython_sre.ascii_tolower(88) == 120\n");
157+
}
158+
159+
private static void asyncStartPythonThreadsThatSleep(Context context) {
160+
for (int i = 0; i < 10; i++) {
161+
context.eval("python", "import threading; import time; threading.Thread(target=lambda: time.sleep(10000)).start()");
162+
}
163+
}
164+
165+
private static void countDownLatchAndSleepInPython(Context context, CountDownLatch latch) {
166+
context.eval("python", "def run(latch): import time; latch.countDown(); time.sleep(100000)");
167+
context.getBindings("python").getMember("run").execute(latch);
168+
}
169+
}

0 commit comments

Comments
 (0)