Comment puis-je propager et intercepter les erreurs lancées dans un autre thread dans Raku?

9

Quelle est la meilleure façon de propager les erreurs à partir d'un thread séparé (par exemple, le bloc de démarrage, Proc :: Async ou un sous-conteneur les contenant). Le simple encapsulage du code qui dérive un nouveau thread dans un bloc try / CATCH ne fonctionne pas, et l'utilisation de l'attente ne fonctionne que selon la valeur de retour de la sous-routine (c'est-à-dire qu'un sous-retour ne fonctionnera pas avec l'approche de l'attente).

ryn1x
la source
Peut foo- être et barpeut être éliminé ici?
jjmerelo
1
J'ai toujours des problèmes avec ce scénario ... n'est-il tout simplement pas possible à Raku et nécessite une restructuration des classes réelles? Ce ne serait pas idéal parce que je ne veux pas de gestion d'erreurs spécifique à l'application dans les classes qui peuvent être réutilisées ailleurs ...
ryn1x
@ ryn1x Je vous suggère d'envisager de restaurer cette question dans sa forme d'origine. Ajoutez ensuite une note au début expliquant que, bien que certaines de nos réponses aient résolu l'énoncé du problème donné dans le corps de votre question, vous cherchiez en fait quelque chose de plus général. De plus, bien que la réponse que vous avez acceptée soit plus générale, vous avez conclu depuis qu'elle n'était toujours pas suffisamment générale. De plus, que vous avez essayé une prime, couplée à demander plus de généralité, mais cela n'a pas aidé. Ensuite, écrivez une nouvelle question, reliant à celle-ci, avec un exemple qui, selon vous, illustre le problème.
raiph
La réponse actuelle me suffit complètement. J'ai changé la question parce qu'elle devenait trop longue et trop spécifique pour quiconque se retrouverait ici.
ryn1x

Réponses:

6

Utilisez await.

Par exemple, remplacez ces trois lignes dans votre code:

foo;
bar;
baz;

avec:

await foo, bar, baz;
raiph
la source
Cela fonctionne, mais ne correspond pas à mon problème réel car foo, bar et baz sont en fait des méthodes qui renvoient soi-même. J'ai mis à jour la question et l'exemple.
ryn1x
5

Théoriquement, ce code devrait mourir :

À partir de la version 6.d du langage, le préfixe d'instruction de début utilisé dans le contexte de récepteur attachera automatiquement un gestionnaire d'exceptions. Si une exception se produit dans le code donné, elle sera imprimée et le programme se fermera alors, comme si elle était levée sans aucun préfixe d'instruction de démarrage impliqué.

use v6.c;
start { die }; sleep ⅓; say "hello"; # OUTPUT: «hello␤» 

use v6.d;
start { die }; sleep ⅓; say "hello";
# OUTPUT: 
# Unhandled exception in code scheduled on thread 4 
# Died 
#     in block  at -e line 1 

Dans ce cas, c'est une situation étrange parce que vous ne perdez pas la promesse (vous la rendez), mais finalement vous la perdez parce que vous l'exécutez dans un contexte vide.

La même documentation vous donne la solution: ne coulez pas le contexte:

# Don't sink it: 
my $ = start { die }; sleep ⅓; say "hello"; # OUTPUT: «hello␤» 

# Catch yourself: 
start { die; CATCH { default { say "caught" } } };
sleep ⅓;
say "hello";

Puisque votre programme ne meurt pas, je dirais que vous êtes dans la deuxième situation. Pour une raison quelconque, ce n'est pas coulé. Mais quelle que soit la situation, la solution est la même: vous devez intercepter l'exception dans le même bloc de code.

Solution: awaitla promesse (qui ne la coulera pas) ou l'affecter à une variable, de sorte que le code environnant meurt aussi. Mais en répondant à votre OP, non, vous ne pouvez pas intercepter une exception d'un autre thread, de la même manière que vous ne pouvez pas intercepter une exception d'un autre bloc.

jjmerelo
la source
Merci pour tout ça. J'ai en fait besoin d'être plus précis que dans mon OP. Je n'appelle pas dans le contexte de puits et la solution d'attente ne fonctionne pas non plus car les fonctions de l'OP sont en fait des méthodes qui renvoient self. J'ai mis à jour la question et l'exemple.
ryn1x
4

En suivant la convention utilisée dans Go pour passer les erreurs des routines go en utilisant des canaux, j'ai trouvé la même approche pour travailler dans Raku. On peut utiliser un canal pour envoyer des erreurs hors du code asynchrone à gérer par le thread principal.

Exemple:

my $errors = Channel.new;

my $err-supply = $errors.Supply;
$err-supply.tap(-> $e {say "handle error: $e"});

start {
    die "something went horribly wrong";

    CATCH {
        default {
            $errors.send($_);
        }
    }
}

sleep 1;
ryn1x
la source