Async-Event-Interval

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

      fits in IPC::Shareable 1.14+'s SEM_PROTECTED semaphore slot
    - shared_scalar() segments are now tied with protected => _shm_lock() to
      close the IPC::Shareable->clean_up_all foot-gun that could wipe them
      out from under a running event; the owning event's DESTROY still
      removes them via IPC::Shareable->remove
    - %events bootstrap loop is now capped at SHM_CREATE_RETRIES (100)
      attempts and croaks with the last underlying error instead of
      spinning forever when shmget fails persistently
    - All reads/writes to %events now go through _events_read (LOCK_SH) /
      _events_write (LOCK_EX) to synchronize access across processes
    - events() now returns a read-locked deep copy snapshot; mutations to
      the returned hashref do not affect the live %events
    - info() now returns a shallow copy snapshot, consistent with events()
    - _rand_shm_key() now generates hex strings within the 32-bit SHM key
      range (0x0–0x7FFFFFFF), replacing the 12 random letters which also
      removes the srand()-in-a-loop pattern
    - shared_scalar() no longer stores tied refs inside %events;
      %events now holds an arrayref of hex key strings instead,
      eliminating a same-process FETCH deadlock in IPC::Shareable
    - $SIG{__WARN__} moved to local inside _event() so it no longer
      silently replaces the caller's handler at module load; $SIG{CHLD}

Changes  view on Meta::CPAN

    - _detect_crash no longer marks cleanly-exited one-shot events as
      crashed; child writes _clean_exit flag to shared %events before
      finish(0) so the parent can distinguish normal completion from a crash
    - Fix flaky t/90 on macOS CI by replacing nested-hash shared memory
      writes with flat keys, eliminating child-segment race between forks
    - Fix shared memory leak on SIGINT/SIGTERM: install signal handlers
      that run _end() cleanup before re-raising; _end() now unconditionally
      stops children and removes protected segments instead of skipping
      when _event_count > 0
    - Fix _end() SIGALRM deadlock: clear SA_RESTART via POSIX::sigaction so
      the alarm actually interrupts blocked semop(); split the single 2s alarm
      into per-phase evals so each cleanup step runs even when an earlier one
      times out; add @all_pids fallback so children are killed even when the
      %events read-lock is stuck
    - Add 'error' and 'waiting' fields to events() and info() snapshots
    - Add wait() method that polls until the event is dormant (optional
      poll interval, default 0.01s)

1.13    2024-03-04
    - Added ability to send in per-callback call parameters via the start()
      method (closes #10)

t/69-wait.t  view on Meta::CPAN

{
    my $e = $mod->new(0, sub { select(undef, undef, undef, 0.1) });
    $e->start;

    my $start = Time::HiRes::time();
    $e->wait;
    my $elapsed = Time::HiRes::time() - $start;

    ok $e->waiting, "wait(): event is dormant after wait() returns";
    cmp_ok $elapsed, '>=', 0.05,
        "wait(): blocked for approximately the callback duration (elapsed=$elapsed)";
}

# 2. wait() returns immediately when the event is already dormant.
{
    my $e = $mod->new(0, sub {});

    my $start = Time::HiRes::time();
    $e->wait;
    my $elapsed = Time::HiRes::time() - $start;

t/92-end_phased_cleanup.t  view on Meta::CPAN


use File::Temp;
use IPC::Shareable;
use POSIX ();
use Test::More;

my $segs_before = IPC::Shareable::seg_count();
my $sems_before = IPC::Shareable::sem_count();

# ---------------------------------------------------------------------------
# Test 1: _alarmed_eval actually interrupts a blocked call (SA_RESTART cleared)
# ---------------------------------------------------------------------------
# Fork a child that calls _alarmed_eval with a 1s timeout around a 5s
# sleep.  The child writes the elapsed wall-clock time to a temp file.
# If SA_RESTART were still set, the alarm would be swallowed and the
# sleep would complete (~5s).  With the fix, it should exit in ~1s.
{
    my $time_file = File::Temp::tmpnam();

    my $pid = fork;
    die "fork: $!" unless defined $pid;

t/92-end_phased_cleanup.t  view on Meta::CPAN

    waitpid $pid, 0;

    ok -e $time_file, "_alarmed_eval: child wrote timing file";

    SKIP: {
        skip "_alarmed_eval: no timing file", 1 unless -e $time_file;
        open my $fh, '<', $time_file;
        chomp(my $elapsed = <$fh>);
        close $fh;
        cmp_ok $elapsed, '<', 3,
            "_alarmed_eval interrupted blocked call in ${elapsed}s (< 3s)";
    }

    unlink $time_file if -e $time_file;
}

# ---------------------------------------------------------------------------
# Test 2: @all_pids fallback — children are killed even when %events
#          read would fail
# ---------------------------------------------------------------------------
# We can't easily deadlock the real %events lock in a test, but we can



( run in 0.883 second using v1.01-cache-2.11-cpan-bbe5e583499 )