Revision history for Async-Event-Interval

1.14    2026-06-01
    - Add $count++ in callback exec params example in POD
    - s/contains any/doesn't contain/ in Changes entry 1.12
    - Remove all reliance on CI_TESTING env var in unit tests. They all run on
      all systems by default
    - Bump IPC::Shareable prereq to 1.14
    - Add t/lib/TestHelper.pm; automatically does the seg/sem tests in all test
      files that "use" it to ensure there are no leaks
    - Test updates to conform with updates to IPC::Shareable
    - Make shared_scalar() a property of the event that created it. Clarify its
      cleanup in POD
    - Fixes #16; _rand_shm_lock() now returns 1 + ($$ % 32767) so the value
      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}
      remains at file scope (children outlive _event(), need auto-reap)
    - Add write-path tests and automated source audit to t/09-locking.t
    - Extract _run_callback() from _event() to deduplicate the eval +
      error-capture + store pattern and isolate $@ preservation
    - Add _end() clean_up_protected coverage test; fix _end() mock-counter
      test that was silently destroying the semaphore set, causing 11
      subsequent tests to be skipped; fix events() deep-copy still treating
      shared_scalars as a hashref instead of the new arrayref
    - Fix fork failure silently falling through to child path and running
      the callback in the parent process; now croaks with "fork() failed"
    - Wrap _run_callback in eval inside _event() and always call
      _pm->finish($@ ? 1 : 0) so ForkManager isn't left with a stale
      child record when the callback dies
    - Guard _end() with a local SIGALRM (END_LOCK_TIMEOUT, default 2s)
      so process exit doesn't block forever on _events_read' LOCK_SH if
      a peer still holds LOCK_EX on the events knot
    - stop() now polls kill(0) at STOP_KILL_POLL_INTERVAL (0.05s) for up
      to STOP_KILL_TIMEOUT (1s) instead of always sleeping a fixed 1s;
      returns as soon as the target process is gone (full test suite
      wallclock roughly halved)
    - Refactor error()/status() to remove their mutual-recursion
      side-effect chain. Crash detection now lives in a private
      _detect_crash helper that both methods call independently
    - Retire the undocumented -99 PID sentinel that marked crashes
    - stop() now sends SIGTERM first (STOP_TERM_TIMEOUT 0.5s) and escalates
      to SIGKILL only if the child is still alive; _signal_and_wait() helper
      encapsulates the signal-and-poll logic
    - Replace mutable module-level $is_child_process flag with
      $$ != $creator_pid check in DESTROY; a forked child inherently has
      a different PID than the process that loaded the module, so no
      mutable state that mock-fork tests can corrupt is needed
    - Replace lexical $id counter with shared _id_counter in %events to
      prevent duplicate IDs across forked processes
    - Closes #15; Add _stop_requested cooperative flag in shared %events so the
      child's interval loop can break cleanly and call finish() instead of being
      killed by SIGTERM; stop() sets it, start() clears it, the loop checks it
      on each iteration
    - Closes #14; Add timeout() accessor so a callback that exceeds the
      specified number of whole seconds self-terminates with an error.
      Accepts a non-negative integer or undef; fractional seconds are
      not supported
    - Closes #9; Add immediate() method. Set a flag to have the event fire
      immediately on start rather than waiting for the first interval to be
      reached
    - Adopt IPC::Shareable's testing_set/clean_up_testing API to brand all
      test-suite segments and purge orphans from prior crashed runs; bump
      IPC::Shareable prereq to 1.17
    - Fixes and enhancements to the local VM test infrastructure
    - Pin Parallel::ForkManager to < 2.00 on Perl < 5.14 to avoid Moo XS
      compile failures on macOS
    - Guard DESTROY's _events_write in eval and clear stale _lock on
      failure to prevent EAGAIN from IPC::Shareable's unlock() leaking
      IPC segments on Linux
    - Ensure counters are properly cleaned up in _end() cleanup
    - Use Time::HiRes wall-clock time in _signal_and_wait so stop() timeout
      is accurate on busy/VM systems where select() jitter accumulates
    - Relax t/15-interval.t exact-count timing checks to >= so slow macOS VMs
      don't fail before the first callback fires
    - _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)
    - Add prereq of Test::SharedFork to keep tests in order in t/46
    - Bumped prereq version of IPC::Shareable to 1.13

