=head1 NAME

WiringPi::API::WORKERS - Concurrency & background-worker examples

=head1 DESCRIPTION

Worked, runnable examples for running background work concurrently with your main
program using L<WiringPi::API>. The C<worker()> helper described here is
implemented in C<WiringPi::API> 3.18 and verified on Pi 5 hardware; the snippets
run as written.

C<worker()> is B<fork-based by default and needs no> C<use threads> B<and no
threaded Perl> - an ithread mechanism is a documented opt-in only. For reacting
to GPIO I<edges> in the background, see C<docs/interrupt-examples.md> /
L<WiringPi::API::INTERRUPTS> (C<background_interrupt>).

See the L<WiringPi::API> POD ("CONCURRENCY / BACKGROUND WORKERS") for the
authoritative per-function reference.

=head1 ABOUT THESE EXAMPLES

This document covers running work B<concurrently> with the main program - distinct
from reacting to interrupts.

=over 4

=item *

B<Prefer> C<worker()>. It hides the spawn mechanism, the loop B<and> the
lifecycle: your body carries no C<fork>, no C<use threads>, no C<detach>, no
C<while (1)> and no cleanup. It is the general-purpose sibling of
C<background_interrupt>.

=item *

B<No threads required.> C<worker()> forks by default and works on B<any> Perl,
threaded or not. The ithread mechanism (scenario 6) is an opt-in for users who
specifically want shared-memory ergonomics on a threaded Perl.

=item *

B<Pin numbering> follows whichever setup you call: C<setup()> = wiringPi
numbering, C<setup_gpio()> = BCM. Examples use C<setup()>.

=item *

B<Mode constants> for C<pin_mode>: C<INPUT>=0, C<OUTPUT>=1 (shown as integers).

=item *

B<The setup-once-in-main contract:> call C<setup()>/C<pin_mode> B<once, in the
parent, before> starting a worker. A fork worker inherits the configuration; you
drive pins from inside the body.

=item *

Sections 7-9 (L</UNDER THE HOOD>) show the raw C<fork> / C<threads-E<gt>create> /
C<Async::Event::Interval> plumbing that C<worker()> packages up - read them to
understand what happens beneath the helper, but most programs only need
C<worker()>.

=back

=head1 DECISION GUIDE

None of these need C<use threads> except scenario 6. To hide the most plumbing,
use C<worker()> (scenarios 1-5).

B<fork vs thread in one line:> the default C<fork> worker is crash-isolated and
works on any Perl but B<can't touch main's variables> (hand data back with
C<results>/C<shared>); a C<< mechanism => 'thread' >> worker shares memory
directly but needs a threaded Perl and C<pi_lock> discipline. No shared-memory
need? Use the default fork.

=over 4

=item *

Run a background task and forget it (its own GPIO) - scenario 1 (C<worker>).

=item *

Sample periodically; main reads the latest value - scenario 2 (C<worker> +
C<interval>/C<shared>).

=item *

Stream every value the worker produces back to main - scenario 3 (C<worker> +
C<results>).

=item *

Do one background job once, then exit - scenario 4 (C<worker> + C<once>).

=item *

Several independent workers, each on its own pin - scenario 5 (C<worker>).

=item *

Share memory directly between main and the worker - scenario 6 (C<worker> +
C<< mechanism => 'thread' >>).

=item *

React to a pin edge in the background - C<background_interrupt> (see
L<WiringPi::API::INTERRUPTS>).

=item *

Understand/hand-roll the raw mechanism - scenarios 7, 8, 9.

=back

=head1 BACKGROUND WORKERS (worker)

=head2 1. Heartbeat LED - a worker on its own pin

B<Why/when:> Run a self-contained background task on its own GPIO while main does
its own work; the simplest possible case.

B<Real-world:> A status heartbeat LED blinking on its own cadence while the main
program does its real work.

B<Main & background:> The helper owns the loop and the lifecycle. You write only
the body; C<worker()> repeats it until you C<stop>.

    use strict;
    use warnings;
    use WiringPi::API qw(setup pin_mode write_pin worker);

    setup();
    pin_mode(2, 1);                   # OUTPUT, once in main

    my $w = worker(sub {
        write_pin(2, 1); sleep 1;
        write_pin(2, 0); sleep 1;
    });

    # ... main does its own work ...

    $w->stop;                         # idempotent; END reaps if you forget

No C<use threads>, no C<fork>, no C<detach>, no C<while (1)>, no C<waitpid>.

=head2 2. Periodic sampler handing data back (interval + shared)

B<Why/when:> Timer-driven sampling where main only ever wants the B<latest>
reading, not every sample.

