Skip to content

Commit cae90ef

Browse files
committed
add a per context shutdown hook mechanism
- needed for removing temporary files that are managed withing the python context - add os.ftruncate - add os.dup2 - add faulthandler.disable() - add faulthandler _enabled flag
1 parent 9bccf90 commit cae90ef

File tree

10 files changed

+265
-22
lines changed

10 files changed

+265
-22
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def test_create():
5353
f = TemporaryFile()
5454
assert f is not None
5555
f.close()
56-
except:
56+
except Exception as e:
57+
print(e)
5758
assert False
5859

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ public NodeFactory getNodeFactory() {
153153
@Override
154154
protected void finalizeContext(PythonContext context) {
155155
context.runShutdownHooks();
156+
context.getResources().release();
156157
super.finalizeContext(context);
157158
}
158159

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ private static final String[] initializeCoreFiles() {
214214
"_weakref",
215215
"set",
216216
"itertools",
217+
"faulthandler",
217218
"base_exception",
218219
PythonCextBuiltins.PYTHON_CEXT,
219220
"_collections",

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

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@
142142
import com.oracle.graal.python.runtime.exception.PythonExitException;
143143
import com.oracle.graal.python.runtime.sequence.PSequence;
144144
import com.oracle.graal.python.runtime.sequence.storage.ByteSequenceStorage;
145+
import com.oracle.graal.python.util.FileDeleteShutdownHook;
145146
import com.oracle.truffle.api.CompilerDirectives;
146147
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
147148
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -903,6 +904,30 @@ int dupOvf(PInt fd) {
903904
}
904905
}
905906

907+
@Builtin(name = "dup2", minNumOfPositionalArgs = 2)
908+
@GenerateNodeFactory
909+
@TypeSystemReference(PythonArithmeticTypes.class)
910+
abstract static class Dup2Node extends PythonFileNode {
911+
@Specialization
912+
int dup(int fd, int fd2) {
913+
return getResources().dup2(fd, fd2);
914+
}
915+
916+
@Specialization(rewriteOn = ArithmeticException.class)
917+
int dupPInt(PInt fd, PInt fd2) {
918+
return getResources().dup2(fd.intValueExact(), fd2.intValueExact());
919+
}
920+
921+
@Specialization(replaces = "dupPInt")
922+
int dupOvf(PInt fd, PInt fd2) {
923+
try {
924+
return dupPInt(fd, fd2);
925+
} catch (ArithmeticException e) {
926+
throw raise(OSError, "invalid fd %r", fd);
927+
}
928+
}
929+
}
930+
906931
@Builtin(name = "open", minNumOfPositionalArgs = 2, parameterNames = {"pathname", "flags", "mode", "dir_fd"})
907932
@GenerateNodeFactory
908933
@TypeSystemReference(PythonArithmeticTypes.class)
@@ -929,7 +954,8 @@ Object openMode(VirtualFrame frame, Object pathArg, int flags, int fileMode, @Su
929954
if (options.contains(StandardOpenOption.DELETE_ON_CLOSE)) {
930955
// TODO: (cbas) remove patch once the TruffleFile API supports temp file
931956
// creation -> GR-17515
932-
pathname = getRandomFilePath(pathname, options);
957+
pathname = getRandomFilePath(pathname, options, getContext().getEnv());
958+
getContext().registerShutdownHook(new FileDeleteShutdownHook(pathname));
933959
}
934960
truffleFile = getContext().getPublicTruffleFileRelaxed(pathname, PythonLanguage.DEFAULT_PYTHON_EXTENSIONS);
935961
fc = truffleFile.newByteChannel(options, attributes);
@@ -1060,12 +1086,6 @@ Object close(Object fdObject,
10601086
throw raise(OSError, "invalid fd");
10611087
} else {
10621088
resources.close(fd);
1063-
try {
1064-
closeChannel(channel);
1065-
} catch (IOException e) {
1066-
gotException.enter();
1067-
throw raise(OSError, e);
1068-
}
10691089
}
10701090
return PNone.NONE;
10711091
}
@@ -1981,4 +2001,18 @@ PNone fsync(VirtualFrame frame, int fd) {
19812001
return PNone.NONE;
19822002
}
19832003
}
2004+
2005+
@Builtin(name = "ftruncate", minNumOfPositionalArgs = 2)
2006+
@GenerateNodeFactory
2007+
abstract static class FTruncateNode extends PythonBinaryBuiltinNode {
2008+
@Specialization
2009+
PNone ftruncate(VirtualFrame frame, int fd, long length) {
2010+
try {
2011+
getContext().getResources().ftruncate(fd, length);
2012+
} catch (IOException e) {
2013+
throw raiseOSError(frame, OSErrorEnum.ENOENT.getNumber());
2014+
}
2015+
return PNone.NONE;
2016+
}
2017+
}
19842018
}

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,15 @@
4646
import java.nio.channels.Channel;
4747
import java.nio.channels.Channels;
4848
import java.nio.channels.Pipe;
49+
import java.nio.channels.SeekableByteChannel;
4950
import java.util.ArrayList;
5051
import java.util.Collections;
5152
import java.util.HashMap;
53+
import java.util.HashSet;
5254
import java.util.List;
5355
import java.util.Locale;
5456
import java.util.Map;
57+
import java.util.Set;
5558

