Async-Event-Interval

 view release on metacpan or  search on metacpan

lib/Async/Event/Interval.pm  view on Meta::CPAN

    }

    # Phase 2: Kill children (bounded by poll loops, no alarm needed).

    for my $pid (@pids) {
        kill 'TERM', $pid;
    }
    for my $pid (@pids) {
        for (1..10) {
            last unless kill(0, $pid);
            select(undef, undef, undef, 0.05);
        }

        kill 'KILL', $pid if kill(0, $pid);
    }

    # Phase 3: Clear %events (1 second timeout).

    _alarmed_eval(1, sub {
        _events_write(sub {
            delete @events{keys %events};
        });
    });

    # Phase 4: Release IPC segments (1 second timeout).
    #
    # IPC::Shareable's STORE on a nested hashref/arrayref creates child
    # segments and calls _remove_child() on the previous value. When a forked
    # child does that during its callback, the parent's global_register still
    # lists the now-dead child segments. clean_up_protected then re-removes
    # them and shmctl() returns EINVAL. The cleanup itself is correct (seg
    # count returns to baseline); only the warning is spurious, so filter it.

    _alarmed_eval(1, sub {
        local $SIG{__WARN__} = sub {
            my $msg = shift;

            return if $msg =~ /Couldn't remove (?:shm segment|semaphore set) \d+: Invalid argument/;
            return if $msg =~ /Use of uninitialized value \$sem_remove_status/;
            return if $msg =~ /Use of uninitialized value \$id in hash element/;

            warn $msg;
        };
        IPC::Shareable::clean_up_protected(_shm_lock());
    });
}
sub DESTROY {
    my $self = $_[0];

    return if $$ != $creator_pid;
    return if $_shutting_down;

    if (defined $self) {
        $self->stop if $self->pid;
    }

    # On events with interval of zero, ForkManager runs finish(), which calls
    # our destroy method. We only want to blow away the %events hash if we truly
    # go out of scope

    return if (caller())[0] eq 'Parallel::ForkManager::Child';

    # Release any shared_scalar segments owned by this event. These are  tracked
    # in $self->{_shared_scalars}, not inside %events, so they can be cleaned up
    # outside the %events lock.

    if ($self->{_shared_scalars}) {
        for my $scalar (@{ $self->{_shared_scalars} }) {
            next unless ref $scalar eq 'SCALAR';
            my $knot = tied $$scalar;
            eval { $knot->remove } if $knot;
        }
    }

    my $ok = eval {
        _events_write(sub {
            delete $events{$self->id};
            $events{_event_count}--;
        });
        1;
    };

    if (! $ok) {
        if (my $knot = tied(%events)) {
            $knot->{_lock} = 0;
        }
    }
}
END {
    _end(1);
}

sub _vim{} # vim navigation marker; intentionally empty

1;

__END__

=head1 NAME

Async::Event::Interval - Scheduled and one-off restartable asynchronous events

=for html
<a href="https://github.com/stevieb9/async-event-interval/actions"><img src="https://github.com/stevieb9/async-event-interval/workflows/CI/badge.svg"/></a>
<a href='https://coveralls.io/github/stevieb9/async-event-interval?branch=master'><img src='https://coveralls.io/repos/stevieb9/async-event-interval/badge.svg?branch=master&service=github' alt='Coverage Status' /></a>


=head1 SYNOPSIS

Here's an example of a simple asynchronous event that fetches JSON data from a
website every two seconds using a shared scalar variable to hold the decoded
JSON hashref, while allowing the main application to continue running in the
foreground. Multiple events can be used simultaneously if desired.

See the L</SCENARIOS/EXAMPLES> section for further usage examples.

    use warnings;
    use strict;

    use Async::Event::Interval;
    use JSON;



( run in 0.746 second using v1.01-cache-2.11-cpan-817d5f8af8b )