B<Real-world:> Polling a sensor every second into a value the main app (a web
handler or display loop) reads on demand.

B<Main & background:> C<< { interval => $secs } >> paces the loop (the body needs
no C<sleep>); C<< { shared => 1 } >> publishes the body's return value as a lossy
latest value the parent reads with C<< $w->value >>.

    use strict;
    use warnings;
    use WiringPi::API qw(setup pin_mode analog_read worker);

    setup();
    pin_mode(0, 0);                   # INPUT, once in main

    my $w = worker(sub { analog_read(0) }, { interval => 1, shared => 1 });

    while (1) {
        my $latest = $w->value;       # most recent sample, or undef until the first
        print "latest: ", (defined $latest ? $latest : 'n/a'), "\n";
        sleep 5;
    }

    $w->stop;

The channel is B<lossy> - the worker never blocks on a slow reader, so C<value()>
gives you the most recent sample and discards the ones you didn't read.

=head2 3. Streaming every result (results)

B<Why/when:> When you need B<every> value the worker produces, in order, not just
the latest.

B<Real-world:> A logger that records each reading, or a counter feeding an
event-loop via C<select>.

B<Main & background:> C<< { results => 1 } >> length-frames every defined return
value back over a pipe. Drain it with C<< $w->read >> (non-blocking), or select on
C<< $w->fh >>.

    use strict;
    use warnings;
    use WiringPi::API qw(setup pin_mode analog_read worker);

    setup();
    pin_mode(0, 0);                   # INPUT, once in main

    my $w = worker(sub { analog_read(0) }, { interval => 0.5, results => 1 });

    for (1 .. 20) {
        while (defined(my $v = $w->read)) {   # drain everything pending
            print "sample: $v\n";
        }
        sleep 1;
    }

    $w->stop;

This is identical to C<background_interrupt>'s C<< { results => 1 } >> channel.

=head2 4. A one-shot background task (once)

B<Why/when:> A single background job - run it off the main path and let it exit on
its own.

B<Real-world:> Firing a one-shot solenoid pulse, or taking a single sensor
reading, without blocking main.

B<Main & background:> C<< { once => 1 } >> runs the body exactly once; the child
then exits and C<< $w->running >> becomes false. You can still C<stop> (idempotent)
or just let the END reaper clean up.

    use strict;
    use warnings;
    use WiringPi::API qw(setup pin_mode write_pin worker);

    setup();
    pin_mode(5, 1);                   # OUTPUT, once in main

    my $w = worker(sub {
        write_pin(5, 1);
        select(undef, undef, undef, 0.2);     # 200ms pulse
        write_pin(5, 0);
    }, { once => 1 });

    # ... main carries on; the pulse fires in the background ...

    $w->stop if $w->running;          # usually already finished

=head2 5. Several workers on distinct pins

B<Why/when:> Multiple independent background tasks at once, each owning its own
pin.

B<Real-world:> A multi-channel relay board where each channel toggles on its own
cadence while main runs the control logic.

B<Main & background:> Configure every pin B<once in main>, then start one worker
per pin. Each runs independently; each returns its own handle.

    use strict;
    use warnings;
    use WiringPi::API qw(setup pin_mode write_pin worker);

    setup();
    pin_mode($_, 1) for (2, 3, 4);    # OUTPUT - all config in main, up front

    my @workers = (
        worker(sub { write_pin(2, 1); sleep 1; write_pin(2, 0); sleep 1 }),
        worker(sub { write_pin(3, 1); sleep 2; write_pin(3, 0); sleep 2 }),
        worker(sub { write_pin(4, 1); sleep 3; write_pin(4, 0); sleep 3 }),
    );

    # ... main's own work ...

    $_->stop for @workers;

Workers must drive B<distinct> pins - see L</THE SETUP-ONCE-IN-MAIN CONTRACT>.

=head2 6. Shared memory - the opt-in ithread mechanism

B<Why/when:> You specifically want to share memory directly between main and the
worker (no IPC), and you have a threaded Perl.

B<Real-world:> A counter or state machine the worker mutates and main reads in the
same address space.

B<Main & background:> C<< { mechanism => 'thread' } >> runs the body in an ithread
instead of a fork. It B<requires> C<use threads> (croaks otherwise) and rejects
the C<results>/C<shared> pipe channels - share a C<:shared> variable and serialize
it with C<pi_lock>/C<pi_unlock> (keys 0-3) instead. C<stop> sets the stop flag and
joins the thread.

    use strict;
    use warnings;
    use threads;                      # required for mechanism => 'thread'
    use threads::shared;
    use WiringPi::API qw(setup worker pi_lock pi_unlock);

    setup();

    my $count :shared = 0;

    my $w = worker(sub {
        pi_lock(0);
        $count++;
        pi_unlock(0);
        select(undef, undef, undef, 0.1);
    }, { mechanism => 'thread' });

    while (1) {
        pi_lock(0);
        my $n = $count;
        pi_unlock(0);
        print "count: $n\n";
        sleep 1;
    }

    $w->stop;                         # sets the stop flag and joins

