Data-HierTimingWheel-Shared

 view release on metacpan or  search on metacpan

xt/fork_hw.t  view on Meta::CPAN

use strict;
use warnings;
use Test::More;
use POSIX qw(_exit);
plan skip_all => 'author test' unless $ENV{AUTHOR_TESTING};
use Data::HierTimingWheel::Shared;

# An anonymous MAP_SHARED hierarchical wheel inherited across fork: children each
# schedule a disjoint block of timers at delays spanning several levels,
# concurrently (contending on the free-list and bucket lists under the rwlock).
# The parent then advances past every delay and must collect exactly one fire per
# scheduled timer -- no lost schedules, no double fires, no corrupted lists or
# cascades under contention.
my $kids = 4;
my $per  = 5_000;
my $maxd = 4000;                      # spans levels 0..1 for S=64
my $cap  = $kids * $per + 16;
my $tw = Data::HierTimingWheel::Shared->new(undef, 64, 3, $cap);   # max delay 64**3 - 1

my @pids;
for my $c (0 .. $kids - 1) {
    my $pid = fork // die "fork: $!";
    if (!$pid) {
        my $seed = 1 + $c;
        for my $i (1 .. $per) {
            $seed = ($seed * 1103515245 + 12345) & 0x7fffffff;
            $tw->add(1 + $seed % $maxd, $c * $per + $i);   # delay 1..4000, unique payload
        }
        _exit(0);
    }
    push @pids, $pid;
}
waitpid $_, 0 for @pids;

is $tw->count, $kids * $per, 'every child scheduled its timers (no lost schedules)';

my %fired;
my $dupes = 0;
for my $t (1 .. $maxd) {
    for my $p ($tw->advance(1)) { $dupes++ if $fired{$p}++; }
}
is scalar(keys %fired), $kids * $per, 'every scheduled timer fired exactly once (cascades intact under contention)';
is $dupes, 0, 'no timer fired twice';
is $tw->count, 0, 'no timers left pending after advancing past every delay';

done_testing;



( run in 1.482 second using v1.01-cache-2.11-cpan-e7c6538aa59 )