Skip to content

Commit fc830bf

Browse files
committed
[GR-7165] [GR-13470] Run weakreference callbacks and signals on the main thread
PullRequest: graalpython/373
2 parents 8c98b71 + 07b7f6e commit fc830bf

22 files changed

+683
-272
lines changed
Lines changed: 14 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -37,69 +37,27 @@
3737
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3838
# SOFTWARE.
3939

40-
def skip_test_alarm():
41-
# (tfel): this test is very brittle, because it is supposed to work with our
42-
# first, very primitive implementation of signal handlers, which does not
43-
# allow Python code to run in the handler. So we rely on a side-effect on an
44-
# open file descriptor instead.
40+
def test_alarm2():
4541
try:
4642
import _signal
4743
except ImportError:
4844
import signal as _signal
49-
import posix
5045
import time
51-
import sys
5246

53-
# first, we start opening files until the fd is the same as SIGALRM
54-
fds = []
55-
dupd_fd = None
56-
fd = None
47+
triggered = None
5748

58-
try:
59-
fd = posix.open(__file__, posix.O_RDONLY)
60-
while fd < _signal.SIGALRM:
61-
fds.append(fd)
62-
fd = posix.open(__file__, posix.O_RDONLY)
63-
64-
if fd > _signal.SIGALRM:
65-
dupd_fd = posix.dup(_signal.SIGALRM)
66-
posix.close(_signal.SIGALRM)
67-
fd = posix.open(__file__, posix.O_RDONLY)
68-
69-
# close the unneeded fds
70-
for oldfd in fds:
71-
posix.close(oldfd)
72-
73-
assert fd == _signal.SIGALRM, "fd not equal to SIGALRM"
74-
75-
# temporary: graalpython doesn't check the argcount for the handler atm
76-
if sys.implementation.name == "graalpython":
77-
handler = posix.close
78-
else:
79-
handler = lambda s,f: posix.close(s)
49+
def handler(signal, frame):
50+
nonlocal triggered
51+
triggered = (signal, frame)
8052

81-
oldhandler = _signal.signal(_signal.SIGALRM, handler)
82-
assert oldhandler == _signal.SIG_DFL, "oldhandler != SIG_DFL"
83-
assert _signal.getsignal(_signal.SIGALRM) is handler, "getsignal handler != handler"
53+
oldhandler = _signal.signal(_signal.SIGALRM, handler)
54+
assert oldhandler == _signal.SIG_DFL, "oldhandler != SIG_DFL"
55+
assert _signal.getsignal(_signal.SIGALRM) is handler, "getsignal handler != handler"
8456

85-
# schedule the alarm signal, that will trigger the handler, which
86-
# will in turn close our file
87-
_signal.alarm(1)
57+
_signal.alarm(1)
8858

89-
# wait for the signal to come in and be handled
90-
time.sleep(1.5)
59+
while not triggered:
60+
time.sleep(0.5)
9161

92-
# check for the side-effect
93-
try:
94-
posix.read(fd, 1)
95-
except OSError:
96-
assert True
97-
else:
98-
assert False, "file is still open"
99-
finally:
100-
if dupd_fd is not None:
101-
try:
102-
posix.close(fd)
103-
except OSError:
104-
pass
105-
posix.dup(dupd_fd) # duplicates back into just free'd fd
62+
assert triggered[0] == _signal.SIGALRM
63+
assert triggered[1].f_code.co_name == "test_alarm2", triggered[1].f_code
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
2+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3+
#
4+
# The Universal Permissive License (UPL), Version 1.0
5+
#
6+
# Subject to the condition set forth below, permission is hereby granted to any
7+
# person obtaining a copy of this software, associated documentation and/or
8+
# data (collectively the "Software"), free of charge and under any and all
9+
# copyright rights in the Software, and any and all patent rights owned or
10+
# freely licensable by each licensor hereunder covering either (i) the
11+
# unmodified Software as contributed to or provided by such licensor, or (ii)
12+
# the Larger Works (as defined below), to deal in both
13+
#
14+
# (a) the Software, and
15+
#
16+
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
17+
# one is included with the Software each a "Larger Work" to which the Software
18+
# is contributed by such licensors),
19+
#
20+
# without restriction, including without limitation the rights to copy, create
21+
# derivative works of, display, perform, and distribute the Software and make,
22+
# use, sell, offer for sale, import, export, have made, and have sold the
23+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
24+
# either these or other terms.
25+
#
26+
# This license is subject to the following condition:
27+
#
28+
# The above copyright notice and either this complete permission notice or at a
29+
# minimum a reference to the UPL must be included in all copies or substantial
30+
# portions of the Software.
31+
#
32+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38+
# SOFTWARE.
39+
40+
41+
def test_weakref_finalizer():
42+
import gc, weakref
43+
class A(): pass
44+
for i in range(2):
45+
w = weakref.ref(A(), cleanup)
46+
while not cleaned_up:
47+
gc.collect()
48+
assert not w()
49+
assert cleaned_up
50+
51+
52+
cleaned_up = False
53+
def cleanup(ref):
54+
global cleaned_up
55+
cleaned_up = True

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