1.12    2022-03-31
    - Only remove the Async::Event::Interval protected shared %events hash
      if it doesn't contain keys (ie. event objects). When running in things
      like 'plackup', END{} was being called on each browser session close,
      but there were still objects, so we were trashing the %events
      infrastructure prematurely
    - Each unit test file now does pre-and-post segment counts and displays
      them if PRINT_SEGS env var is true, and the suite itself has a before
      and after count comparison to ensure the suite leaked no segments or
      semaphores
    - Fix issue where we were trying to set a _pid() on an undefined value,
      causing IPC::Shareable to complain that "Can't STORE on undef val"

1.11    2022-03-09
    - Modified tests to run only on valid CI platforms

1.10    2022-03-07
    - Add tests to ensure that all shared memory segments created during the
      unit test suite run get cleaned up ok
    - Bump prereq of IPC::Shareable to 1.11 due to bug fix where child shared
      memory segments created under one process weren't being registered if the
      parent was created in a different process

1.09    2022-03-05
    - Bump prereq IPC::Shareable to 1.08 due to a fix in creating random SHM
      keys in forked environments, major improvements of its _shm_key_rand()
      function, and its ability to set a 'protected' option so our global shared
      %events hash doesn't get cleaned up automatically
    - Modified examples/shared.pl so it doesn't leak segments and semaphores
      after completion
    - We now remove %events hash in END instead of DESTROY. This way we can have
      objects go out of scope in a script without blowing away the global shared
      data
    - Added _end() so that we can test the END block in unit tests. END{} calls
      _end() upon program termination

1.08    2022-03-03
    - Added pid(), getter-only wrapper for _pid()
    - Added example of using shared data with IPC::Shareable

1.07    2022-03-03
    - interval() no longer requires stop/start of event to take effect
    - Re-arranged initialization routine to allow for the above
    - Interval is now stored in the %events shared hash so that the event can
      access it directly

1.06    2022-03-03
    - Class level hash that contains information about all existing events
      now lives in shared memory so we can track operation informtion from
      within the events themselves
    - Added runs() method, tracks the number of callback executions of an
      event
    - Added errors() method, tracks the number of times a callback fails
    - Added error_message() method, stores the most recent error message
      from the callback if an error has been logged
    - Modified _shm_rand_key() to return a 12-char ALPHA string
    - Updated tests to ensure they destroy any shared memory segments they
      create

1.05    2022-03-01
    - We now wait interval time before executing the event for the first time
    - Added error(), returns error status instead of using status() == -1
    - status() no longer sets -1. We now use error() for that

1.04    2021-05-11
    - Fix issue in t/45-params.t where done_testing() may have been called
      multiple times (fixes #5)
    - Fix issue where _rand_shm_key() could generate non-unique shared memory
      segment keys (fixes #4)

1.03    2021-05-05
    - Added events() class function, returns hash reference details of each
      event that's been created
    - Added id() object method; returns the ID of the event
    - Added info() object method; returns an href of details about the event
    - Added shared_scalar() object method; Returns a reference to a scalar
      that resides in shared memory. We use IPC::Shareable for the backend
    - Fixed issue where fractional seconds weren't being honoured
    - POD updates, including a new SYNOPSIS
    - Added accessor methods for most object attributes

1.02    2021-04-15
    - Updated shared data example to be a scalar
    - Update t/10-shared.t as it was failing under MacOS/FreeBSD

1.01    2021-04-14
    - If interval is set to zero, we'll run only once
    - Added IPC::Shareable as prerequisite
    - Added waiting(), checks to see if an event is ready to start/restart

1.00    2018-01-23
    - modified t/15-interval.t with some timing changes to prevent CPAN
      Testers failures
    - added t/45-params.t to cover cases where we send in parameters to the
      event
    - added an EXAMPLE to display how to send in params to the event
      callback

0.05    2017-10-20
    - POD fix (indenting examples)
    - bump copyright to 2017

0.04    2017-10-19
    - added examples/shared.pl, shared variable between all events and
      the parent
    - major overhaul on PID handling which provided the ability to rework
      status() to be much more reliable (and effective)
    - we now set $SIG{CHLD}="IGNORE" to avoid defunct processes
    - added Carp (croak) for all fatal errors
    - status() now returns -1 if an event has crashed, providing the user
      with the ability to restart the event, or take other action
    - added examples/timeout.pl and examples/event_crash.pl

0.03    2016-10-16
    - removed restart(), and aliased it instead to start()
    - fixed issue in an already-running warn on start(), and added status()
      to check the running status of an event (closes #1)

0.02    2016-09-24
    - POD fixes

0.01    Date/time
    - First version, released on an unsuspecting world.

