Acme-Parataxis

 view release on metacpan or  search on metacpan

lib/Acme/Parataxis.pm  view on Meta::CPAN

        my $fileno = fileno($fh);
        die 'Not a valid filehandle' unless defined $fileno;
        my $handle = $^O eq 'MSWin32' ? win32_get_osfhandle($fileno) : $fileno;
        _submit_job( 3, $handle, $timeout );
        return yield('WAITING');
    }

    sub maybe_yield {
        my $invocant = shift;
        if ( !defined $invocant ||
            ( ( ref $invocant || $invocant ) ne __PACKAGE__ && !( builtin::blessed($invocant) && $invocant->isa(__PACKAGE__) ) ) ) {
            unshift @_, $invocant if defined $invocant;
        }
        my $result = Acme::Parataxis::_maybe_yield();
        return unless defined $result;
        return wantarray ? @$result : $result->[-1];
    }
    sub tid            { get_os_thread_id_export() }
    sub current_fid    { get_current_parataxis_id() }
    sub root           { state $root //= Acme::Parataxis::Root->new() }
    sub max_threads () { Acme::Parataxis::get_max_thread_pool_size() }

    # Scheduler internals
    sub _scheduler_enqueue_by_id ($fid) {
        return if $SCHEDULER_QUEUED{$fid};
        if ( my $fiber = Acme::Parataxis->by_id($fid) ) {
            _enqueue($fiber);
        }
    }

    sub poll_io {
        my @ready;
        while (1) {
            my $job_idx = check_for_completion();
            last if $job_idx == -1;
            my $fid = get_job_coro_id($job_idx);
            my $res = get_job_result($job_idx);
            push @ready, [ $fid, $res ];
            free_job_slot($job_idx);
        }
        return @ready;
    }

    sub _handle_run ( $fiber, $status ) {
        if ( $status == 1 ) {
            Acme::Parataxis::_mark_done($fiber);
            my $err = $fiber->[F_ERROR];
            die $err if defined $err;
            return 1;
        }
        if ( $status == 0 ) {
            $fiber->[F_PRIORITY] //= 0;
            _enqueue($fiber);
        }
        return $status;
    }

    sub run ($code) {
        if ($IS_RUNNING) {

            # Nested run/async inside a shared global scheduler. Queue a fresh fiber for the block and park the current
            # fiber until it completes.
            my $fiber = __PACKAGE__->new( code => $code );
            _enqueue($fiber);
            return $fiber->await;
        }
        @SCHEDULER_QUEUE  = ();
        %SCHEDULER_QUEUED = ();
        $IS_RUNNING       = 1;
        my $main_fiber = __PACKAGE__->new( code => $code );
        _enqueue($main_fiber);
        while ($IS_RUNNING) {
            my @ready;
            if ( get_outstanding_jobs() ) {
                my $out = [];
                drain_jobs($out);
                @ready = @$out;
            }
            for my $ready (@ready) {
                my ( $fid, $res ) = @$ready;
                my $fiber = __PACKAGE__->by_id($fid);
                next unless $fiber;
                my $yield_val = $fiber->call($res);
                if ( defined $fiber && !$fiber->is_done ) {
                    _enqueue($fiber) unless defined $yield_val && $yield_val eq 'WAITING';
                }
            }
            if (@SCHEDULER_QUEUE) {
                my @work = @SCHEDULER_QUEUE;
                @SCHEDULER_QUEUE  = ();
                %SCHEDULER_QUEUED = ();
                for my $current (@work) {
                    next unless $current;
                    _handle_run( $current, run_fiber_checked( $current->fid, undef ) );
                }
            }
            my $active_count = get_live_fiber_count();
            if ( $IS_RUNNING && !@SCHEDULER_QUEUE && !@ready ) {
                if ( get_outstanding_jobs() ) {
                    usleep(1000);    # Wait for background jobs to finish
                }
                else {
                    die 'FATAL: deadlock detected...' if $active_count > 0;
                    $IS_RUNNING = 0                   if defined $main_fiber && $main_fiber->is_done;
                }
            }
        }
        return $main_fiber->[F_RESULT];
    }
    sub stop () { $IS_RUNNING = 0 }

    sub new ( $class, %args ) {
        my $self = bless [ $args{code}, 0, undef, undef, undef, 0, [], undef, undef, 0 ], $class;
        my $fid  = Acme::Parataxis::create_fiber( $args{code}, $self );
        croak 'could not allocate a fiber: the fiber table is full (destroy some fibers first)' if $fid < 0;
        $self->[F_FID] = $fid;
        return $self;
    }
    sub fid   ($self) { $self->[F_FID] }
    sub code  ($self) { $self->[F_CODE] }
    sub error ($self) { $self->[F_ERROR] }



( run in 1.030 second using v1.01-cache-2.11-cpan-4716db65a44 )