NAME
Acme::Parataxis::Compat - Transparent Gevent-Style Cooperative Unblocking of Builtins
SYNOPSIS
use v5.40;
use Acme::Parataxis qw[async fiber];
# Must be installed at compile time, in a BEGIN block, above affected code:
BEGIN { Acme::Parataxis->enable_transparent_unblocking() }
async {
my $sock = IO::Socket::INET->new( PeerAddr => 'example.com:80' ) or die $!;
# Standard blocking builtins are now cooperative inside fibers:
my $napper = fiber {
sleep 0.05; # yields to the scheduler; does not block the OS thread
say 'f1: finished sleep';
};
my $reader = fiber {
my $buf = q{};
read( $sock, $buf, 64 ); # frames onto await_read; yields while waiting
say "f2: read ${\length($buf)} bytes";
};
$napper->await;
$reader->await;
};
DESCRIPTION
Acme::Parataxis::Compat provides an opt-in, gevent-style compatibility layer for legacy synchronous code. By installing CORE::GLOBAL overrides for sleep, read, and sysread, existing modules and synchronous loops run cooperatively inside fibers without freezing the entire process:
sleepbecomes await_sleepThe calling fiber yields for the requested duration while other fibers proceed. Fractional seconds are honored with millisecond precision (
sleep 0.05sleeps 50ms, whereasCORE::sleeptruncates this to 0). When called outside a fiber, it falls back immediately toCORE::sleep. The$_default argument is supported.readandsysreadbecome non-blocking reads framed on await_readThe fiber parks until the filehandle is reported readable, flips the handle non-blocking, reads available bytes, and restores the original handle mode. It can return fewer bytes than requested, exactly like a non-blocking socket read; callers needing an exact byte count should loop.
Everything else remains untouched
select,alarm,connect, and C-level extensions likeDBIare not intercepted and remain blocking. Use await_read, await_write, or Acme::Parataxis::Blocking for those.
Nothing is modified unless you explicitly invoke enable_transparent_unblocking(), and only code compiled after the call is affected. Perl resolves CORE::GLOBAL overrides at parse time, so installation must take place within a BEGIN block placed above the target code. Outside of a running fiber, all three overrides delegate directly to the native CORE:: builtins.
METHODS
Acme::Parataxis->enable_transparent_unblocking( )
BEGIN { Acme::Parataxis->enable_transparent_unblocking() }
# or:
BEGIN { Acme::Parataxis::Compat->install() }
Installs the CORE::GLOBAL overrides and returns 1. Idempotent.
Acme::Parataxis->disable_transparent_unblocking( )
Acme::Parataxis->disable_transparent_unblocking();
# or:
Acme::Parataxis::Compat->disable();
Restores the global symbols to their pre-installation state. Safely clears internal subroutine depth tracking so Perl's subroutine finalization does not trigger "Can't undef active subroutine" errors. Only code compiled after this call reverts to the builtins.
Acme::Parataxis->transparent_unblocking( )
say 'Active' if Acme::Parataxis->transparent_unblocking();
# or:
say 'Active' if Acme::Parataxis::Compat->installed();
Returns true if the transparent unblocking overrides are currently installed.
Recipes
Enabling unblocking for a single file or script
package My::Worker;
use v5.40;
use Acme::Parataxis qw[async fiber];
BEGIN { Acme::Parataxis->enable_transparent_unblocking() }
use Net::FTP; # compiled AFTER the overrides; its internal reads and sleeps cooperate!
async {
my $ftp = fiber { Net::FTP->new('ftp.example.com', Timeout => 10) };
$ftp->await;
};
Reading an exact number of bytes safely
Because non-blocking reads return as soon as data arrives, wrap reads in a loop when a fixed payload size is required:
sub read_exact ( $fh, $len ) {
my $buf = q{};
my $got = 0;
while ( $got < $len ) {
my $n = read( $fh, $buf, $len - $got, $got );
last if !defined($n) || $n == 0; # error or EOF
$got += $n;
}
return $buf;
}
The OFFSET parameter places each chunk at the correct offset in the buffer without extra string copies.
Scoped testing and cleanup
use Test2::V1;
use Acme::Parataxis;
subtest 'sleep restores truncation outside overrides' => sub {
Acme::Parataxis->disable_transparent_unblocking();
my $rc = eval 'sleep(0.05)'; # eval forces compilation after disable()
is $rc, 0, 'raw CORE::sleep truncated fractional seconds';
Acme::Parataxis->enable_transparent_unblocking();
};
Behavior Notes
Short reads are normal
Perl's builtin
read()blocks the entire OS thread until the requested buffer is completely filled.Compat::readcannot do this without freezing sibling fibers. Instead, it waits for readiness and returns whatever bytes have arrived. Always loop if your protocol requires a fixed frame length.Waiting remains fully cancellable
Waiting for a handle to become readable parks the fiber in the scheduler. Deadlines from with_timeout, cancellations from Acme::Parataxis::CancellationToken, and nursery abort signals will interrupt the wait cleanly.
Original handle modes are restored
The override switches the filehandle to non-blocking mode (via
IO::Handle::blocking($fh, 0)) only for the duration of the read, and restores the original mode before returning. Code compiled before installation that shares the handle continues to observe standard blocking semantics.Bareword filehandles stop compiling
Because
CORE::GLOBALoverrides cannot reproduce the compiler's specialized grammar rules for bareword filehandles, code compiled underuse strictafter installation will fail to compile bareword handles:BEGIN { Acme::Parataxis->enable_transparent_unblocking() } read( FILEHANDLE, $buf, 128 ); # syntax error under strict subsAlways use lexical filehandles (
my $fh) or indirect handles (\*STDIN) in code compiled with unblocking enabled.Windows non-socket fallback
Windows
select()only operates on network sockets; attempting to wait on a pipe or disk filehandle returns an immediate error. WhenCompatdetects a descriptor on Windows that completes its probe in less than 90% of the wait budget, it recognizes the handle as unwatchable and performs an immediate direct read. If a pipe has no data buffered, this direct read will block the host thread until data arrives.
SEE ALSO
Acme::Parataxis, Acme::Parataxis::CancellationToken, IO::Handle
AUTHOR
Sanko Robinson https://github.com/sanko
LICENSE
Copyright (C) Sanko Robinson.
This library is free software; you can redistribute it and/or modify it under the terms found in the Artistic License 2.