5659
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
5760
import com.oracle.truffle.api.TruffleFile;
@@ -68,6 +71,7 @@
6871
public class PosixResources {
6972
/** Context-local file-descriptor mappings and PID mappings */
7073
private final List<Channel> files;
74+
private final Set<Channel> closedFiles;
7175
private final List<String> filePaths;
7276
private final List<Process> children;
7377
private final Map<String, Integer> inodes;
@@ -137,6 +141,7 @@ public Process destroyForcibly() {
137141

138142
public PosixResources() {
139143
files = Collections.synchronizedList(new ArrayList<>());
144+
closedFiles = Collections.synchronizedSet(new HashSet<>());
140145
filePaths = Collections.synchronizedList(new ArrayList<>());
141146
children = Collections.synchronizedList(new ArrayList<>());
142147
String osProperty = System.getProperty("os.name");
@@ -184,6 +189,7 @@ public String getFilePath(int fd) {
184189
@TruffleBoundary(allowInlining = true)
185190
public void close(int fd) {
186191
if (filePaths.size() > fd) {
192+
closedFiles.add(files.get(fd));
187193
files.set(fd, null);
188194
filePaths.set(fd, null);
189195
}
@@ -210,10 +216,28 @@ public int dup(int fd) {
210216
return dupFd;
211217
}
212218

219+
@TruffleBoundary(allowInlining = true)
220+
public int dup2(int fd, int fd2) {
221+
int dupFd = nextFreeFd();
222+
files.set(dupFd, getFileChannel(fd));
223+
filePaths.set(dupFd, getFilePath(fd));
224+
return dupFd;
225+
}
226+
213227
public void fsync(int fd) throws ArrayIndexOutOfBoundsException {
214228
files.get(fd); // for the side-effect
215229
}
216230

231+
@TruffleBoundary(allowInlining = true)
232+
public void ftruncate(int fd, long size) throws IOException {
233+
if (filePaths.size() > fd) {
234+
Channel channel = files.get(fd);
235+
if (channel instanceof SeekableByteChannel) {
236+
((SeekableByteChannel) channel).truncate(size);
237+
}
238+
}
239+
}
240+
217241
@TruffleBoundary(allowInlining = true)
218242
public int[] pipe() throws IOException {
219243
Pipe pipe = Pipe.open();
@@ -316,4 +340,15 @@ public int getInodeId(String canonical) {
316340
return inodeId;
317341
}
318342
}
343+
344+
@TruffleBoundary
345+
public void release() {
346+
// close all channels that have not been closed
347+
for (Channel channel: closedFiles) {
348+
try {
349+
channel.close();
350+
} catch (IOException ignored) {}
351+
}
352+
closedFiles.clear();
353+
}
319354
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@
3636
import java.io.OutputStream;
3737
import java.nio.file.LinkOption;
3838
import java.text.MessageFormat;
39+
import java.util.ArrayList;
3940
import java.util.HashMap;
41+
import java.util.List;
4042
import java.util.concurrent.atomic.AtomicLong;
4143
import java.util.concurrent.locks.ReentrantLock;
4244
import java.util.function.Supplier;
@@ -56,6 +58,7 @@
5658
import com.oracle.graal.python.nodes.SpecialAttributeNames;
5759
import com.oracle.graal.python.runtime.AsyncHandler.AsyncAction;
5860
import com.oracle.graal.python.runtime.exception.PException;
61+
import com.oracle.graal.python.util.ShutdownHook;
5962
import com.oracle.truffle.api.Assumption;
6063
import com.oracle.truffle.api.CallTarget;
6164
import com.oracle.truffle.api.CompilerDirectives;
@@ -86,6 +89,7 @@ public final class PythonContext {
8689
private final PythonLanguage language;
8790
private PythonModule mainModule;
8891
private final PythonCore core;
92+
private final List<ShutdownHook> shutdownHooks = new ArrayList<>();
8993
private final HashMap<Object, CallTarget> atExitHooks = new HashMap<>();
9094
private final HashMap<PythonNativeClass, CyclicAssumption> nativeClassStableAssumptions = new HashMap<>();
9195
private final AtomicLong globalId = new AtomicLong(Integer.MAX_VALUE * 2L + 4L);
@@ -516,6 +520,11 @@ public HashingStorage.Equivalence getSlowPathEquivalence() {
516520
return slowPathEquivalence;
517521
}
518522

523+
@TruffleBoundary
524+
public void registerShutdownHook(ShutdownHook shutdownHook) {
525+
shutdownHooks.add(shutdownHook);
526+
}
527+
519528
@TruffleBoundary
520529
public void registerShutdownHook(Object callable, CallTarget ct) {
521530
atExitHooks.put(callable, ct);
@@ -532,6 +541,9 @@ public void runShutdownHooks() {
532541
for (CallTarget f : atExitHooks.values()) {
533542
f.call();
534543
}
544+
for (ShutdownHook h : shutdownHooks) {
545+
h.call(this);
546+
}
535547
}
536548

537549
@TruffleBoundary
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright (c) 2019, 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.util;
42+
43+
import java.io.IOException;
44+
45+
import com.oracle.graal.python.runtime.PythonContext;
46+
import com.oracle.truffle.api.TruffleFile;
47+
48+
public class FileDeleteShutdownHook implements ShutdownHook {
49+
private final String path;
50+
51+
public FileDeleteShutdownHook(String path) {
52+
this.path = path;
53+
}
54+
55+
@Override
56+
public void call(PythonContext context) {
57+
TruffleFile truffleFile = context.getEnv().getTruffleFile(path);
58+
try {
59+
truffleFile.delete();
60+
} catch (IOException ignored) {}
61+
}
62+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (c) 2019, 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.util;
42+
43+
import com.oracle.graal.python.runtime.PythonContext;
44+
45+
public interface ShutdownHook {
46+
void call(PythonContext context);
47+
}

0 commit comments

Comments
 (0)