Skip to content

Commit 3ca8512

Browse files
authored
Ensure GIL is released after exception is thrown (#27013)
Ensure that the Python GIL is released when an exception is thrown from an initializer. The pattern taken by our throwing initializers for the Python module is that the super class Value.init acquires the GIL, and then the super class Value.postinit releases the GIL (after all init's have fired). However, based on recent discussions (see #26997 and #26990) these calls cannot rely on a postinit being called when an exception is thrown. Prior to this PR, if an exception was thrown in the init, the GIL would never be released. This PR resolves the problem by explicitly handling the exception in the init, releasing the GIL, and then rethrowing the caught exception. - [x] Ran `start_test test/library/packages/Python` [Reviewed by @lydia-duncan]
2 parents f4a3e9b + 9d6323f commit 3ca8512

File tree

5 files changed

+106
-12
lines changed

5 files changed

+106
-12
lines changed

modules/packages/Python.chpl

+67-7
Original file line numberDiff line numberDiff line change
@@ -1834,7 +1834,19 @@ module Python {
18341834
this.isOwned = true;
18351835
this.ctxInitState = chpl_pythonContext.enter();
18361836
init this;
1837-
this.obj = this.interpreter.loadPickle(pickleData);
1837+
// Only catch-less try! statements are allowed in initializers for now :(
1838+
proc helper() throws {
1839+
try {
1840+
this.obj = this.interpreter.loadPickle(pickleData);
1841+
} catch e {
1842+
// an exception thrown from the init will not result in a call to the postinit
1843+
// or the deinit, so we have to handle that stuff here
1844+
defer PyGILState_Release(this.gilInitState);
1845+
// rethrow the exception
1846+
throw e;
1847+
}
1848+
}
1849+
helper();
18381850
}
18391851
/*
18401852
Creates a new Python object from a Chapel value.
@@ -1847,7 +1859,19 @@ module Python {
18471859
this.isOwned = true;
18481860
this.ctxInitState = chpl_pythonContext.enter();
18491861
init this;
1850-
this.obj = this.interpreter.toPythonInner(value);
1862+
// Only catch-less try! statements are allowed in initializers for now :(
1863+
proc helper() throws {
1864+
try {
1865+
this.obj = this.interpreter.toPythonInner(value);
1866+
} catch e {
1867+
// an exception thrown from the init will not result in a call to the postinit
1868+
// or the deinit, so we have to handle that stuff here
1869+
defer PyGILState_Release(this.gilInitState);
1870+
// rethrow the exception
1871+
throw e;
1872+
}
1873+
}
1874+
helper();
18511875
}
18521876

18531877
@chpldoc.nodoc
@@ -2415,7 +2439,19 @@ module Python {
24152439
super.init(interpreter, nil: PyObjectPtr, isOwned=true);
24162440
this.modName = modName;
24172441
init this;
2418-
this.obj = interpreter.compileModule(moduleContents, modName);
2442+
// Only catch-less try! statements are allowed in initializers for now :(
2443+
proc helper() throws {
2444+
try {
2445+
this.obj = interpreter.compileModule(moduleContents, modName);
2446+
} catch e {
2447+
// an exception thrown from the init will not result in a call to the postinit
2448+
// or the deinit, so we have to handle that stuff here
2449+
defer PyGILState_Release(this.gilInitState);
2450+
// rethrow the exception
2451+
throw e;
2452+
}
2453+
}
2454+
helper();
24192455
}
24202456

24212457
/*
@@ -2427,7 +2463,19 @@ module Python {
24272463
super.init(interpreter, nil: PyObjectPtr, isOwned=true);
24282464
this.modName = modName;
24292465
init this;
2430-
this.obj = interpreter.compileModule(moduleBytecode, modName);
2466+
// Only catch-less try! statements are allowed in initializers for now :(
2467+
proc helper() throws {
2468+
try {
2469+
this.obj = interpreter.compileModule(moduleBytecode, modName);
2470+
} catch e {
2471+
// an exception thrown from the init will not result in a call to the postinit
2472+
// or the deinit, so we have to handle that stuff here
2473+
defer PyGILState_Release(this.gilInitState);
2474+
// rethrow the exception
2475+
throw e;
2476+
}
2477+
}
2478+
helper();
24312479
}
24322480
}
24332481

@@ -2482,7 +2530,19 @@ module Python {
24822530
super.init(interpreter, nil: PyObjectPtr, isOwned=true);
24832531
this.fnName = "<anon>";
24842532
init this;
2485-
this.obj = interpreter.compileLambdaInternal(lambdaFn);
2533+
// Only catch-less try! statements are allowed in initializers for now :(
2534+
proc helper() throws {
2535+
try {
2536+
this.obj = interpreter.compileLambdaInternal(lambdaFn);
2537+
} catch e {
2538+
// an exception thrown from the init will not result in a call to the postinit
2539+
// or the deinit, so we have to handle that stuff here
2540+
defer PyGILState_Release(this.gilInitState);
2541+
// rethrow the exception
2542+
throw e;
2543+
}
2544+
}
2545+
helper();
24862546
}
24872547
@chpldoc.nodoc
24882548
proc init(in interpreter: borrowed Interpreter,
@@ -3403,15 +3463,15 @@ module Python {
34033463
:arg interpreter: The interpreter that this object is associated with.
34043464
:arg arr: The Chapel array to create the object from.
34053465
*/
3406-
proc init(in interpreter: borrowed Interpreter, ref arr: []) throws
3466+
proc init(in interpreter: borrowed Interpreter, ref arr: [])
34073467
where isSupportedArrayType(arr) {
34083468
super.init(interpreter, nil: PyObjectPtr, isOwned=true);
34093469
this.eltType = arr.eltType;
34103470
init this;
34113471
this.obj = ArrayTypes.createArray(arr);
34123472
}
34133473
@chpldoc.nodoc
3414-
proc init(in interpreter: borrowed Interpreter, ref arr: []) throws
3474+
proc init(in interpreter: borrowed Interpreter, ref arr: [])
34153475
where !isSupportedArrayType(arr) {
34163476
super.init(interpreter, nil: PyObjectPtr, isOwned=false);
34173477
compilerError("Only 1D local rectangular arrays are currently supported");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//
2+
// Test case of a variety of possible scenarios where an exception could be thrown
3+
// This test makes sure the exception is caught and the program does not hang
4+
// test/library/packages/Python/correctness/unsupportedTypes.chpl tests something similar
5+
//
6+
7+
8+
use Python;
9+
var i = new Interpreter();
10+
11+
try {
12+
var f = new Function(i, "lambda x,:");
13+
} catch e {
14+
writeln("Caught exception: ", e);
15+
}
16+
17+
try {
18+
var fakePklData: bytes = b"fakePklData";
19+
var v = new Value(i, fakePklData);
20+
} catch e {
21+
writeln("Caught exception: ", e);
22+
}
23+
24+
try {
25+
var fakeBytecode: bytes = b"fakeBytecode";
26+
var m = new Module(i, "foo", fakeBytecode);
27+
} catch e {
28+
writeln("Caught exception: ", e);
29+
}
30+
31+
try {
32+
var m = new Module(i, "foo", "I am a syntax error!");
33+
} catch e {
34+
writeln("Caught exception: ", e);
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Caught exception: PythonException: invalid syntax (<string>, line 1)
2+
Caught exception: PythonException: invalid load key, 'f'.
3+
Caught exception: PythonException: marshal data too short
4+
Caught exception: PythonException: invalid syntax (<string>, line 1)

test/library/packages/Python/correctness/unsupportedTypes.future

-4
This file was deleted.

test/library/packages/Python/correctness/unsupportedTypes.timeout

-1
This file was deleted.

0 commit comments

Comments
 (0)