Why am I not allowed to break a Promise?

250 views Asked by At

The following simple Promise is vowed and I am not allowed to break it.

my $my_promise = start {
    loop {}   # or sleep x;
    
    'promise response'
}

say 'status : ', $my_promise.status;      # status : Planned

$my_promise.break('promise broke');       # Access denied to keep/break this Promise; already vowed
                                          # in block <unit> at xxx line xxx

Why is that?

2

There are 2 answers

0
Elizabeth Mattijsen On BEST ANSWER

Because the Promise is vowed, you cannot change it: only something that actually has the vow, can break the Promise. That is the intent of the vow functionality.

What are you trying to achieve by breaking the promise as you showed? Is it to stop the work being done inside of the start block? Breaking the Promise would not do that. And the vow mechanism was explicitly added to prevent you from thinking it can somehow stop the work inside a start block.

If you want work inside a start block to be interruptible, you will need to add some kind of semaphore that is regularly checked, for instance:

my int $running = 1;
my $my_promise = start {
    while $running {
        # do stuff
    }
    $running
}

# do other stuff
$running = 0;
await $my_promise;

Hope this made sense.

0
Pawel Pabian bbkr On

The reason why you cannot directly keep/break Promise from outside or stop it on Thread Pool are explained here in Jonathans comment.

Common misuse of Promises comes from timeout pattern.

await Promise.anyof(
    start { sleep 4; say "finished"; },
    Promise.in( 1 )
);
say "moving on...";
sleep;

This will print "finished". And when user realize that the next logical step for him is to try to kill obsolete Promise. While the only correct way to solve it is to make Promise aware that its work is no longer needed. For example through periodically checking some shared variable.

Things gets complicated if you have blocking code on Promise (for example database query) that runs for too long and you want to terminate it from main thread. That is not doable on Promises. All you can do is to ensure Promise will run in finite time (for example on MySQL by setting MAX_EXECUTION_TIME before running query). And then you have choice:

  • You can grind your teeth and patiently wait for Promise to finish. For example if you really must disconnect database in main thread.
  • Or you can move on immediately and allow "abandoned" Promise to finish on its own, without ever receiving its result. In this case you should control how many of those Promises can stack up in background by using Semaphore or running them on dedicated ThreadPoolScheduler.