diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_poll.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_poll.txt new file mode 100644 index 0000000000..9c143e5024 --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_poll.txt @@ -0,0 +1,6 @@ +test.test_poll.PollTests.test_poll1 @ darwin-arm64,linux-aarch64,linux-x86_64 +test.test_poll.PollTests.test_poll2 @ darwin-arm64,linux-aarch64,linux-x86_64 +test.test_poll.PollTests.test_poll3 @ darwin-arm64,linux-aarch64,linux-x86_64 +test.test_poll.PollTests.test_poll_blocks_with_negative_ms @ darwin-arm64,linux-aarch64,linux-x86_64 +test.test_poll.PollTests.test_poll_unit_tests @ darwin-arm64,linux-aarch64,linux-x86_64 +test.test_poll.PollTests.test_threaded_poll @ darwin-arm64,linux-aarch64,linux-x86_64 diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_selectors.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_selectors.txt index f4eb4d57be..10d5480d6b 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_selectors.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_selectors.txt @@ -17,6 +17,7 @@ test.test_selectors.DefaultSelectorTestCase.test_unregister @ darwin-arm64,linux test.test_selectors.DefaultSelectorTestCase.test_unregister_after_fd_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_selectors.DefaultSelectorTestCase.test_unregister_after_fd_close_and_reuse @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_selectors.DefaultSelectorTestCase.test_unregister_after_socket_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_selectors.PollSelectorTestCase.test_select_interrupt_noraise @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_selectors.SelectSelectorTestCase.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_selectors.SelectSelectorTestCase.test_context_manager @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_selectors.SelectSelectorTestCase.test_empty_select @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github diff --git a/graalpython/com.oracle.graal.python/AGENTS.md b/graalpython/com.oracle.graal.python/AGENTS.md index 9567d4e747..bcc9a6af86 100644 --- a/graalpython/com.oracle.graal.python/AGENTS.md +++ b/graalpython/com.oracle.graal.python/AGENTS.md @@ -26,6 +26,7 @@ com.oracle.graal.python/ ## CONVENTIONS - Code style enforced via pre-commit Eclipse formatter + checkstyle; don’t hand-format Java. - Keep naming/layout close to CPython where practical (helps cross-referencing). +- Put user-facing error and warning message strings in `src/com/oracle/graal/python/nodes/ErrorMessages.java`; reuse an existing constant when possible instead of inlining message literals at call sites. ## ANTI-PATTERNS - Don’t edit generated sources under `mxbuild/**` or distribution outputs; edit `src/**`. diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java index 8c3f35bb25..3cad68a2e7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java @@ -63,6 +63,8 @@ import java.util.Map.Entry; import java.util.logging.Level; +import org.graalvm.nativeimage.ImageInfo; + import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.PythonOS; import com.oracle.graal.python.builtins.modules.AbcModuleBuiltins; @@ -75,7 +77,6 @@ import com.oracle.graal.python.builtins.modules.CodecsModuleBuiltins; import com.oracle.graal.python.builtins.modules.CodecsTruffleModuleBuiltins; import com.oracle.graal.python.builtins.modules.CollectionsModuleBuiltins; -import com.oracle.graal.python.builtins.modules.codecs.CodecsRegistry; import com.oracle.graal.python.builtins.modules.ContextvarsModuleBuiltins; import com.oracle.graal.python.builtins.modules.ErrnoModuleBuiltins; import com.oracle.graal.python.builtins.modules.FaulthandlerModuleBuiltins; @@ -99,7 +100,6 @@ import com.oracle.graal.python.builtins.modules.PosixShMemModuleBuiltins; import com.oracle.graal.python.builtins.modules.PosixSubprocessModuleBuiltins; import com.oracle.graal.python.builtins.modules.PwdModuleBuiltins; -import com.oracle.graal.python.builtins.modules.pyexpat.PyExpatModuleBuiltins; import com.oracle.graal.python.builtins.modules.QueueModuleBuiltins; import com.oracle.graal.python.builtins.modules.RandomModuleBuiltins; import com.oracle.graal.python.builtins.modules.ReadlineModuleBuiltins; @@ -142,6 +142,7 @@ import com.oracle.graal.python.builtins.modules.cjkcodecs.MultibyteStreamReaderBuiltins; import com.oracle.graal.python.builtins.modules.cjkcodecs.MultibyteStreamWriterBuiltins; import com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins; +import com.oracle.graal.python.builtins.modules.codecs.CodecsRegistry; import com.oracle.graal.python.builtins.modules.csv.CSVDialectBuiltins; import com.oracle.graal.python.builtins.modules.csv.CSVModuleBuiltins; import com.oracle.graal.python.builtins.modules.csv.CSVReaderBuiltins; @@ -202,6 +203,8 @@ import com.oracle.graal.python.builtins.modules.pickle.PicklerMemoProxyBuiltins; import com.oracle.graal.python.builtins.modules.pickle.UnpicklerBuiltins; import com.oracle.graal.python.builtins.modules.pickle.UnpicklerMemoProxyBuiltins; +import com.oracle.graal.python.builtins.modules.pyexpat.PyExpatModuleBuiltins; +import com.oracle.graal.python.builtins.modules.pyexpat.XMLParserBuiltins; import com.oracle.graal.python.builtins.modules.re.MatchBuiltins; import com.oracle.graal.python.builtins.modules.re.PatternBuiltins; import com.oracle.graal.python.builtins.modules.re.SREModuleBuiltins; @@ -215,10 +218,10 @@ import com.oracle.graal.python.builtins.objects.NotImplementedBuiltins; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.array.ArrayBuiltins; +import com.oracle.graal.python.builtins.objects.asyncio.ANextAwaitableBuiltins; import com.oracle.graal.python.builtins.objects.asyncio.AsyncGenSendBuiltins; import com.oracle.graal.python.builtins.objects.asyncio.AsyncGenThrowBuiltins; import com.oracle.graal.python.builtins.objects.asyncio.AsyncGeneratorBuiltins; -import com.oracle.graal.python.builtins.objects.asyncio.ANextAwaitableBuiltins; import com.oracle.graal.python.builtins.objects.asyncio.CoroutineWrapperBuiltins; import com.oracle.graal.python.builtins.objects.bool.BoolBuiltins; import com.oracle.graal.python.builtins.objects.bytes.ByteArrayBuiltins; @@ -347,6 +350,7 @@ import com.oracle.graal.python.builtins.objects.range.RangeBuiltins; import com.oracle.graal.python.builtins.objects.referencetype.ReferenceTypeBuiltins; import com.oracle.graal.python.builtins.objects.reversed.ReversedBuiltins; +import com.oracle.graal.python.builtins.objects.select.PollBuiltins; import com.oracle.graal.python.builtins.objects.set.BaseSetBuiltins; import com.oracle.graal.python.builtins.objects.set.FrozenSetBuiltins; import com.oracle.graal.python.builtins.objects.set.SetBuiltins; @@ -371,7 +375,6 @@ import com.oracle.graal.python.builtins.objects.tuple.InstantiableStructSequenceBuiltins; import com.oracle.graal.python.builtins.objects.tuple.StructSequenceBuiltins; import com.oracle.graal.python.builtins.objects.tuple.TupleBuiltins; -import com.oracle.graal.python.builtins.modules.pyexpat.XMLParserBuiltins; import com.oracle.graal.python.builtins.objects.tuple.TupleGetterBuiltins; import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass; import com.oracle.graal.python.builtins.objects.type.PythonManagedClass; @@ -422,7 +425,6 @@ import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.strings.TruffleString; -import org.graalvm.nativeimage.ImageInfo; /** * The core is a historical artifact and PythonContext and Python3Core should be merged. @@ -674,6 +676,7 @@ private static PythonBuiltins[] initializeBuiltins(TruffleLanguage.Env env) { new XMLParserBuiltins(), new AstModuleBuiltins(), PythonImageBuildOptions.WITHOUT_NATIVE_POSIX && (PythonImageBuildOptions.WITHOUT_JAVA_INET || !env.isSocketIOAllowed()) ? null : new SelectModuleBuiltins(), + PythonImageBuildOptions.WITHOUT_NATIVE_POSIX && (PythonImageBuildOptions.WITHOUT_JAVA_INET || !env.isSocketIOAllowed()) ? null : new PollBuiltins(), PythonImageBuildOptions.WITHOUT_NATIVE_POSIX && (PythonImageBuildOptions.WITHOUT_JAVA_INET || !env.isSocketIOAllowed()) ? null : new SocketModuleBuiltins(), PythonImageBuildOptions.WITHOUT_NATIVE_POSIX && (PythonImageBuildOptions.WITHOUT_JAVA_INET || !env.isSocketIOAllowed()) ? null : new SocketBuiltins(), PythonImageBuildOptions.WITHOUT_PLATFORM_ACCESS ? null : new SignalModuleBuiltins(), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java index d1e219cd6f..95fdc70b30 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java @@ -563,6 +563,7 @@ def x(self): -- Simple, unbounded, reentrant FIFO queue.""")), + PPoll("poll", PythonObject, newBuilder().moduleName("select").disallowInstantiation()), PRandom("Random", PythonObject, newBuilder().publishInModule("_random").basetype().slots(RandomBuiltins.SLOTS)), PRange("range", PythonObject, newBuilder().publishInModule(J_BUILTINS).slots(RangeBuiltins.SLOTS).doc(""" range(stop) -> range object diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SelectModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SelectModuleBuiltins.java index abdb923349..6f9963e03d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SelectModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SelectModuleBuiltins.java @@ -48,11 +48,14 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.Builtin; +import com.oracle.graal.python.annotations.PythonOS; import com.oracle.graal.python.builtins.CoreFunctions; import com.oracle.graal.python.builtins.Python3Core; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; +import com.oracle.graal.python.builtins.objects.exception.OSErrorEnum; import com.oracle.graal.python.builtins.objects.list.PList; +import com.oracle.graal.python.builtins.objects.select.PPoll; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.lib.PyObjectAsFileDescriptor; import com.oracle.graal.python.lib.PyObjectGetItem; @@ -74,6 +77,7 @@ import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; import com.oracle.graal.python.runtime.PosixSupportLibrary.SelectResult; import com.oracle.graal.python.runtime.PosixSupportLibrary.Timeval; +import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.exception.PythonErrorType; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.util.ArrayBuilder; @@ -94,15 +98,6 @@ @CoreFunctions(defineModule = "select") public final class SelectModuleBuiltins extends PythonBuiltins { - /* - * ATTENTION: if we ever add "poll" support, update the code in - * MultiprocessingModuleBuilins#SelectNode to use it if available - */ - - public SelectModuleBuiltins() { - addBuiltinConstant("error", PythonErrorType.OSError); - } - @Override protected List> getNodeFactories() { return SelectModuleBuiltinsFactory.getFactories(); @@ -111,9 +106,25 @@ protected List> getNodeFa @Override public void initialize(Python3Core core) { super.initialize(core); + addBuiltinConstant("error", PythonErrorType.OSError); if (PosixConstants.PIPE_BUF.defined) { addBuiltinConstant("PIPE_BUF", PosixConstants.PIPE_BUF.getValueIfDefined()); } + for (PosixConstants.IntConstant constant : PosixConstants.pollFlags) { + if (constant.defined) { + addBuiltinConstant(constant.name, constant.getValueIfDefined()); + } + } + } + + @Builtin(name = "poll", minNumOfPositionalArgs = 0, os = PythonOS.PLATFORM_LINUX) + @Builtin(name = "poll", minNumOfPositionalArgs = 0, os = PythonOS.PLATFORM_DARWIN) + @GenerateNodeFactory + abstract static class PollNode extends PythonBuiltinNode { + @Specialization + static PPoll poll(@Bind PythonLanguage language) { + return PFactory.createPoll(language); + } } @Builtin(name = "select", minNumOfPositionalArgs = 3, parameterNames = {"rlist", "wlist", "xlist", "timeout"}) @@ -139,29 +150,48 @@ static PTuple doGeneric(VirtualFrame frame, Object rlist, Object wlist, Object x ObjAndFDList xFDs = seq2set(frame, inliningTarget, xlist, sizeNode, asFileDescriptor, callGetItemNode, constructListNode, raiseNode); Timeval timeoutval = null; + long timeoutNs = -1; if (!PGuards.isPNone(timeout)) { isNotNoneTimeout.enter(inliningTarget); - timeoutval = TimeUtils.pyTimeAsTimeval(pyTimeFromObjectNode.execute(frame, inliningTarget, timeout, RoundType.TIMEOUT, SEC_TO_NS)); + timeoutNs = pyTimeFromObjectNode.execute(frame, inliningTarget, timeout, RoundType.TIMEOUT, SEC_TO_NS); + timeoutval = TimeUtils.pyTimeAsTimeval(timeoutNs); if (timeoutval.getSeconds() < 0) { throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.ValueError, ErrorMessages.MUST_BE_NON_NEGATIVE, "timeout"); } } SelectResult result; - try { - gil.release(true); + long startNano = timeoutval != null ? System.nanoTime() : 0; + while (true) { try { - result = posixLib.select(PosixSupport.get(inliningTarget), readFDs.fds, writeFDs.fds, xFDs.fds, timeoutval); - } finally { - gil.acquire(); + gil.release(true); + try { + result = posixLib.select(PosixSupport.get(inliningTarget), readFDs.fds, writeFDs.fds, xFDs.fds, timeoutval); + } finally { + gil.acquire(); + } + break; + } catch (ChannelNotSelectableException e) { + // GraalPython hack: if one of the channels is not selectable (can happen only + // in the emulated mode), we just return everything. + notSelectableBranch.enter(inliningTarget); + return PFactory.createTuple(language, new Object[]{rlist, wlist, xlist}); + } catch (PosixException e) { + if (!e.hasErrno(OSErrorEnum.EINTR)) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); + } + PythonContext.triggerAsyncActions(inliningTarget); + if (timeoutval != null) { + long remainingNs = timeoutNs - (System.nanoTime() - startNano); + if (remainingNs <= 0) { + return PFactory.createTuple(language, new PList[]{ + PFactory.createList(language), + PFactory.createList(language), + PFactory.createList(language)}); + } + timeoutval = TimeUtils.pyTimeAsTimeval(remainingNs); + } } - } catch (ChannelNotSelectableException e) { - // GraalPython hack: if one of the channels is not selectable (can happen only in - // the emulated mode), we just return everything. - notSelectableBranch.enter(inliningTarget); - return PFactory.createTuple(language, new Object[]{rlist, wlist, xlist}); - } catch (PosixException e) { - throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } return PFactory.createTuple(language, new PList[]{ toList(result.getReadFds(), readFDs, language), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/select/PPoll.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/select/PPoll.java new file mode 100644 index 0000000000..5cc9057110 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/select/PPoll.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.graal.python.builtins.objects.select; + +import java.util.LinkedHashMap; +import java.util.Map; + +import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.object.Shape; + +public final class PPoll extends PythonBuiltinObject { + private final Map registrations; + private int[] pollFds; + private int[] pollEvents; + private int[] pollRevents; + private boolean pollDataUpToDate; + private boolean pollRunning; + + @TruffleBoundary + public PPoll(Object cls, Shape instanceShape) { + super(cls, instanceShape); + registrations = new LinkedHashMap<>(); + } + + @TruffleBoundary + public synchronized void register(int fd, int events) { + registrations.put(fd, events); + pollDataUpToDate = false; + } + + @TruffleBoundary + public synchronized boolean modify(int fd, int events) { + if (!registrations.containsKey(fd)) { + return false; + } + registrations.put(fd, events); + pollDataUpToDate = false; + return true; + } + + @TruffleBoundary + public synchronized boolean unregister(int fd) { + if (registrations.remove(fd) == null) { + return false; + } + pollDataUpToDate = false; + return true; + } + + @TruffleBoundary + public synchronized boolean startPoll() { + if (pollRunning) { + return false; + } + if (!pollDataUpToDate) { + pollFds = new int[registrations.size()]; + pollEvents = new int[registrations.size()]; + pollRevents = new int[registrations.size()]; + int i = 0; + for (Map.Entry entry : registrations.entrySet()) { + pollFds[i] = entry.getKey(); + pollEvents[i] = entry.getValue(); + i++; + } + pollDataUpToDate = true; + } + pollRunning = true; + return true; + } + + public int[] getPollFds() { + return pollFds; + } + + public int[] getPollEvents() { + return pollEvents; + } + + public int[] getPollRevents() { + return pollRevents; + } + + @TruffleBoundary + public synchronized void finishPoll() { + pollRunning = false; + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/select/PollBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/select/PollBuiltins.java new file mode 100644 index 0000000000..eb2dc10cd0 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/select/PollBuiltins.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.graal.python.builtins.objects.select; + +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.KeyError; +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.OverflowError; +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.RuntimeError; +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError; +import static com.oracle.graal.python.util.TimeUtils.MS_TO_NS; + +import java.util.List; + +import com.oracle.graal.python.PythonLanguage; +import com.oracle.graal.python.annotations.Builtin; +import com.oracle.graal.python.builtins.CoreFunctions; +import com.oracle.graal.python.builtins.PythonBuiltinClassType; +import com.oracle.graal.python.builtins.PythonBuiltins; +import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.exception.OSErrorEnum; +import com.oracle.graal.python.builtins.objects.list.PList; +import com.oracle.graal.python.lib.PyLongAsLongAndOverflowNode; +import com.oracle.graal.python.lib.PyLongCheckNode; +import com.oracle.graal.python.lib.PyObjectAsFileDescriptor; +import com.oracle.graal.python.lib.PyTimeFromObjectNode; +import com.oracle.graal.python.lib.PyTimeFromObjectNode.RoundType; +import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PConstructAndRaiseNode; +import com.oracle.graal.python.nodes.PGuards; +import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; +import com.oracle.graal.python.nodes.function.PythonBuiltinNode; +import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; +import com.oracle.graal.python.runtime.GilNode; +import com.oracle.graal.python.runtime.PosixConstants; +import com.oracle.graal.python.runtime.PosixSupport; +import com.oracle.graal.python.runtime.PosixSupportLibrary; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; +import com.oracle.graal.python.runtime.PythonContext; +import com.oracle.graal.python.runtime.exception.PException; +import com.oracle.graal.python.runtime.object.PFactory; +import com.oracle.graal.python.util.OverflowException; +import com.oracle.graal.python.util.TimeUtils; +import com.oracle.truffle.api.dsl.Bind; +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.GenerateNodeFactory; +import com.oracle.truffle.api.dsl.NodeFactory; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.Node; + +@CoreFunctions(extendClasses = PythonBuiltinClassType.PPoll) +public final class PollBuiltins extends PythonBuiltins { + @Override + protected List> getNodeFactories() { + return PollBuiltinsFactory.getFactories(); + } + + @Builtin(name = "register", minNumOfPositionalArgs = 2, numOfPositionalOnlyArgs = 3, parameterNames = {"$self", "fd", "eventmask"}) + @GenerateNodeFactory + abstract static class RegisterNode extends PythonBuiltinNode { + @Specialization + static PNone register(VirtualFrame frame, PPoll self, Object fdObject, Object eventmask, + @Bind Node inliningTarget, + @Cached PyObjectAsFileDescriptor asFileDescriptor, + @Cached PyLongCheckNode longCheck, + @Cached PyLongAsLongAndOverflowNode asLong, + @Cached PRaiseNode raiseNode) { + int fd = asFileDescriptor.execute(frame, inliningTarget, fdObject); + int events = PGuards.isNoValue(eventmask) ? defaultEventMask() : asUnsignedShort(frame, inliningTarget, eventmask, longCheck, asLong, raiseNode); + self.register(fd, events); + return PNone.NONE; + } + } + + @Builtin(name = "modify", minNumOfPositionalArgs = 3, numOfPositionalOnlyArgs = 3, parameterNames = {"$self", "fd", "eventmask"}) + @GenerateNodeFactory + abstract static class ModifyNode extends PythonBuiltinNode { + @Specialization + static PNone modify(VirtualFrame frame, PPoll self, Object fdObject, Object eventmask, + @Bind Node inliningTarget, + @Cached PyObjectAsFileDescriptor asFileDescriptor, + @Cached PyLongCheckNode longCheck, + @Cached PyLongAsLongAndOverflowNode asLong, + @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode, + @Cached PRaiseNode raiseNode) { + int fd = asFileDescriptor.execute(frame, inliningTarget, fdObject); + int events = asUnsignedShort(frame, inliningTarget, eventmask, longCheck, asLong, raiseNode); + if (!self.modify(fd, events)) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSError(frame, OSErrorEnum.ENOENT); + } + return PNone.NONE; + } + } + + @Builtin(name = "unregister", minNumOfPositionalArgs = 2, numOfPositionalOnlyArgs = 2, parameterNames = {"$self", "fd"}) + @GenerateNodeFactory + abstract static class UnregisterNode extends PythonBuiltinNode { + @Specialization + static PNone unregister(VirtualFrame frame, PPoll self, Object fdObject, + @Bind Node inliningTarget, + @Cached PyObjectAsFileDescriptor asFileDescriptor, + @Cached PRaiseNode raiseNode) { + int fd = asFileDescriptor.execute(frame, inliningTarget, fdObject); + if (!self.unregister(fd)) { + throw raiseNode.raise(inliningTarget, KeyError, new Object[]{fd}); + } + return PNone.NONE; + } + } + + @Builtin(name = "poll", minNumOfPositionalArgs = 1, numOfPositionalOnlyArgs = 2, parameterNames = {"$self", "timeout"}) + @GenerateNodeFactory + abstract static class PollNode extends PythonBuiltinNode { + @Specialization + static PList poll(VirtualFrame frame, PPoll self, Object timeoutObject, + @Bind Node inliningTarget, + @Bind PythonLanguage language, + @Cached PyTimeFromObjectNode fromTime, + @Cached IsBuiltinObjectProfile typeErrorProfile, + @CachedLibrary(limit = "1") PosixSupportLibrary posixLib, + @Cached GilNode gil, + @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode, + @Cached PRaiseNode raiseNode) { + int timeoutMs = -1; + long timeoutNs = -1; + if (!(timeoutObject instanceof PNone)) { + try { + timeoutNs = fromTime.execute(frame, inliningTarget, timeoutObject, RoundType.TIMEOUT, MS_TO_NS); + } catch (PException e) { + e.expectTypeError(inliningTarget, typeErrorProfile); + throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.TIMEOUT_MUST_BE_INTEGER_OR_NONE); + } + long timeoutMsLong = TimeUtils.pyTimeDivide(timeoutNs, MS_TO_NS); + if (timeoutMsLong < Integer.MIN_VALUE || timeoutMsLong > Integer.MAX_VALUE) { + throw raiseNode.raise(inliningTarget, OverflowError, ErrorMessages.TIMEOUT_IS_TOO_LARGE); + } + if (timeoutMsLong >= 0) { + timeoutMs = (int) timeoutMsLong; + } + } + + if (!self.startPoll()) { + throw raiseNode.raise(inliningTarget, RuntimeError, ErrorMessages.CONCURRENT_POLL_INVOCATION); + } + int[] pollFds = self.getPollFds(); + int[] pollEvents = self.getPollEvents(); + int[] pollRevents = self.getPollRevents(); + boolean timedOut = false; + long startNano = timeoutMs >= 0 ? System.nanoTime() : 0; + try { + while (true) { + try { + gil.release(true); + try { + posixLib.poll(PosixSupport.get(inliningTarget), pollFds, pollEvents, pollRevents, timeoutMs); + } finally { + gil.acquire(); + } + break; + } catch (PosixException e) { + if (!e.hasErrno(OSErrorEnum.EINTR)) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); + } + PythonContext.triggerAsyncActions(inliningTarget); + if (timeoutMs >= 0) { + long remainingNs = timeoutNs - (System.nanoTime() - startNano); + if (remainingNs <= 0) { + timedOut = true; + break; + } + timeoutMs = (int) TimeUtils.pyTimeDivide(remainingNs, MS_TO_NS); + } + } + } + } finally { + self.finishPoll(); + } + + if (timedOut) { + return PFactory.createList(language); + } + + int resultSize = 0; + for (int revents : pollRevents) { + if (revents != 0) { + resultSize++; + } + } + Object[] result = new Object[resultSize]; + int resultIndex = 0; + for (int i = 0; i < pollRevents.length; i++) { + if (pollRevents[i] != 0) { + result[resultIndex++] = PFactory.createTuple(language, new Object[]{pollFds[i], pollRevents[i]}); + } + } + return PFactory.createList(language, result); + } + } + + private static int defaultEventMask() { + return PosixConstants.POLLIN.getValueIfDefined() | PosixConstants.POLLPRI.getValueIfDefined() | PosixConstants.POLLOUT.getValueIfDefined(); + } + + private static int asUnsignedShort(VirtualFrame frame, Node inliningTarget, Object value, PyLongCheckNode longCheck, + PyLongAsLongAndOverflowNode asLong, PRaiseNode raiseNode) { + if (!longCheck.execute(inliningTarget, value)) { + throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.INTEGER_REQUIRED); + } + long result; + try { + result = asLong.execute(frame, inliningTarget, value); + } catch (OverflowException e) { + throw raiseNode.raise(inliningTarget, OverflowError, ErrorMessages.PYTHON_INT_TOO_LARGE_TO_CONV_TO, "C unsigned long"); + } + if (result < 0) { + throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.VALUE_MUST_BE_POSITIVE); + } + if (result > 0xffff) { + throw raiseNode.raise(inliningTarget, OverflowError, ErrorMessages.PYTHON_INT_TOO_LARGE_TO_CONV_TO, "C unsigned short"); + } + return (int) result; + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketUtils.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketUtils.java index 399bffaf39..e3513ecdb9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketUtils.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketUtils.java @@ -44,13 +44,16 @@ import static com.oracle.graal.python.builtins.objects.exception.OSErrorEnum.EINTR; import static com.oracle.graal.python.builtins.objects.exception.OSErrorEnum.EWOULDBLOCK; import static com.oracle.graal.python.builtins.objects.socket.PSocket.INVALID_FD; +import static com.oracle.graal.python.util.PythonUtils.EMPTY_INT_ARRAY; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PConstructAndRaiseNode; import com.oracle.graal.python.runtime.GilNode; +import com.oracle.graal.python.runtime.PosixConstants; import com.oracle.graal.python.runtime.PosixSupportLibrary; import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixErrnoException; import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.SelectResult; import com.oracle.graal.python.runtime.PosixSupportLibrary.Timeval; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.util.TimeUtils; @@ -100,9 +103,26 @@ public static T callSocketFunctionWithRetry(Frame frame, Node inliningTarget try { gil.release(true); try { - // CPython uses poll for a single fd when available here, so even higher fd - // socket connections can be established - if (!posixLib.poll(posixSupport, socket.getFd(), writing, selectTimeout)) { + boolean ready; + if (PosixConstants.IS_WIN32) { + int[] errorfds = connect ? new int[]{socket.getFd()} : EMPTY_INT_ARRAY; + if (writing) { + SelectResult selected = posixLib.select(posixSupport, EMPTY_INT_ARRAY, new int[]{socket.getFd()}, errorfds, selectTimeout); + ready = selected.getWriteFds()[0] || connect && selected.getErrorFds()[0]; + } else { + SelectResult selected = posixLib.select(posixSupport, new int[]{socket.getFd()}, EMPTY_INT_ARRAY, errorfds, selectTimeout); + ready = selected.getReadFds()[0] || connect && selected.getErrorFds()[0]; + } + } else { + int event = (writing ? PosixConstants.POLLOUT : PosixConstants.POLLIN).getValueIfDefined(); + if (connect) { + event |= PosixConstants.POLLERR.getValueIfDefined(); + } + int[] revents = new int[1]; + posixLib.poll(posixSupport, new int[]{socket.getFd()}, new int[]{event}, revents, pollTimeout(selectTimeout)); + ready = revents[0] != 0; + } + if (!ready) { throw constructAndRaiseNode.get(inliningTarget).raiseTimeoutError(frame, ErrorMessages.TIMED_OUT); } } finally { @@ -145,6 +165,14 @@ public static T callSocketFunctionWithRetry(Frame frame, Node inliningTarget } } + private static int pollTimeout(Timeval timeout) { + if (timeout == null) { + return -1; + } + long milliseconds = timeout.getSeconds() * 1000 + (timeout.getMicroseconds() + 999) / 1000; + return milliseconds > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) milliseconds; + } + public static class TimeoutHelper { long startNano; long initialTimeoutNs; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java index cb53abd406..7e5e3f206f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java @@ -420,6 +420,7 @@ private static long defaultBuiltinFlags(PythonBuiltinClassType clazz) { case PAsyncGenerator: case PCell: case PIterator: + case PPoll: result = DEFAULT | HAVE_GC; break; case PMappingproxy: diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java index 8b674fbcc1..2e73793ff5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java @@ -208,6 +208,7 @@ public abstract class ErrorMessages { : tsLiteral("cannot load %s: %m"); public static final TruffleString CANNOT_RELEASE_UNAQUIRED_LOCK = tsLiteral("cannot release un-acquired lock"); public static final TruffleString CANNOT_REENTER_TEE_ITERATOR = tsLiteral("cannot re-enter the tee iterator"); + public static final TruffleString CONCURRENT_POLL_INVOCATION = tsLiteral("concurrent poll() invocation"); public static final TruffleString CANNOT_SPECIFY_BOTH_COMMA_AND_UNDERSCORE = tsLiteral("Cannot specify both ',' and '_'."); public static final TruffleString CANNOT_SPECIFY_C_WITH_C = tsLiteral("Cannot specify '%c' with '%c'."); public static final TruffleString CANNOT_SUBCLASS_AN_INSTANCE_OF_PARAMSPEC = tsLiteral("Cannot subclass an instance of ParamSpec"); @@ -880,6 +881,8 @@ public abstract class ErrorMessages { public static final TruffleString RANGE_OBJ_IDX_OUT_OF_RANGE = tsLiteral("range object index out of range"); public static final TruffleString NUMBER_OF_BITS_MUST_BE_NON_NEGATIVE = tsLiteral("number of bits must be non-negative"); public static final TruffleString TIMEOUT_MUST_BE_NON_NEG_NUM = tsLiteral("'timeout' must be a non-negative number"); + public static final TruffleString TIMEOUT_MUST_BE_INTEGER_OR_NONE = tsLiteral("timeout must be an integer or None"); + public static final TruffleString TIMEOUT_IS_TOO_LARGE = tsLiteral("timeout is too large"); public static final TruffleString THIRD_ARG_MUST_BE_A_VALID_MACHINE_CODE_FMT = tsLiteral("third argument must be a valid machine format code."); public static final TruffleString OBJ_DOES_NOT_PROVIDE_DIR = tsLiteral("object does not provide __dir__"); public static final TruffleString ARRAY_ARG_1_MUST_BE_UNICODE = tsLiteral("array() argument 1 must be a unicode character, not str"); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java index a766102861..a77c43c72e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java @@ -171,10 +171,12 @@ import java.nio.ByteOrder; import java.nio.channels.AlreadyConnectedException; import java.nio.channels.ByteChannel; +import java.nio.channels.CancelledKeyException; import java.nio.channels.Channel; import java.nio.channels.DatagramChannel; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; +import java.nio.channels.IllegalBlockingModeException; import java.nio.channels.NetworkChannel; import java.nio.channels.NotYetConnectedException; import java.nio.channels.ReadableByteChannel; @@ -688,7 +690,12 @@ public SelectResult select(int[] readfds, int[] writefds, int[] errorfds, Timeva for (SelectableChannel channel : writeChannels) { channel.configureBlocking(false); - channel.register(selector, writeOps); + SelectionKey key = channel.keyFor(selector); + if (key == null) { + channel.register(selector, writeOps); + } else { + key.interestOps(key.interestOps() | writeOps); + } } // IMPORTANT: The meaning of the timeout value is slightly different: 'timeout == 0.0' @@ -716,8 +723,14 @@ public SelectResult select(int[] readfds, int[] writefds, int[] errorfds, Timeva boolean[] resWritefds = createSelectedMap(writefds, writeChannels, selector, writeOps); boolean[] resErrfds = new boolean[errorfds.length]; - assert selected == countSelected(resReadfds) + countSelected(resWritefds) + countSelected(resErrfds); + // One selected channel can satisfy multiple entries and both read and write interests. + assert selected <= countSelected(resReadfds) + countSelected(resWritefds) + countSelected(resErrfds); return new SelectResult(resReadfds, resWritefds, resErrfds); + } catch (CancelledKeyException e) { + // A concurrently closed descriptor cancels its selection key. + throw posixException(OSErrorEnum.EBADF); + } catch (IllegalBlockingModeException e) { + throw posixException(OSErrorEnum.EINVAL); } catch (IOException e) { throw posixException(OSErrorEnum.fromException(e, TruffleString.EqualNode.getUncached())); } finally { @@ -733,17 +746,20 @@ public SelectResult select(int[] readfds, int[] writefds, int[] errorfds, Timeva channel.configureBlocking(true); } } - } catch (IOException e) { + } catch (IOException | IllegalBlockingModeException e) { // We didn't manage to restore the blocking status, ignore } } } - private static boolean[] createSelectedMap(int[] fds, SelectableChannel[] channels, Selector selector, int op) { + private static boolean[] createSelectedMap(int[] fds, SelectableChannel[] channels, Selector selector, int op) throws PosixException { boolean[] result = new boolean[fds.length]; for (int i = 0; i < channels.length; i++) { SelectableChannel channel = channels[i]; SelectionKey selectionKey = channel.keyFor(selector); + if (selectionKey == null || !selectionKey.isValid()) { + throw posixException(OSErrorEnum.EBADF); + } result[i] = (selectionKey.readyOps() & op) != 0; } return result; @@ -762,41 +778,99 @@ private static int countSelected(boolean[] selected) { private SelectableChannel[] getSelectableChannels(int[] fds) throws PosixException { SelectableChannel[] channels = new SelectableChannel[fds.length]; for (int i = 0; i < fds.length; i++) { - Channel ch = getFileChannel(fds[i]); - if (ch == null) { - throw posixException(OSErrorEnum.EBADF); - } - if (ch instanceof SelectableChannel) { - channels[i] = (SelectableChannel) ch; - } else if (ch instanceof EmulatedDatagramSocket) { - channels[i] = ((EmulatedDatagramSocket) ch).channel; - } else if (ch instanceof EmulatedStreamSocket) { - EmulatedStreamSocket streamSocket = (EmulatedStreamSocket) ch; - synchronized (streamSocket) { - if (streamSocket.clientChannel != null) { - channels[i] = streamSocket.clientChannel; - } else if (streamSocket.serverChannel != null) { - channels[i] = streamSocket.serverChannel; - } else { - throw ChannelNotSelectableException.INSTANCE; - } + channels[i] = getSelectableChannel(fds[i]); + } + return channels; + } + + private SelectableChannel getSelectableChannel(int fd) throws PosixException { + Channel ch = getFileChannel(fd); + if (ch == null) { + throw posixException(OSErrorEnum.EBADF); + } + if (ch instanceof SelectableChannel) { + return (SelectableChannel) ch; + } else if (ch instanceof EmulatedDatagramSocket) { + return ((EmulatedDatagramSocket) ch).channel; + } else if (ch instanceof EmulatedStreamSocket) { + EmulatedStreamSocket streamSocket = (EmulatedStreamSocket) ch; + synchronized (streamSocket) { + if (streamSocket.clientChannel != null) { + return streamSocket.clientChannel; + } else if (streamSocket.serverChannel != null) { + return streamSocket.serverChannel; } - } else { - throw ChannelNotSelectableException.INSTANCE; } } - return channels; + throw ChannelNotSelectableException.INSTANCE; } @ExportMessage - public boolean poll(int fd, boolean forWriting, Timeval timeout) throws PosixException { - SelectResult r = select(forWriting ? EMPTY_INT_ARRAY : new int[]{fd}, - forWriting ? new int[]{fd} : EMPTY_INT_ARRAY, EMPTY_INT_ARRAY, timeout); - if (forWriting) { - return r.getWriteFds().length > 0 && r.getWriteFds()[0]; - } else { - return r.getReadFds().length > 0 && r.getReadFds()[0]; + @TruffleBoundary + public void poll(int[] fds, int[] events, int[] revents, int timeout) throws PosixException { + assert fds.length == events.length && fds.length == revents.length; + java.util.Arrays.fill(revents, 0); + int pollIn = pollFlag(PosixConstants.POLLIN); + int pollOut = pollFlag(PosixConstants.POLLOUT); + int pollRdNorm = pollFlag(PosixConstants.POLLRDNORM); + int pollWrNorm = pollFlag(PosixConstants.POLLWRNORM); + int[] readFds = new int[fds.length]; + int[] writeFds = new int[fds.length]; + int readCount = 0; + int writeCount = 0; + for (int i = 0; i < fds.length; i++) { + if (getFileChannel(fds[i]) == null) { + revents[i] = PosixConstants.POLLNVAL.getValueIfDefined(); + continue; + } + try { + SelectableChannel channel = getSelectableChannel(fds[i]); + if ((events[i] & (pollIn | pollRdNorm)) != 0 && (channel.validOps() & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0) { + readFds[readCount++] = fds[i]; + } + if ((events[i] & (pollOut | pollWrNorm)) != 0 && (channel.validOps() & SelectionKey.OP_WRITE) != 0) { + writeFds[writeCount++] = fds[i]; + } + } catch (ChannelNotSelectableException e) { + // Files that cannot be registered with a Java Selector do not block. + revents[i] = events[i] & (pollIn | pollOut | pollRdNorm | pollWrNorm); + } + } + readFds = Arrays.copyOf(readFds, readCount); + writeFds = Arrays.copyOf(writeFds, writeCount); + boolean alreadyReady = false; + for (int event : revents) { + alreadyReady |= event != 0; + } + Timeval timeval = null; + if (alreadyReady || timeout == 0) { + timeval = Timeval.SELECT_TIMEOUT_NOW; + } else if (timeout > 0) { + timeval = new Timeval(timeout / 1000, timeout % 1000 * 1000); + } + SelectResult selected = select(readFds, writeFds, EMPTY_INT_ARRAY, timeval); + for (int i = 0; i < readFds.length; i++) { + if (selected.getReadFds()[i]) { + for (int j = 0; j < fds.length; j++) { + if (fds[j] == readFds[i]) { + revents[j] |= events[j] & (pollIn | pollRdNorm); + } + } + } } + for (int i = 0; i < writeFds.length; i++) { + if (selected.getWriteFds()[i]) { + for (int j = 0; j < fds.length; j++) { + if (fds[j] == writeFds[i]) { + revents[j] |= events[j] & (pollOut | pollWrNorm); + } + } + } + } + } + + private static int pollFlag(PosixConstants.IntConstant constant) { + return constant.defined ? constant.getValueIfDefined() : 0; } @ExportMessage diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java index de40c75409..c17800ebcc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java @@ -309,11 +309,12 @@ public SelectResult select(int[] readfds, int[] writefds, int[] errorfds, Timeva } @ExportMessage - public boolean poll(int fd, boolean forWriting, Timeval timeout, + public void poll(int[] fds, int[] events, int[] revents, int timeout, @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException { - logEnter("poll", "%s %s %s", fd, forWriting, timeout); + logEnter("poll", "%s %s %s", fds, events, timeout); try { - return logExit("poll", "%s", lib.poll(delegate, fd, forWriting, timeout)); + lib.poll(delegate, fds, events, revents, timeout); + logExit("poll", "%s", revents); } catch (PosixException e) { throw logException("poll", e); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixConstants.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixConstants.java index edb4fb39df..8cdedc3c00 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixConstants.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixConstants.java @@ -70,6 +70,13 @@ public enum NativePosixConstants { SIZEOF_STRUCT_IN6_ADDR, SIZEOF_STRUCT_IN6_ADDR_S6_ADDR, OFFSETOF_STRUCT_IN6_ADDR_S6_ADDR, + SIZEOF_STRUCT_POLLFD, + SIZEOF_STRUCT_POLLFD_FD, + OFFSETOF_STRUCT_POLLFD_FD, + SIZEOF_STRUCT_POLLFD_EVENTS, + OFFSETOF_STRUCT_POLLFD_EVENTS, + SIZEOF_STRUCT_POLLFD_REVENTS, + OFFSETOF_STRUCT_POLLFD_REVENTS, SIZEOF_STRUCT_SOCKADDR_UN, SIZEOF_STRUCT_SOCKADDR_UN_SUN_FAMILY, OFFSETOF_STRUCT_SOCKADDR_UN_SUN_FAMILY, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java index e1ffd6e3ad..0606acc0ee 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java @@ -48,6 +48,9 @@ import static com.oracle.graal.python.nodes.StringLiterals.T_NATIVE; import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_IN6_ADDR_S6_ADDR; import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_IN_ADDR_S_ADDR; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_POLLFD_EVENTS; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_POLLFD_FD; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_POLLFD_REVENTS; import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_ADDR; import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_FLOWINFO; import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_PORT; @@ -57,6 +60,7 @@ import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_SA_FAMILY; import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_UN_SUN_PATH; import static com.oracle.graal.python.runtime.NativePosixConstants.SIZEOF_STRUCT_SOCKADDR_IN; +import static com.oracle.graal.python.runtime.NativePosixConstants.SIZEOF_STRUCT_POLLFD; import static com.oracle.graal.python.runtime.NativePosixConstants.SIZEOF_STRUCT_SOCKADDR_IN6; import static com.oracle.graal.python.runtime.NativePosixConstants.SIZEOF_STRUCT_SOCKADDR_SA_FAMILY; import static com.oracle.graal.python.runtime.NativePosixConstants.SIZEOF_STRUCT_SOCKADDR_STORAGE; @@ -250,8 +254,8 @@ abstract static class PosixNativeFunctionInvoker { @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, POINTER, SINT32, POINTER, SINT32, SINT64, SINT64, POINTER}) abstract int call_select(int nfds, long readfds, int readfdsLen, long writefds, int writefdsLen, long errfds, int errfdsLen, long timeoutSec, long timeoutUsec, long selected); - @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32, SINT64, SINT64}) - abstract int call_poll(int fd, int writing, long timeoutSec, long timeoutUsec); + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32, SINT32}) + abstract int call_poll(long pollData, int count, int timeout); @DowncallSignature(returnType = SINT64, argumentTypes = {SINT32, SINT64, SINT32}) abstract long call_lseek(int fd, long offset, int whence); @@ -940,20 +944,33 @@ private static int findMax(int[] items, int currentMax) { } @ExportMessage - public boolean poll(int fd, boolean forWriting, Timeval timeout) throws PosixException { - long secs = -1, usecs = -1; - if (timeout != null) { - secs = timeout.getSeconds(); - usecs = timeout.getMicroseconds(); - } - int result = posixNativeFunctionInvoker.call_poll(fd, forWriting ? 1 : 0, secs, usecs); - if (result < 0) { - throw getErrnoAndThrowPosixException(); - } - if (result == 0) { - return false; - } else { - return true; + public void poll(int[] fds, int[] events, int[] revents, int timeout) throws PosixException { + assert fds.length == events.length && fds.length == revents.length; + int count = fds.length; + long nativePollData = NULLPTR; + try { + long pollfdSize = getConstant(SIZEOF_STRUCT_POLLFD); + long fdOffset = getConstant(OFFSETOF_STRUCT_POLLFD_FD); + long eventsOffset = getConstant(OFFSETOF_STRUCT_POLLFD_EVENTS); + long reventsOffset = getConstant(OFFSETOF_STRUCT_POLLFD_REVENTS); + if (count != 0) { + nativePollData = NativeMemory.malloc(pollfdSize * count); + for (int i = 0; i < count; i++) { + long pollfd = nativePollData + pollfdSize * i; + NativeMemory.writeInt(pollfd + fdOffset, fds[i]); + NativeMemory.writeShort(pollfd + eventsOffset, (short) events[i]); + NativeMemory.writeShort(pollfd + reventsOffset, (short) 0); + } + } + int result = posixNativeFunctionInvoker.call_poll(nativePollData, count, timeout); + if (result < 0) { + throw getErrnoAndThrowPosixException(); + } + for (int i = 0; i < count; i++) { + revents[i] = Short.toUnsignedInt(NativeMemory.readShort(nativePollData + pollfdSize * i + reventsOffset)); + } + } finally { + NativeMemory.free(nativePollData); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstants.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstants.java index dd33f9b75d..8dad0ccda1 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstants.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstants.java @@ -97,6 +97,17 @@ public final class PosixConstants { public static final OptionalIntConstant SEEK_HOLE; public static final MandatoryIntConstant SOMAXCONN; public static final OptionalIntConstant PIPE_BUF; + public static final OptionalIntConstant POLLIN; + public static final OptionalIntConstant POLLPRI; + public static final OptionalIntConstant POLLOUT; + public static final OptionalIntConstant POLLERR; + public static final OptionalIntConstant POLLHUP; + public static final OptionalIntConstant POLLNVAL; + public static final OptionalIntConstant POLLRDNORM; + public static final OptionalIntConstant POLLRDBAND; + public static final OptionalIntConstant POLLWRNORM; + public static final OptionalIntConstant POLLWRBAND; + public static final OptionalIntConstant POLLMSG; public static final OptionalIntConstant SEM_VALUE_MAX; public static final OptionalIntConstant RUSAGE_CHILDREN; public static final MandatoryIntConstant RUSAGE_SELF; @@ -387,6 +398,7 @@ public final class PosixConstants { public static final MandatoryIntConstant _SC_NPROCESSORS_CONF; public static final MandatoryIntConstant _SC_NPROCESSORS_ONLN; + public static final IntConstant[] pollFlags; public static final IntConstant[] openFlags; public static final IntConstant[] fileType; public static final IntConstant[] mmapFlags; @@ -436,6 +448,17 @@ public final class PosixConstants { SEEK_HOLE = reg.createOptionalInt("SEEK_HOLE"); SOMAXCONN = reg.createMandatoryInt("SOMAXCONN"); PIPE_BUF = reg.createOptionalInt("PIPE_BUF"); + POLLIN = reg.createOptionalInt("POLLIN"); + POLLPRI = reg.createOptionalInt("POLLPRI"); + POLLOUT = reg.createOptionalInt("POLLOUT"); + POLLERR = reg.createOptionalInt("POLLERR"); + POLLHUP = reg.createOptionalInt("POLLHUP"); + POLLNVAL = reg.createOptionalInt("POLLNVAL"); + POLLRDNORM = reg.createOptionalInt("POLLRDNORM"); + POLLRDBAND = reg.createOptionalInt("POLLRDBAND"); + POLLWRNORM = reg.createOptionalInt("POLLWRNORM"); + POLLWRBAND = reg.createOptionalInt("POLLWRBAND"); + POLLMSG = reg.createOptionalInt("POLLMSG"); SEM_VALUE_MAX = reg.createOptionalInt("SEM_VALUE_MAX"); RUSAGE_CHILDREN = reg.createOptionalInt("RUSAGE_CHILDREN"); RUSAGE_SELF = reg.createMandatoryInt("RUSAGE_SELF"); @@ -726,6 +749,7 @@ public final class PosixConstants { _SC_NPROCESSORS_CONF = reg.createMandatoryInt("_SC_NPROCESSORS_CONF"); _SC_NPROCESSORS_ONLN = reg.createMandatoryInt("_SC_NPROCESSORS_ONLN"); + pollFlags = new IntConstant[]{POLLIN, POLLPRI, POLLOUT, POLLERR, POLLHUP, POLLNVAL, POLLRDNORM, POLLRDBAND, POLLWRNORM, POLLWRBAND, POLLMSG}; openFlags = new IntConstant[]{O_ACCMODE, O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_EXCL, O_TRUNC, O_APPEND, O_NONBLOCK, O_NOCTTY, O_NDELAY, O_DSYNC, O_CLOEXEC, O_SYNC, O_DIRECT, O_RSYNC, O_TMPFILE, O_TEMPORARY, O_DIRECTORY, O_BINARY, O_TEXT, O_XATTR, O_LARGEFILE, O_SHLOCK, O_EXLOCK, O_EXEC, O_SEARCH, O_PATH, O_TTY_INIT, O_EVTONLY, O_FSYNC, O_SYMLINK, O_NOINHERIT, O_NOFOLLOW, O_NOFOLLOW_ANY, O_NOLINKS, O_NOATIME, O_RANDOM, O_SEQUENTIAL, O_ASYNC}; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstantsDarwin.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstantsDarwin.java index e77400131e..41b9c292c4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstantsDarwin.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstantsDarwin.java @@ -68,6 +68,16 @@ static void getConstants(PosixConstants.Registry constants) { constants.put("SEEK_HOLE", 3); constants.put("SOMAXCONN", 128); constants.put("PIPE_BUF", 512); + constants.put("POLLIN", 0x00000001); + constants.put("POLLPRI", 0x00000002); + constants.put("POLLOUT", 0x00000004); + constants.put("POLLERR", 0x00000008); + constants.put("POLLHUP", 0x00000010); + constants.put("POLLNVAL", 0x00000020); + constants.put("POLLRDNORM", 0x00000040); + constants.put("POLLRDBAND", 0x00000080); + constants.put("POLLWRNORM", 0x00000004); + constants.put("POLLWRBAND", 0x00000100); constants.put("SEM_VALUE_MAX", 32767); constants.put("RUSAGE_CHILDREN", -1); constants.put("RUSAGE_SELF", 0); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstantsLinux.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstantsLinux.java index ef346f36a6..f9a1c20834 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstantsLinux.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstantsLinux.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -40,8 +40,8 @@ */ package com.oracle.graal.python.runtime; -// Auto generated by gen_native_cfg.py at 2025-12-16 11:41:03.832352 -// on Linux arisu 6.17.11-200.fc42.x86_64 #1 SMP PREEMPT_DYNAMIC Tue Dec 9 00:25:56 UTC 2025 x86_64 +// Auto generated by gen_native_cfg.py at 2026-09-04 10:44:31.642954 +// on Linux yuyuko 7.1.12-200.fc44.x86_64 #1 SMP PREEMPT_DYNAMIC Fri Aug 28 14:00:18 UTC 2026 x86_64 class PosixConstantsLinux { private PosixConstantsLinux() { @@ -69,6 +69,17 @@ static void getConstants(PosixConstants.Registry constants) { constants.put("SEEK_HOLE", 4); constants.put("SOMAXCONN", 4096); constants.put("PIPE_BUF", 4096); + constants.put("POLLIN", 0x00000001); + constants.put("POLLPRI", 0x00000002); + constants.put("POLLOUT", 0x00000004); + constants.put("POLLERR", 0x00000008); + constants.put("POLLHUP", 0x00000010); + constants.put("POLLNVAL", 0x00000020); + constants.put("POLLRDNORM", 0x00000040); + constants.put("POLLRDBAND", 0x00000080); + constants.put("POLLWRNORM", 0x00000100); + constants.put("POLLWRBAND", 0x00000200); + constants.put("POLLMSG", 0x00000400); constants.put("SEM_VALUE_MAX", 2147483647); constants.put("RUSAGE_CHILDREN", -1); constants.put("RUSAGE_SELF", 0); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java index 552bec4bc0..99dd66312a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java @@ -116,7 +116,7 @@ public abstract class PosixSupportLibrary extends Library { public abstract SelectResult select(Object receiver, int[] readfds, int[] writefds, int[] errorfds, Timeval timeout) throws PosixException; - public abstract boolean poll(Object receiver, int fd, boolean forWriting, Timeval timeout) throws PosixException; + public abstract void poll(Object receiver, int[] fds, int[] events, int[] revents, int timeout) throws PosixException; public abstract long lseek(Object receiver, int fd, long offset, int how) throws PosixException; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java index 281004c367..1c7f61128a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java @@ -284,10 +284,10 @@ final SelectResult select(int[] readfds, int[] writefds, int[] errorfds, Timeval } @ExportMessage - final boolean poll(int fd, boolean forWriting, Timeval timeout, + final void poll(int[] fds, int[] events, int[] revents, int timeout, @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException { checkNotInPreInitialization(); - return nativeLib.poll(nativePosixSupport, fd, forWriting, timeout); + nativeLib.poll(nativePosixSupport, fds, events, revents, timeout); } @ExportMessage diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java index f2ffc12f91..4565d027cc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java @@ -125,6 +125,7 @@ import com.oracle.graal.python.builtins.objects.filter.PFilter; import com.oracle.graal.python.builtins.objects.floats.PFloat; import com.oracle.graal.python.builtins.objects.frame.PFrame; +import com.oracle.graal.python.builtins.objects.frame.PFrameLocalsProxy; import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction; import com.oracle.graal.python.builtins.objects.function.PFunction; import com.oracle.graal.python.builtins.objects.function.PKeyword; @@ -171,7 +172,6 @@ import com.oracle.graal.python.builtins.objects.list.PList; import com.oracle.graal.python.builtins.objects.map.PMap; import com.oracle.graal.python.builtins.objects.mappingproxy.PMappingproxy; -import com.oracle.graal.python.builtins.objects.frame.PFrameLocalsProxy; import com.oracle.graal.python.builtins.objects.memoryview.BufferLifecycleManager; import com.oracle.graal.python.builtins.objects.memoryview.MemoryViewIterator; import com.oracle.graal.python.builtins.objects.memoryview.PMemoryView; @@ -194,6 +194,7 @@ import com.oracle.graal.python.builtins.objects.referencetype.PReferenceType; import com.oracle.graal.python.builtins.objects.reversed.PSequenceReverseIterator; import com.oracle.graal.python.builtins.objects.reversed.PStringReverseIterator; +import com.oracle.graal.python.builtins.objects.select.PPoll; import com.oracle.graal.python.builtins.objects.set.PBaseSet; import com.oracle.graal.python.builtins.objects.set.PFrozenSet; import com.oracle.graal.python.builtins.objects.set.PSet; @@ -1477,6 +1478,10 @@ public static PSimpleQueue createSimpleQueue(Object cls, Shape shape) { return new PSimpleQueue(cls, shape); } + public static PPoll createPoll(PythonLanguage language) { + return new PPoll(PythonBuiltinClassType.PPoll, PythonBuiltinClassType.PPoll.getInstanceShape(language)); + } + public static PContextVar createContextVar(PythonLanguage language, TruffleString name, Object def) { return new PContextVar(PythonBuiltinClassType.ContextVar, PythonBuiltinClassType.ContextVar.getInstanceShape(language), name, def); } diff --git a/graalpython/python-libposix/src/posix.c b/graalpython/python-libposix/src/posix.c index d7e0b2c374..a371d83e2f 100644 --- a/graalpython/python-libposix/src/posix.c +++ b/graalpython/python-libposix/src/posix.c @@ -1317,9 +1317,12 @@ GP_EXPORT int32_t call_select(int32_t nfds, int32_t* readfds, int32_t readfdsLen return result; } -GP_EXPORT int32_t call_poll(int32_t fd, int32_t writing, int64_t timeoutSec, int64_t timeoutUsec) { - int8_t selected = 0; - return call_select(1, writing ? NULL : &fd, writing ? 0 : 1, writing ? &fd : NULL, writing ? 1 : 0, NULL, 0, timeoutSec, timeoutUsec, &selected); +GP_EXPORT int32_t call_poll(void *poll_data, int32_t count, int32_t timeout) { + (void)poll_data; + (void)count; + (void)timeout; + set_posix_errno(ENOSYS); + return -1; } GP_EXPORT int64_t call_lseek(int32_t fd, int64_t offset, int32_t whence) { @@ -2232,7 +2235,7 @@ GP_EXPORT void call_initialize(void) { } GP_EXPORT int32_t init_constants(int64_t* out, int32_t len) { - if (len != 33) + if (len != 40) return -1; out[0] = sizeof(struct sockaddr); out[1] = sizeof(((struct sockaddr*)0)->sa_family); @@ -2262,11 +2265,18 @@ GP_EXPORT int32_t init_constants(int64_t* out, int32_t len) { out[25] = sizeof(struct in6_addr); out[26] = sizeof(((struct in6_addr*)0)->s6_addr); out[27] = offsetof(struct in6_addr, s6_addr); - out[28] = sizeof(struct sockaddr_un); - out[29] = sizeof(((struct sockaddr_un*)0)->sun_family); - out[30] = offsetof(struct sockaddr_un, sun_family); - out[31] = sizeof(((struct sockaddr_un*)0)->sun_path); - out[32] = offsetof(struct sockaddr_un, sun_path); + out[28] = 0; + out[29] = 0; + out[30] = 0; + out[31] = 0; + out[32] = 0; + out[33] = 0; + out[34] = 0; + out[35] = sizeof(struct sockaddr_un); + out[36] = sizeof(((struct sockaddr_un*)0)->sun_family); + out[37] = offsetof(struct sockaddr_un, sun_family); + out[38] = sizeof(((struct sockaddr_un*)0)->sun_path); + out[39] = offsetof(struct sockaddr_un, sun_path); return 0; } @@ -2491,37 +2501,13 @@ int32_t call_select(int32_t nfds, int32_t* readfds, int32_t readfdsLen, CAPTURE_ERRNO_AND_RETURN(-1, (int32_t) result); } -int32_t call_poll(int32_t fd, int32_t writing, int64_t timeoutSec, int64_t timeoutUsec) { +int32_t call_poll(struct pollfd *poll_data, int32_t count, int32_t timeout) { #ifdef _WIN32 - // for windows, use select() as a worse fallback - int selected[2] = {0, 0}; - return call_select(1, - writing ? NULL : &fd, writing ? 0 : 1, - writing ? &fd : NULL, writing ? 1 : 0, - NULL, 0, - timeoutSec, timeoutUsec, &selected); + errno = ENOSYS; + capture_errno(); + return -1; #else - struct pollfd pollfd; - pollfd.fd = fd; - pollfd.events = writing ? POLLOUT : POLLIN; - - int timeout_ms; - if (timeoutSec < 0) { - timeout_ms = -1; - } else if (timeoutSec > INT_MAX / 1000) { - errno = EINVAL; - capture_errno(); - return -1; - } else { - int64_t timeout_ms_64 = timeoutSec * 1000 + timeoutUsec / 1000; - if (timeout_ms_64 > INT_MAX) { - errno = EINVAL; - capture_errno(); - return -1; - } - timeout_ms = (int)timeout_ms_64; - } - CAPTURE_ERRNO_AND_RETURN(-1, poll(&pollfd, 1, timeout_ms)); + CAPTURE_ERRNO_AND_RETURN(-1, poll(poll_data, (nfds_t)count, timeout)); #endif } @@ -3540,7 +3526,7 @@ int32_t get_error_source() { // start generated int32_t init_constants(int64_t* out, int32_t len) { - if (len != 33) + if (len != 40) return -1; out[0] = sizeof(struct sockaddr); out[1] = sizeof(((struct sockaddr*)0)->sa_family); @@ -3570,11 +3556,18 @@ int32_t init_constants(int64_t* out, int32_t len) { out[25] = sizeof(struct in6_addr); out[26] = sizeof(((struct in6_addr*)0)->s6_addr); out[27] = offsetof(struct in6_addr, s6_addr); - out[28] = unix_or_0(sizeof(struct sockaddr_un)); - out[29] = unix_or_0(sizeof(((struct sockaddr_un*)0)->sun_family)); - out[30] = unix_or_0(offsetof(struct sockaddr_un, sun_family)); - out[31] = unix_or_0(sizeof(((struct sockaddr_un*)0)->sun_path)); - out[32] = unix_or_0(offsetof(struct sockaddr_un, sun_path)); + out[28] = unix_or_0(sizeof(struct pollfd)); + out[29] = unix_or_0(sizeof(((struct pollfd*)0)->fd)); + out[30] = unix_or_0(offsetof(struct pollfd, fd)); + out[31] = unix_or_0(sizeof(((struct pollfd*)0)->events)); + out[32] = unix_or_0(offsetof(struct pollfd, events)); + out[33] = unix_or_0(sizeof(((struct pollfd*)0)->revents)); + out[34] = unix_or_0(offsetof(struct pollfd, revents)); + out[35] = unix_or_0(sizeof(struct sockaddr_un)); + out[36] = unix_or_0(sizeof(((struct sockaddr_un*)0)->sun_family)); + out[37] = unix_or_0(offsetof(struct sockaddr_un, sun_family)); + out[38] = unix_or_0(sizeof(((struct sockaddr_un*)0)->sun_path)); + out[39] = unix_or_0(offsetof(struct sockaddr_un, sun_path)); return 0; } // end generated diff --git a/scripts/gen_native_cfg.py b/scripts/gen_native_cfg.py index 7a7e1df262..350989c009 100644 --- a/scripts/gen_native_cfg.py +++ b/scripts/gen_native_cfg.py @@ -74,6 +74,7 @@ # include # include # include +# include # include # include # include @@ -150,6 +151,19 @@ * i PIPE_BUF +[pollFlags] +* x POLLIN +* x POLLPRI +* x POLLOUT +* x POLLERR +* x POLLHUP +* x POLLNVAL +* x POLLRDNORM +* x POLLRDBAND +* x POLLWRNORM +* x POLLWRBAND +* x POLLMSG + * i SEM_VALUE_MAX * i RUSAGE_CHILDREN @@ -516,6 +530,11 @@ [struct in6_addr] s6_addr +[struct pollfd] u + fd + events + revents + [struct sockaddr_un] u sun_family sun_path @@ -691,7 +710,7 @@ def generate_platform(): subprocess.run(f'./{c_executable_file}', shell=False, check=True, stdout=output_file, universal_newlines=True) output_file.seek(0) output = output_file.read()[:-1] - uname = " ".join(tuple(plat.uname())) + uname = " ".join(tuple(plat.uname())).rstrip() out_path = DIR / f'graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstants{platform}.java'