NAME

Acme::Parataxis::Sync - Base Class for Fiber Synchronization Primitives

SYNOPSIS

use v5.40;
use Acme::Parataxis qw[async fiber];
use Acme::Parataxis::Sync::Mutex;
use Acme::Parataxis::Sync::WaitGroup;

my $lock  = Acme::Parataxis::Sync::Mutex->new;
my $group = Acme::Parataxis::Sync::WaitGroup->new;

async {
    $group->add(4);

    for my $id ( 1 .. 4 ) {
        fiber {
            my $guard = $lock->guard;    # mutex held for scope of $guard
            perform_exclusive_operation($id);
            $group->done;
        };
    }

    $group->wait;    # parks until all 4 workers call done()
    say 'All workers completed safely!';
};

DESCRIPTION

Acme::Parataxis::Sync is the abstract base class powering the Parataxis synchronization primitive family:

All primitives share uniform operational guarantees:

  • Scheduled cooperative parking: Waiting fibers suspend cleanly in the scheduler; neither the OS thread nor the event loop is ever blocked.

  • Context validation: Methods that manage locks or waits resolve the calling fiber's ID immediately via _fid(), croaking with a descriptive error if called outside a scheduled fiber.

  • Safe wait unwinding: Every wait is interruptible via with_timeout and cancellation tokens. When an interrupted wait unwinds, the primitive dequeues the fiber so stale wakeups never target reused fiber IDs.

SUBCLASS CONTRACT

The base class provides internal helpers for implementing custom synchronization objects:

_fid( $croak_message )

Returns the numeric fiber ID of the currently executing fiber. If called from outside a scheduled fiber, croaks with $croak_message.

_park( $reason, [$dereg_coderef] )

Parks the calling fiber in the scheduler with diagnostic label $reason. If the park is interrupted by a timeout or cancellation, $dereg_coderef is executed immediately to clean up wait queues.

_wake( $fiber_id )

Re-enqueues the fiber with ID $fiber_id onto the scheduler run queue. If the fiber has already been destroyed or reaped, the wake is safely dropped.

How Contended Waits Work

Contended acquisitions in Parataxis follow a strict four-stage cycle:

  1. Resolve the fiber ID: Verify the calling fiber exists via _fid().

  2. Evaluate immediate acquisition: Uncontended locks, matching phases, or positive counts resolve immediately without touching scheduler queues.

  3. Queue and park: The fiber ID and an unregister callback are added to the primitive's FIFO queue, and the fiber parks. If interrupted, the callback cleans the queue.

  4. Atomic hand-off: When a releasing fiber calls unlock() or done(), ownership is assigned to the next waiter before the waiter is enqueued. This prevents third-party fibers from stealing the resource before the woken waiter runs.

Behavior Notes

  • Locks outlive fibers

    If a fiber acquires a Mutex or RwLock and terminates without calling unlock(), the lock remains held permanently. Always use scoped guards (guard, read_guard, write_guard) to guarantee release upon scope exit or exception unwinding.

  • Guards cannot cross fibers

    Guards verify that the releasing fiber matches the acquiring fiber upon destruction. Passing a guard object to another fiber will cause the guard's destructor to croak and strand the lock.

  • Off-fiber arithmetic

    WaitGroup-add> and WaitGroup-done> perform plain counter arithmetic and are safe to call outside a scheduled fiber. All methods that actually park (such as wait, lock, and arrive_and_wait) require an active fiber.

SEE ALSO

Acme::Parataxis, Acme::Parataxis::Sync::Mutex, Acme::Parataxis::Sync::RwLock, Acme::Parataxis::Sync::WaitGroup, Acme::Parataxis::Sync::Barrier, Acme::Parataxis::Sync::Once

AUTHOR

Sanko Robinson https://github.com/sanko

LICENSE

Copyright (C) Sanko Robinson.

This library is free software; you can redistribute it and/or modify it under the terms found in the Artistic License 2.