Open
Conversation
2e1fda1 to
ad4db36
Compare
Analyzed POE 1.370 test results (~15/97 pass). Identified 7 root causes: - P0: exists(&sub) fails in require context (blocks IO::Socket::INET) - P0: use vars globals not visible across files under strict - P1: Symbol.pm $VERSION not set in Java module - P1: POSIX missing errno/signal constants and uname() - P1: indirect method call syntax (import $pkg ()) - P2: POE constants as barewords cascade from P0 - P3: IO::Tty/IO::Pty need native PTY (JVM limitation) Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Fix ConstantFoldingVisitor corrupting AST for exists(&Errno::EINVAL) by skipping folding of & (code sigil) operands - Add PF_UNSPEC and SOMAXCONN to Socket module (needed by IO::Pipely) - Add POSIX signal constants (SIGHUP..SIGTSTP) with macOS/Linux values - Add POSIX errno constants (EPERM..ERANGE) - Add POSIX::uname(), sigprocmask() stub, SigSet/SigAction stubs - Add SIG_BLOCK/SIG_UNBLOCK/SIG_SETMASK/SIG_DFL/SIG_IGN/SIG_ERR - Set Symbol::$VERSION to 1.09 Result: use POE now loads successfully. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Add parsingIndirectObject flag to Parser to allow $var( pattern in indirect object context (e.g., import $package (), new $class (args)) - Fix ConcurrentModificationException in hash each() by snapshotting entries at iterator creation time, matching Perl tolerance for hash modification during each() iteration - These fixes unblock POE::Filter::Reference (import $package () syntax) and POE::Resource::Aliases (each + delete pattern) Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Perl's %SIG hash is pre-populated with all available OS signal names as keys (with undef values). Modules like POE rely on `keys %SIG` to discover which signals are available. Without this, POE's signal handling was completely broken because _data_sig_initialize() found no signals to register. The fix adds a constructor to RuntimeSigHash that populates the hash with POSIX signals and platform-specific signals (macOS: EMT, INFO, IOT; Linux: CLD, STKFLT, PWR, IOT). Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…aner Uses Cleaner to detect when blessed objects become unreachable and schedules DESTROY calls on the main thread at safe points. Per-blessId cache makes the check O(1) for repeated bless calls. Exceptions in DESTROY are caught and printed as "(in cleanup)" warnings matching Perl behavior. - DestroyManager: new class managing Cleaner registration, pending queue, and proxy object creation for DESTROY calls - ReferenceOperators: call registerForDestroy at bless time - PerlSignalQueue: check pending DESTROYs at safe points - PerlLanguageProvider: run global destruction after END blocks Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Perl foreach iterates over elements pushed to the array during the loop. The iterator was caching the array size at creation time, missing new elements. Now checks elements.size() dynamically on each hasNext(). This fixes POE::Kernel->stop() which uses the foreach-push pattern to walk the session tree. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…OY findings Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Fix parser to handle require File::Spec->catfile(...) as an expression rather than treating File::Spec as a module name. This allows POE to dynamically load Time::HiRes via require. - Add non-blocking I/O support for internal pipe handles: InternalPipeHandle now supports setBlocking/isBlocking, and sysread returns undef with EAGAIN (errno 11) when non-blocking and no data available. - Fix IO::Handle::blocking() to properly delegate to the underlying handle blocking state, and fix argument passing (shift on glob copy issue). - Add FileDescriptorTable for managing file descriptors and implement 4-argument select() (pselect-style) for POE I/O multiplexing. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
DestroyManager.registerForDestroy used Math.abs(blessId) as the cache key, but overloaded classes have negative blessIds (-1, -2, ...). Math.abs(-1) = 1 collided with normal class IDs, causing getBlessStr to return null and triggering a NullPointerException in normalizeVariableName. Fix: use the original blessId directly as the cache key in all three places (registerForDestroy, invalidateDestroyCache, processPendingDestroys). Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…is fragile The Cleaner-based DESTROY implementation used proxy objects to reconstruct blessed references after GC. This caused: - close() inside DESTROY corrupting proxy hash access (File::Temp bug) - Overloaded class blessId collisions (Math.abs on negative IDs) - Incomplete reconstruction for tied/magic/overloaded objects Tied variable DESTROY (TieScalar.tiedDestroy) is unaffected — it uses scope-based cleanup. Updated dev/design/object_lifecycle.md with findings and future directions (scope-based ref counting recommended over GC proxy). Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Bug 9: Updated to ATTEMPTED AND REVERTED with explanation of proxy reconstruction failures (close() corruption, blessId collision) - Bug 11: require File::Spec->catfile() parser fix - Bug 12: Non-blocking I/O for pipe handles - Bug 13: DestroyManager Math.abs(blessId) collision - Added Phase 3.2 to progress tracking - Updated Key Findings with DESTROY proxy failure analysis Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
… always ready The previous NIO-based select() implementation marked all non-socket handles (pipes, files) as unconditionally ready. This broke POE's event loop: select() returned immediately when monitoring the signal pipe, preventing the event loop from sleeping for timer timeouts. Fix: Replace the "always ready" assumption with proper polling: - For InternalPipeHandle: use hasDataAvailable() to check if data is actually available for reading - For write ends of pipes and regular files: still treat as ready - Implement a poll loop with 10ms intervals that respects timeout - Check both pollable fds and NIO selector in each iteration This fixes the regression where POE's ses_session.t hung at test 7 (before POE::Kernel->run()) because select() never blocked. Note: ses_session.t still hangs due to missing DESTROY support (postback refcounts never decrement), but this is a pre-existing limitation, not a regression from this change. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ation - Bug 14: 4-arg select() was marking pipes as always ready, causing POE event loop to busy-loop instead of blocking for timers - Updated Key Findings: DESTROY is not feasible via JVM GC (unreliable across JVM implementations, incompatible with Perl ref counting) - Added Phase 3.3 to progress tracking - Documented ses_session.t hang root cause (AnonEvent postback DESTROY) Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Bug 15: pipe() created RuntimeIO objects but never registered them in RuntimeIO.filenoToIO, making pipes invisible to select(). POE signal pipe (used for asynchronous signal delivery) was affected - select() could not detect data written to the signal pipe. Fix: Added registerExternalFd() to RuntimeIO that registers a RuntimeIO at a specific fd number (matching FileDescriptorTable fd) and advances nextFileno to prevent future collisions. Called from IOOperator.pipe() after creating the pipe handles. Bug 16: InternalPipeHandle.sysread() set errno to 11 (Linux EAGAIN) on all platforms. On macOS EAGAIN is 35, so POE check failed producing Resource deadlock avoided errors. Fixed to use ErrnoVariable.EAGAIN() for platform-correct values. Also fixed in CustomFileChannel.java. ses_session.t: 7/41 -> 37/41 (remaining 4 are expected DESTROY failures) Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…k fixes Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Three bugs fixed: 1. select() bitvector write-back: The 4-arg select() created copies of the bitvector arguments but never wrote the modified copies back to the original variables. This meant select() never actually cleared or modified the output bitvectors, causing spurious read-readiness detection in POE's event loop. 2. Fd allocation collision: FileDescriptorTable (used by pipe()) and RuntimeIO (used by socket/socketpair/accept) had separate fd counters that got out of sync. After socketpair allocated fds 5+ from RuntimeIO, FileDescriptorTable still started at 5, causing pipe() to allocate fds that collided with existing socket fds. Fixed by cross-synchronizing both counters. 3. socketpair() stream initialization: The SocketIO constructor used by socketpair() didn't initialize inputStream/outputStream, causing sysread() to fail with "No input stream available". Fixed by using the Socket-based constructor which initializes streams. Also made sysread() fall back to channel-based I/O when streams are unavailable. POE test results: - k_selects: 5/17 → 17/17 (all pass) - ses_session: 37/41 (unchanged, DESTROY-related failures) - ses_nfa: 39/39 (unchanged) - k_alarms: 37/37 (unchanged) - k_aliases: 20/20 (unchanged) Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
k_selects.t now 17/17 (was 5/17). Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Inventoried all 35 POE event loop test files (~596 tests total): - 12 fully passing (175 tests) - 5 blocked by missing Socket pack_sockaddr_un stubs - 7 blocked by missing POSIX terminal constants - 1 running but failing (wheel_readwrite 15/28) - 8 skipped (platform/network) - Several fork-dependent (JVM limitation) Phase 4 plan: 4.1: Socket pack_sockaddr_un stubs → unblock SocketFactory 4.2: POSIX constants → unblock Wheel::Run/FollowTail 4.3: Debug wheel_readwrite I/O failures 4.4: Test SocketFactory-dependent wheel tests Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…n/FollowTail Phase 4.2: Add comprehensive POSIX constants needed by POE wheels. POSIX.pm changes: - Add stat permission constants (S_IRUSR, S_IWUSR, S_IXUSR, etc.) to constant-generation loop - Add terminal I/O constants (ECHO, ICANON, OPOST, TCSANOW, BRKINT, ICRNL, ISTRIP, IXON, CSIZE, PARENB, baud rates, etc.) - Add S_IS* file type test functions (S_ISBLK, S_ISCHR, S_ISDIR, S_ISFIFO, S_ISLNK, S_ISREG, S_ISSOCK) as pure Perl - Add setsid() and sysconf() function stubs - Add _SC_OPEN_MAX constant POSIX.java changes: - Add 14 stat permission constant methods - Add 66 terminal I/O constant methods with macOS/Linux detection - Add setsid() (returns PID as approximation) - Add sysconf() (supports _SC_OPEN_MAX via ulimit -n) - Add _SC_OPEN_MAX constant (macOS=5, Linux=4) Results: - POE::Wheel::FollowTail now loads and runs (4/10 pass) - POE::Wheel::Run now loads and runs (42/103: 6 pass, 36 skip) - Socket pack_sockaddr_un/unpack_sockaddr_un stubs added Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…blockers Updated test results with precise pass/fail counts from fresh test run. Documented three remaining root causes: - Event loop I/O hang: select() callbacks don't fire for pipe/socket watchers (affects wheel_readwrite, wheel_sf_tcp, wheel_accept, wheel_sf_udp) - Missing sysseek operator (blocks FollowTail) - Missing TIOCSWINSZ ioctl constant (blocks Wheel::Run child processes) Reprioritized Phase 4.3-4.5 based on impact analysis. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Regular file open(), JAR resource open(), scalar-backed open(), and pipe open() all created IO handles without assigning a file descriptor number. This caused fileno() to return undef for these handle types, breaking POE select()-based I/O monitoring which needs fileno to register handles. Added assignFileno() calls after handle creation in all four open paths. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
sysseek was only implemented in the interpreter backend. Added JVM backend support by routing through CoreOperatorResolver, EmitBinaryOperatorNode, OperatorHandler, and CompileBinaryOperator. Unlike seek (returns 1/0), sysseek returns the new position on success, '0 but true' when position is 0, or undef on failure. This unblocks POE::Wheel::FollowTail which uses sysseek for file position tracking. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…wheel hangs Documented findings from Phase 4.3 investigation: - fileno() fix for regular file handles (Bug 21) - sysseek implementation for JVM backend (Bug 22) - Root cause analysis: all wheel test hangs are caused by DESTROY not being called when wheels go out of scope. I/O subsystem (select, sysread, fileno, sysseek) verified working correctly in isolation. - Documented POE::Wheel DESTROY cleanup pattern and 6 workaround options - Recommended Option A: trigger DESTROY on hash delete/set Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Documented 6 Windows compatibility issues found via analysis: - Critical: EAGAIN/errno resolution broken (strerror stub) - High: Missing Windows errno table, Unix-only signals in %SIG - Medium: POSIX.java and Socket.java lack Windows branches - Low: sysconf uses ulimit -n Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- FFMPosixWindows.strerror(): expand from 10 entries to full Windows CRT errno table (1-42 standard + 100-143 Winsock/MSVC extended) - ErrnoHash.java: add buildWindowsTable() with MSVC errno values - Errno.pm: add $^O eq 'MSWin32' branch with Windows CRT errno values - RuntimeSigHash.java: add Windows-only signal list (INT, TERM, ABRT, FPE, ILL, SEGV, BREAK) instead of full POSIX signal set - POSIX.java: add IS_WINDOWS flag, fix EAGAIN/ETXTBSY errno constants for Windows, fix sysconf to not run ulimit -n on Windows - Socket.java: make SOL_SOCKET, AF_INET6, SO_REUSEADDR, SO_KEEPALIVE, SO_BROADCAST, SO_LINGER, SO_ERROR, IP_TOS, IPV6_V6ONLY, SO_REUSEPORT platform-aware with correct Windows Winsock2 values Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- InternalPipeHandle: Implement non-blocking syswrite with buffer capacity checking (returns EAGAIN when 64KB pipe buffer is full) - InternalPipeHandle: Add createPair() factory method with shared writerClosed flag for proper EOF detection in non-blocking reads - InternalPipeHandle: Increase pipe buffer from 1KB to 64KB (matching OS pipes) - InternalPipeHandle: Simplify blocking doRead/sysread using direct read() instead of polling loop, with proper Pipe broken/closed EOF handling - ErrnoVariable: Add EBADF constant with strerror probe support - IOOperator: Set $! to numeric EBADF (not just string) for sysread/syswrite errors on closed or wrong-direction handles - IOOperator: Set $! after warn() calls to prevent warn from clobbering errno when STDERR is closed - Add sys/ioctl.ph stub for POE::Wheel::Run compatibility POE test results: 01_sysrw.t now passes 15/17 (was 4/17), signals.t 46/46 Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
duplicateFileHandle() currently shares the same IOHandle object between original and duplicate, so closing one invalidates the other. This blocks 01_sysrw.t tests 16-17 and the STDERR save/restore pattern used by many test suites. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Add DupIOHandle wrapper that enables proper Perl dup semantics: - Each dup'd handle has independent closed state and fd number - Shared reference count tracks all duplicates - Underlying resource only closed when last dup is closed - Original handle preserves its fileno after duplication Also fix findFileHandleByDescriptor to check RuntimeIO's fileno registry, fixing "Bad file descriptor" errors when opening by fd number (e.g. open($fh, ">&6")). Fixes POE::Driver::SysRW tests 16-17 (dup/close/reopen cycle). Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Mark Phase 4.8 (DupIOHandle) as complete with results - Update test tables: 15_kernel_internal 12/12, 01_sysrw 17/17, filehandles 131/132, signals 46/46, ses_nfa 39/39 - Clean up Remaining Phases: consolidate into clear next steps with implementation details for each phase - Add Phase 4.9 (Storable) and 4.10 (HTTP::Message) plans - Summary: 38/53 unit+resource pass, 14/35 event loop pass Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…-based handle lookup Three fixes and comprehensive documentation: 1. op/array.t hang (176->0->176 FIXED): map/grep/all/any in ListOperators.java were missing control flow checks after RuntimeCode.apply(). When goto LABEL was used inside a map block, the RuntimeControlFlowList marker was silently discarded, causing an infinite loop (unshift grew the array each iteration). Added isNonLocalGoto() checks that propagate the marker to the caller. 2. io/dup.t regression (25->20->23 IMPROVED): Parsimonious dup (>&= / <&=) was returning the same RuntimeIO object, so close F on a parsimonious dup of STDOUT closed STDOUT itself. Created BorrowedIOHandle -- a non-owning wrapper that delegates all I/O but only flushes on close (never closes the delegate), matching Perl fdopen() semantics. 3. Named handle lookup via glob table: openFileHandleDup() now resolves named handles (STDIN/STDOUT/STDERR and user-defined) via GlobalVariable getGlobalIO() instead of a switch on static RuntimeIO fields. This is essential because standard handles can be redirected at runtime via open(STDOUT, ">file"). The glob table always reflects the current handle. 4. Thorough documentation added to all IO dup-related code including DupIOHandle, BorrowedIOHandle, FileDescriptorTable, and IOOperator methods. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
861fe96 to
082ceee
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
POE 1.370 (Perl Object Environment) event-driven multitasking framework support for PerlOnJava.
Current status: 38/53 unit+resource tests pass, 14/35 event loop tests pass. Core event loop works (alarms, aliases, sessions, NFA, signals, I/O multiplexing).
Bugs fixed (22 root causes)
exists(&Errno::EINVAL)— constant folding bypass for&sigil$VERSIONnot set in Java moduleimport $package ()parser fixuname(),sigprocmask()each()iteration%SIGnot pre-populated with signal namesforeachnot seeing array modifications during iterationrequire File::Spec->catfile(...)parsed as module nameselect()— pipe readiness polling, bitvector write-backselect()socketpair()stream initializationpack_sockaddr_un/unpack_sockaddr_unstubsfileno()returning undef for regular file handlessysseekoperator for JVM backendsyswritefor pipe handles, EBADF errnoDupIOHandle) — properopen(FH, ">&OTHER")semanticsfindFileHandleByDescriptor()fallback to RuntimeIO fileno registryKey test results
Remaining work (priority order)
See
dev/modules/poe.mdfor full plan with implementation details.Test plan
exists(&sub)in require contexteach()ConcurrentModificationException%SIGwith signal namesforeacharray modification visibilityselect()with proper I/O multiplexingsysseekoperatorfileno()for regular filesGenerated with Devin