Mojo-AsyncAwait

 view release on metacpan or  search on metacpan

lib/Mojo/AsyncAwait/Backend/Coro.pm  view on Meta::CPAN

use Mojo::Promise;
use Sub::Util ();

use Exporter 'import';

our @EXPORT = (qw/async await/);

my $main = Coro::State->new;
$main->{desc} = 'Mojo::AsyncAwait::Backend::Coro/$main';

# LIFO stack of coroutines waiting to come back to
# always has $main as the bottom of the stack
my @stack = ($main);

# Coroutines that are ostensible done but need someone to kill them
my @clean;

# _push adds a coroutine to the stack and enters it
# when control returns to the original pusher, it will clean up
# any coroutines that are waiting to be cleaned up

sub _push {
  push @stack, @_;
  $stack[-2]->transfer($stack[-1]);
  $_->cancel for @clean;
  @clean = ();
}

# _pop pops the current coroutine off the stack. If given a callback, it calls
# a callback on it, otherwise, schedules it for cleanup. It then transfers to
# the next one on the stack. Note that it can't pop-and-return (which would
# make more sense) because any action on it must happen before control is
# transfered away from it

sub _pop (;&) {
  Carp::croak "Cannot leave the main thread"
    if $stack[-1] == $main;
  my ($cb) = @_;
  my $current = pop @stack;

lib/Mojo/AsyncAwait/Backend/Coro.pm  view on Meta::CPAN

    $bodyname .= "($name)";
  }
  my $desc = "declared at $caller[1] line $caller[2]";

  Sub::Util::set_subname($bodyname => $body)
    if Sub::Util::subname($body) =~ /::__ANON__$/;

  my $wrapped = sub {
    my @caller  = caller;
    my $promise = Mojo::Promise->new;
    my $coro    = Coro::State->new(sub {
      eval {
        BEGIN { $^H{'Mojo::AsyncAwait::Backend::Coro/async'} = 1 }
        $promise->resolve($body->(@_)); 1
      } or $promise->reject($@);
      _pop;
    }, @_);
    $coro->{desc} = "$subname called at $caller[1] line $caller[2], $desc";
    _push $coro;
    return $promise;
  };

  if ($opts->{-install}) {
    Mojo::Util::monkey_patch $caller[0], $opts->{-name} => $wrapped;
    return;
  }

  Sub::Util::set_subname $subname => $wrapped;
  return $wrapped;

t/promise_actions.t  view on Meta::CPAN

use Test::Mojo;

if (eval{ Mojolicious->VERSION(8.28); 1}) {
  plan skip_all => 'Mojolicious 8.28+ handles PromiseActions in core';
}

# specifically use development mode for the exception page
app->mode('development');

# manually install the guts of Mojolicious::Plugin::PromiseActions
# this tests that exception handling at the main coro can be accomplished from the hook
hook around_action => sub {
  my ($next, $c) = @_;
  my $res = $next->();
  if (blessed($res) && $res->can('then')) {
    my $tx = $c->render_later;
    $res->then(undef, sub { $c->reply->exception('XXX:' . pop) and undef $tx })->wait;
  }
  return $res;
};



( run in 0.350 second using v1.01-cache-2.11-cpan-3cd7ad12f66 )