Lines changed: 4 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@
124124
import com.oracle.graal.python.builtins.objects.iterator.PZipBuiltins;
125125
import com.oracle.graal.python.builtins.objects.iterator.SentinelIteratorBuiltins;
126126
import com.oracle.graal.python.builtins.objects.list.ListBuiltins;
127-
import com.oracle.graal.python.builtins.objects.list.PList;
128127
import com.oracle.graal.python.builtins.objects.mappingproxy.MappingproxyBuiltins;
129128
import com.oracle.graal.python.builtins.objects.memoryview.BufferBuiltins;
130129
import com.oracle.graal.python.builtins.objects.memoryview.MemoryviewBuiltins;
@@ -398,7 +397,6 @@ public boolean isInitialized() {
398397
public void initialize(PythonContext context) {
399398
singletonContext = context;
400399
initializeJavaCore();
401-
initializeSysModule();
402400
initializePythonCore();
403401
initialized = true;
404402
}
@@ -425,96 +423,16 @@ public void postInitialize() {
425423
if (!TruffleOptions.AOT || ImageInfo.inImageRuntimeCode()) {
426424
initialized = false;
427425

428-
loadFile(__BUILTINS_PATCHES__, PythonCore.getCoreHomeOrFail());
426+
for (PythonBuiltins builtin : builtins) {
427+
builtin.postInitialize(this);
428+
}
429429

430-
PythonModule os = lookupBuiltinModule("posix");
431-
Object environAttr = os.getAttribute("environ");
432-
((PDict) environAttr).setDictStorage(createEnvironDict().getDictStorage());
430+
loadFile(__BUILTINS_PATCHES__, PythonCore.getCoreHomeOrFail());
433431

434432
initialized = true;
435433
}
436434
}
437435

438-
public PythonModule initializeSysModule() {
439-
PythonModule sys = builtinModules.get("sys");
440-
PythonContext context = getContext();
441-
String[] args = context.getEnv().getApplicationArguments();
442-
sys.setAttribute("argv", factory().createList(Arrays.copyOf(args, args.length, Object[].class)));
443-
String prefix = PythonCore.getSysPrefix(context.getEnv());
444-
for (String name : SysModuleBuiltins.SYS_PREFIX_ATTRIBUTES) {
445-
sys.setAttribute(name, prefix);
446-
}
447-
448-
sys.setAttribute("executable", PythonOptions.getOption(context, PythonOptions.Executable));
449-
sys.setAttribute("graal_python_home", context.getLanguage().getHome());
450-
sys.setAttribute("graal_python_core_home", PythonOptions.getOption(context, PythonOptions.CoreHome));
451-
sys.setAttribute("graal_python_stdlib_home", PythonOptions.getOption(context, PythonOptions.StdLibHome));
452-
sys.setAttribute("graal_python_opaque_filesystem", PythonOptions.getOption(context, PythonOptions.OpaqueFilesystem));
453-
sys.setAttribute("graal_python_opaque_filesystem_prefix", PythonOptions.getOption(context, PythonOptions.OpaqueFilesystemPrefixes));
454-
sys.setAttribute("__flags__", factory().createTuple(new Object[]{
455-
false, // bytes_warning
456-
!PythonOptions.getFlag(context, PythonOptions.PythonOptimizeFlag), // debug
457-
true, // dont_write_bytecode
458-
false, // hash_randomization
459-
PythonOptions.getFlag(context, PythonOptions.IgnoreEnvironmentFlag), // ignore_environment
460-
PythonOptions.getFlag(context, PythonOptions.InspectFlag), // inspect
461-
PythonOptions.getFlag(context, PythonOptions.TerminalIsInteractive), // interactive
462-
!context.isExecutableAccessAllowed(), // isolated
463-
PythonOptions.getFlag(context, PythonOptions.NoSiteFlag), // no_site
464-
PythonOptions.getFlag(context, PythonOptions.NoUserSiteFlag), // no_user_site
465-
PythonOptions.getFlag(context, PythonOptions.PythonOptimizeFlag), // optimize
466-
PythonOptions.getFlag(context, PythonOptions.QuietFlag), // quiet
467-
PythonOptions.getFlag(context, PythonOptions.VerboseFlag), // verbose
468-
}));
469-
470-
initializeSysPath(sys, args);
471-
472-
return sys;
473-
}
474-
475-
private void initializeSysPath(PythonModule sys, String[] args) {
476-
Env env = getContext().getEnv();
477-
String option = PythonOptions.getOption(getContext(), PythonOptions.PythonPath);
478-
Object[] path;
479-
int pathIdx = 0;
480-
if (option.length() > 0) {
481-
String[] split = option.split(PythonCore.PATH_SEPARATOR);
482-
path = new Object[split.length + 3];
483-
System.arraycopy(split, 0, path, 0, split.length);
484-
pathIdx = split.length;
485-
} else {
486-
path = new Object[3];
487-
}
488-
path[pathIdx] = getScriptPath(env, args);
489-
path[pathIdx + 1] = PythonCore.getStdlibHome(env);
490-
path[pathIdx + 2] = PythonCore.getCoreHome(env) + PythonCore.FILE_SEPARATOR + "modules";
491-
PList sysPaths = factory().createList(path);
492-
sys.setAttribute("path", sysPaths);
493-
}
494-
495-
private static String getScriptPath(Env env, String[] args) {
496-
String scriptPath;
497-
if (args.length > 0) {
498-
String argv0 = args[0];
499-
if (argv0 != null && !argv0.startsWith("-") && !argv0.isEmpty()) {
500-
TruffleFile scriptFile = env.getTruffleFile(argv0);
501-
try {
502-
scriptPath = scriptFile.getAbsoluteFile().getParent().getPath();
503-
} catch (SecurityException e) {
504-
scriptPath = scriptFile.getParent().getPath();
505-
}
506-
if (scriptPath == null) {
507-
scriptPath = ".";
508-
}
509-
} else {
510-
scriptPath = "";
511-
}
512-
} else {
513-
scriptPath = "";
514-
}
515-
return scriptPath;
516-
}
517-
518436
@TruffleBoundary
519437
public PythonModule lookupBuiltinModule(String name) {
520438
return builtinModules.get(name);
@@ -717,16 +635,6 @@ private void loadFile(String s, String prefix) {
717635
callTarget.call(PArguments.withGlobals(mod));
718636
}
719637

720-
@TruffleBoundary
721-
private PDict createEnvironDict() {
722-
Map<String, String> getenv = System.getenv();
723-
PDict environ = factory.createDict();
724-
for (Entry<String, String> entry : getenv.entrySet()) {
725-
environ.setItem(factory.createBytes(entry.getKey().getBytes()), factory.createBytes(entry.getValue().getBytes()));
726-
}
727-
return environ;
728-
}
729-
730638
public PythonObjectFactory factory() {
731639
return factory;
732640
}

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2018, Oracle and/or its affiliates.
2+
* Copyright (c) 2017, 2019, Oracle and/or its affiliates.
33
* Copyright (c) 2013, Regents of the University of California
44
*
55
* All rights reserved.
@@ -54,6 +54,10 @@ public abstract class PythonBuiltins {
5454

5555
protected abstract List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFactories();
5656

57+
/**
58+
* Initialize everything that is truly independent of commandline arguments and that can be
59+
* initialized and frozen into an SVM image.
60+
*/
5761
public void initialize(PythonCore core) {
5862
if (builtinFunctions.size() > 0) {
5963
return;
@@ -101,6 +105,14 @@ public void initialize(PythonCore core) {
101105
});
102106
}
103107

108+
/**
109+
* Run any actions that can only be run in the post-initialization step, that is, if we're
110+
* actually going to start running rather than just pre-initializing.
111+
*/
112+
public void postInitialize(@SuppressWarnings("unused") PythonCore core) {
113+
// nothing to do by default
114+
}
115+
104116
private void initializeEachFactoryWith(BiConsumer<NodeFactory<? extends PythonBuiltinBaseNode>, Builtin> func) {
105117
List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> factories = getNodeFactories();
106118
assert factories != null : "No factories found. Override getFactories() to resolve this.";
@@ -150,5 +162,4 @@ protected Map<PythonBuiltinClass, Entry<PythonBuiltinClassType[], Boolean>> getB
150162
protected Map<String, Object> getBuiltinConstants() {
151163
return builtinConstants;
152164
}
153-
154165
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import static com.oracle.graal.python.nodes.BuiltinNames.SUM;
5858
import static com.oracle.graal.python.nodes.BuiltinNames.__BREAKPOINT__;
5959
import static com.oracle.graal.python.nodes.BuiltinNames.__BUILTIN__;
60+
import static com.oracle.graal.python.nodes.BuiltinNames.__DEBUG__;
6061
import static com.oracle.graal.python.nodes.BuiltinNames.__DUMP_TRUFFLE_AST__;
6162
import static com.oracle.graal.python.nodes.HiddenAttributes.ID_KEY;
6263
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__NAME__;
@@ -157,6 +158,8 @@
157158
import com.oracle.graal.python.nodes.util.CastToIntegerFromIndexNode;
158159
import com.oracle.graal.python.nodes.util.CastToStringNode;
159160
import com.oracle.graal.python.runtime.PythonContext;
161+
import com.oracle.graal.python.runtime.PythonCore;
162+
import com.oracle.graal.python.runtime.PythonOptions;
160163
import com.oracle.graal.python.runtime.PythonParser.ParserMode;
161164
import com.oracle.graal.python.runtime.exception.PException;
162165
import com.oracle.graal.python.runtime.exception.PythonErrorType;
@@ -194,6 +197,13 @@ protected List<com.oracle.truffle.api.dsl.NodeFactory<? extends PythonBuiltinBas
194197
return BuiltinFunctionsFactory.getFactories();
195198
}
196199

200+
@Override
201+
public void postInitialize(PythonCore core) {
202+
super.postInitialize(core);
203+
PythonModule builtinsModule = core.lookupBuiltinModule("builtins");
204+
builtinsModule.setAttribute(__DEBUG__, !PythonOptions.getOption(core.getContext(), PythonOptions.PythonOptimizeFlag));
205+
}
206+
197207
// abs(x)
198208
@Builtin(name = ABS, fixedNumOfPositionalArgs = 1)
199209
@GenerateNodeFactory

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2018, Oracle and/or its affiliates.
2+
* Copyright (c) 2017, 2019, Oracle and/or its affiliates.
33
* Copyright (c) 2013, Regents of the University of California
44
*
55
* All rights reserved.
@@ -53,11 +53,17 @@ protected List<com.oracle.truffle.api.dsl.NodeFactory<? extends PythonBuiltinNod
5353
@GenerateNodeFactory
5454
abstract static class GcCollectNode extends PythonBuiltinNode {
5555
@Specialization
56-
@TruffleBoundary
5756
int collect() {
58-
System.gc();
57+
doGc();
58+
// collect some weak references now
59+
getContext().triggerAsyncActions();
5960
return 0;
6061
}
62+
63+
@TruffleBoundary
64+
private static void doGc() {
65+
System.gc();
66+
}
6167
}
6268

6369
@Builtin(name = "get_count", fixedNumOfPositionalArgs = 0)

0 commit comments

Comments
 (0)