|
141 | 141 | import java.lang.ref.Reference; |
142 | 142 | import java.nio.ByteOrder; |
143 | 143 | import java.nio.charset.Charset; |
| 144 | +import java.util.ArrayList; |
144 | 145 | import java.util.Arrays; |
145 | 146 | import java.util.Date; |
146 | 147 | import java.util.HashSet; |
147 | 148 | import java.util.List; |
148 | 149 | import java.util.Set; |
| 150 | +import java.util.concurrent.CancellationException; |
| 151 | +import java.util.concurrent.ConcurrentHashMap; |
| 152 | +import java.util.concurrent.ExecutionException; |
| 153 | +import java.util.concurrent.Future; |
| 154 | +import java.util.concurrent.TimeUnit; |
| 155 | +import java.util.concurrent.TimeoutException; |
149 | 156 |
|
150 | 157 | import com.oracle.graal.python.PythonLanguage; |
151 | 158 | import com.oracle.graal.python.annotations.ArgumentClinic; |
|
174 | 181 | import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.HandlePointerConverter; |
175 | 182 | import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode; |
176 | 183 | import com.oracle.graal.python.builtins.objects.common.EconomicMapStorage; |
177 | | -import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageSetItem; |
| 184 | +import com.oracle.graal.python.builtins.objects.common.ObjectHashMap; |
178 | 185 | import com.oracle.graal.python.builtins.objects.dict.PDict; |
179 | 186 | import com.oracle.graal.python.builtins.objects.exception.ExceptionNodes; |
180 | 187 | import com.oracle.graal.python.builtins.objects.exception.GetEscapedExceptionNode; |
|
208 | 215 | import com.oracle.graal.python.lib.PyNumberAsSizeNode; |
209 | 216 | import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; |
210 | 217 | import com.oracle.graal.python.lib.PyObjectGetAttr; |
| 218 | +import com.oracle.graal.python.lib.PyObjectHashNode; |
211 | 219 | import com.oracle.graal.python.lib.PyObjectIsInstanceNode; |
212 | 220 | import com.oracle.graal.python.lib.PyObjectLookupAttr; |
213 | 221 | import com.oracle.graal.python.lib.PyObjectReprAsObjectNode; |
|
225 | 233 | import com.oracle.graal.python.nodes.call.CallNode; |
226 | 234 | import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode; |
227 | 235 | import com.oracle.graal.python.nodes.call.special.SpecialMethodNotFound; |
| 236 | +import com.oracle.graal.python.nodes.frame.MaterializeFrameNode; |
228 | 237 | import com.oracle.graal.python.nodes.frame.ReadFrameNode; |
| 238 | +import com.oracle.graal.python.nodes.frame.ReadFrameNode.AllPythonFramesSelector; |
229 | 239 | import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; |
230 | 240 | import com.oracle.graal.python.nodes.function.PythonBuiltinNode; |
231 | 241 | import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; |
|
238 | 248 | import com.oracle.graal.python.nodes.util.ExceptionStateNodes.GetCaughtExceptionNode; |
239 | 249 | import com.oracle.graal.python.runtime.CallerFlags; |
240 | 250 | import com.oracle.graal.python.runtime.ExecutionContext.BoundaryCallContext; |
| 251 | +import com.oracle.graal.python.runtime.GilNode; |
241 | 252 | import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; |
242 | 253 | import com.oracle.graal.python.runtime.PosixSupportLibrary; |
243 | 254 | import com.oracle.graal.python.runtime.PythonContext; |
|
251 | 262 | import com.oracle.truffle.api.CompilerDirectives; |
252 | 263 | import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; |
253 | 264 | import com.oracle.truffle.api.CompilerDirectives.ValueType; |
| 265 | +import com.oracle.truffle.api.ThreadLocalAction; |
254 | 266 | import com.oracle.truffle.api.Truffle; |
255 | 267 | import com.oracle.truffle.api.TruffleLanguage.Env; |
| 268 | +import com.oracle.truffle.api.TruffleSafepoint; |
256 | 269 | import com.oracle.truffle.api.dsl.Bind; |
257 | 270 | import com.oracle.truffle.api.dsl.Cached; |
258 | 271 | import com.oracle.truffle.api.dsl.Cached.Shared; |
|
265 | 278 | import com.oracle.truffle.api.dsl.NodeFactory; |
266 | 279 | import com.oracle.truffle.api.dsl.Specialization; |
267 | 280 | import com.oracle.truffle.api.exception.AbstractTruffleException; |
| 281 | +import com.oracle.truffle.api.frame.FrameInstance.FrameAccess; |
268 | 282 | import com.oracle.truffle.api.frame.VirtualFrame; |
269 | 283 | import com.oracle.truffle.api.nodes.Node; |
270 | 284 | import com.oracle.truffle.api.strings.TruffleString; |
@@ -883,22 +897,81 @@ static PFrame counted(VirtualFrame frame, int depth, |
883 | 897 | @Builtin(name = "_current_frames") |
884 | 898 | @GenerateNodeFactory |
885 | 899 | abstract static class CurrentFrames extends PythonBuiltinNode { |
| 900 | + private static final long CURRENT_FRAMES_TIMEOUT_MILLIS = 20; |
| 901 | + |
886 | 902 | @Specialization |
887 | 903 | Object currentFrames(VirtualFrame frame, |
888 | 904 | @Bind Node inliningTarget, |
889 | 905 | @Cached AuditNode auditNode, |
890 | 906 | @Cached WarningsModuleBuiltins.WarnNode warnNode, |
891 | 907 | @Cached ReadFrameNode readFrameNode, |
892 | | - @Cached HashingStorageSetItem setHashingStorageItem, |
| 908 | + @Cached PyObjectHashNode hashNode, |
| 909 | + @Cached ObjectHashMap.PutNode putNode, |
| 910 | + @Bind PythonContext context, |
893 | 911 | @Bind PythonLanguage language) { |
894 | 912 | auditNode.audit(inliningTarget, "sys._current_frames"); |
895 | | - if (!getLanguage().singleThreadedAssumption.isValid()) { |
896 | | - warnNode.warn(frame, RuntimeWarning, ErrorMessages.WARN_CURRENT_FRAMES_MULTITHREADED); |
897 | | - } |
898 | 913 | PFrame currentFrame = readFrameNode.getCurrentPythonFrame(frame); |
899 | | - PDict result = PFactory.createDict(language); |
900 | | - result.setDictStorage(setHashingStorageItem.execute(frame, inliningTarget, result.getDictStorage(), PThread.getThreadId(Thread.currentThread()), currentFrame)); |
901 | | - return result; |
| 914 | + EconomicMapStorage framesMap = collectCurrentFrames(inliningTarget, context, currentFrame); |
| 915 | + return PFactory.createDict(language, framesMap); |
| 916 | + } |
| 917 | + |
| 918 | + @TruffleBoundary |
| 919 | + @SuppressWarnings("try") |
| 920 | + private static EconomicMapStorage collectCurrentFrames(Node inliningTarget, PythonContext context, PFrame currentFrame) { |
| 921 | + Thread currentThread = Thread.currentThread(); |
| 922 | + Thread[] threads = context.getThreads(); |
| 923 | + ArrayList<Thread> targetThreads = new ArrayList<>(threads.length); |
| 924 | + ConcurrentHashMap<Thread, Object> frames = new ConcurrentHashMap<>(); |
| 925 | + frames.put(currentThread, escapedFrameOrPlaceholder(currentFrame)); |
| 926 | + for (Thread thread : threads) { |
| 927 | + if (thread != currentThread && thread.isAlive()) { |
| 928 | + targetThreads.add(thread); |
| 929 | + } |
| 930 | + } |
| 931 | + if (!targetThreads.isEmpty()) { |
| 932 | + Thread[] threadArray = targetThreads.toArray(new Thread[0]); |
| 933 | + Future<Void> future = context.getEnv().submitThreadLocal(threadArray, new ThreadLocalAction(true, false) { |
| 934 | + @Override |
| 935 | + protected void perform(Access access) { |
| 936 | + PFrame pyFrame = ReadFrameNode.readFrameInThreadLocal(access, null, FrameAccess.READ_ONLY, AllPythonFramesSelector.INSTANCE, 0, CallerFlags.NEEDS_PFRAME, |
| 937 | + MaterializeFrameNode.getUncached()); |
| 938 | + frames.put(access.getThread(), escapedFrameOrPlaceholder(pyFrame)); |
| 939 | + } |
| 940 | + }); |
| 941 | + boolean[] timedOut = new boolean[1]; |
| 942 | + try (var gil = GilNode.uncachedRelease()) { |
| 943 | + TruffleSafepoint.setBlockedThreadInterruptible(inliningTarget, voidFuture -> { |
| 944 | + try { |
| 945 | + voidFuture.get(CURRENT_FRAMES_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); |
| 946 | + } catch (TimeoutException e) { |
| 947 | + timedOut[0] = true; |
| 948 | + } catch (CancellationException e) { |
| 949 | + // Ignore cancellation; unanswered threads will get assigned NONE |
| 950 | + } catch (ExecutionException e) { |
| 951 | + throw new RuntimeException(e); |
| 952 | + } |
| 953 | + }, future); |
| 954 | + } |
| 955 | + if (timedOut[0]) { |
| 956 | + future.cancel(false); |
| 957 | + } |
| 958 | + } |
| 959 | + |
| 960 | + EconomicMapStorage storage = EconomicMapStorage.create(targetThreads.size()); |
| 961 | + for (Thread thread : targetThreads) { |
| 962 | + long key = PThread.getThreadId(thread); |
| 963 | + long hash = PyObjectHashNode.hash(key); |
| 964 | + ObjectHashMap.PutNode.putUncached(storage, key, hash, frames.getOrDefault(thread, PNone.NONE)); |
| 965 | + } |
| 966 | + return storage; |
| 967 | + } |
| 968 | + |
| 969 | + private static Object escapedFrameOrPlaceholder(PFrame frame) { |
| 970 | + if (frame != null) { |
| 971 | + frame.getRef().markAsEscaped(); |
| 972 | + return frame; |
| 973 | + } |
| 974 | + return PNone.NONE; |
902 | 975 | } |
903 | 976 | } |
904 | 977 |
|
|
0 commit comments