Check for a threaded Perl with C<perl -V:useithreads> (Raspberry Pi OS ships one).

=head1 REACTING TO INTERRUPTS IN THE BACKGROUND

C<worker()> is for I<running> background work, not for reacting to GPIO B<edges>.
To handle an edge in the background - fire a callback even while main is blocked -
use B<C<background_interrupt>>, the interrupt-side sibling of C<worker()>. It
forks a child that arms the interrupt and runs your callback on each edge, and
returns a handle with the same C<stop>/C<pid>/C<running> shape:

    use WiringPi::API qw(setup pin_mode background_interrupt INT_EDGE_RISING);

    setup();
    pin_mode(0, 0);                   # INPUT

    my $h = background_interrupt(0, INT_EDGE_RISING, sub {
        my ($edge, $ts_us) = @_;
        # runs in the background on each rising edge
    });

    # ... main does its own work; the handler fires on its own ...

    $h->stop;

The full interrupt story is in L<WiringPi::API::INTERRUPTS> /
C<docs/interrupt-examples.md>.

=head1 THE SETUP-ONCE-IN-MAIN CONTRACT

The rule that keeps concurrent GPIO safe: do all configuration B<once, in main,
before> starting any worker; afterwards each context does only steady-state I/O on
B<distinct> pins.

=over 4

=item *

Call C<setup()>/C<setup_gpio()>, every C<pin_mode>, and any device C<*Setup>
B<once, in the parent, before> the first C<worker()>.

=item *

A fork worker inherits that configuration; a thread worker shares it.

=item *

Afterwards, workers may freely C<read_pin>/C<write_pin> on B<distinct>
pins. B<Never> call C<setup()>/C<pin_mode>/device C<*Setup> concurrently - they
read-modify-write shared registers.

=item *

For shared Perl data under C<< mechanism => 'thread' >>, guard every access with
C<pi_lock>/C<pi_unlock> (or C<threads::shared>'s C<lock>).

=back

=head1 UNDER THE HOOD

These are the raw mechanisms C<worker()> packages up. You rarely need them
directly; they are here to show what the helper does and to cover cases it
doesn't.

=head2 7. Manual fork

B<Why/when:> The fork worker (scenario 1) without the helper - full control over
the child, at the cost of writing the loop, the signal handling and the reaping
yourself.

B<Main & background:> The child is a separate process: truly concurrent and
crash-isolated, but it B<cannot> touch main's variables - pass data back via a
pipe, and reap it yourself.

    use strict;
    use warnings;
    use WiringPi::API qw(setup pin_mode write_pin);

    setup();
    pin_mode(2, 1);                          # OUTPUT - before fork

    my $pid = fork // die "fork: $!";

    if ($pid == 0) {
        while (1) {                          # child: heartbeat forever
            write_pin(2, 1); sleep 1;
            write_pin(2, 0); sleep 1;
        }
        exit 0;
    }

    # ... parent's own work ...

    kill 'TERM', $pid;                       # on shutdown
    waitpid $pid, 0;

C<worker(sub {...})> is exactly this - the fork, the loop, the C<TERM> handler and
the C<waitpid> - done for you, with an idempotent C<stop> and an END-block reaper.

=head2 8. Raw ithreads (threads->create)

B<Why/when:> The thread worker (scenario 6) without the helper - when you want to
manage the thread object yourself.

B<Main & background:> The body runs in its B<own> interpreter; it can't see main's
lexicals - share only via C<:shared> variables guarded by C<pi_lock>/C<lock>.

    use strict;
    use warnings;
    use threads;
    use threads::shared;
    use WiringPi::API qw(setup pin_mode read_pin pi_lock pi_unlock);

    setup();
    pin_mode(3, 0);                          # INPUT

    my $latest :shared = 0;

    my $thr = threads->create(sub {
        while (1) {
            my $v = read_pin(3);
            pi_lock(0); $latest = $v; pi_unlock(0);
            select(undef, undef, undef, 0.05);
        }
    });

    # ... main reads $latest under pi_lock(0) ...

    # To stop a hand-rolled thread you need your own shared flag + join;
    # worker({mechanism=>'thread'}) provides exactly that.
    $thr->detach;

C<worker(sub {...}, { mechanism => 'thread' })> wraps this with a shared stop flag
and a clean C<stop>/join, so you don't hand-roll the lifecycle.

=head2 9. Periodic work with Async::Event::Interval

B<Why/when:> A fork-based CPAN module for I<periodic> tasks with crash
detection/restart and a shared scalar for the latest value. C<worker()> with
C<< { interval, shared } >> (scenario 2) covers most of this without a dependency;
reach for C<Async::Event::Interval> when you specifically want its
restart-on-crash.

B<Main & background:> The callback runs in a forked child every interval; main
reads the latest value via the module's C<shared_scalar> (lossy).

    use strict;
    use warnings;
    use Async::Event::Interval;
    use WiringPi::API qw(setup pin_mode read_pin);

    setup();
    pin_mode(3, 0);                              # INPUT - before the event forks

    my $event  = Async::Event::Interval->new(1, \&sample);   # forks; runs every 1s
    my $latest = $event->shared_scalar;
    $event->start;

    while (1) {
        print "latest: ", (defined $$latest ? $$latest : 'n/a'), "\n";
        $event->restart if $event->error;        # auto-recover a crashed sampler
        sleep 2;
    }

    sub sample {
        $$latest = read_pin(3);
    }

B<Honest fit:> this is a I<timer> with B<latest-value (lossy)> shared state -
ideal for periodic sampling, wrong for B<edge interrupts> (don't drop edges - keep
those on the self-pipe, L<WiringPi::API::INTERRUPTS>). Note
C<Async::Event::Interval> sets C<< $SIG{CHLD} = 'IGNORE' >> and uses SysV shared
memory at load time, so it does not compose with a hand-rolled C<fork>/C<waitpid>
or with C<worker()>.

=head1 ANTI-PATTERNS TO AVOID

=over 4

=item *

B<Reaching for> C<use threads> B<first.> C<worker()> is fork-based and needs no
threaded Perl. Only use C<< mechanism => 'thread' >> when you specifically want
shared memory.

=item *

B<Putting a loop inside the body.> C<worker()> owns the loop - the body is one
pass. Use C<< { interval => $secs } >> for pacing and C<< { once => 1 } >> for a
single pass; a C<while (1)> inside the body defeats C<stop>/C<once>/C<interval>.

=item *

B<Concurrent> C<pin_mode> / C<setup> / device C<*Setup>. Read-modify-write on
shared registers; do them once, in main, before starting any worker. Only
C<read_pin>/C<write_pin> on distinct pins are safe concurrently.

=item *

B<Expecting a fork worker to see main's variables.> Separate memory - hand data
back with C<< { results => 1 } >> / C<< { shared => 1 } >>, not a shared Perl
variable.

=item *

B<Touching> C<:shared> B<data without a lock (thread mode).> Guard every access
with C<pi_lock>/C<pi_unlock> (or C<lock>). A bare C<$shared++> from two threads
races.

=item *

B<Combining> C<< mechanism => 'thread' >> B<with> C<results>/C<shared>. Those are
fork pipe channels and are rejected under thread mode - share a C<:shared>
variable with C<pi_lock> instead.

=item *

B<Mixing a hand-rolled> C<fork>/C<waitpid> B<with> C<Async::Event::Interval>. It
sets C<< $SIG{CHLD} = 'IGNORE' >> at load, which auto-reaps children - your own
C<waitpid> then fails. Pick one process-management model.

=back

=head1 API REFERENCE FOR THESE EXAMPLES

=over 4

=item C<setup()> / C<setup_gpio()>

Init (wiringPi / BCM numbering); once, in main. Returns int status (C<0> = ok).

=item C<pin_mode($pin, $mode)>

C<0>=INPUT, C<1>=OUTPUT.

=item C<write_pin($pin, $val)> / C<read_pin($pin)>

Pin I/O; C<read_pin> returns the pin level (C<0>/C<1>).

=item C<worker(\&body [, \%opts])>

Run C<\&body> in the background. C<\%opts>: C<{once, interval, results, shared,
mechanism}>. Returns a handle C<$w> with C<stop> / C<pid> / C<running> / C<read> /
C<fh> / C<value>.

=item C<pi_lock($key)> / C<pi_unlock($key)>

Mutex (keys 0-3) for shared state under thread mode.

=item C<background_interrupt(...)>

Background B<edge> handler (see L<WiringPi::API::INTERRUPTS>). Returns a handle
C<$h>.

=back

=head1 SEE ALSO

L<WiringPi::API>, L<WiringPi::API::INTERRUPTS>

=head1 AUTHOR

Steve Bertrand, E<lt>steveb@cpan.orgE<gt>

=cut
