Status of a future in motoko and sleeping

Quick question for @claudio, @ggreif , @luc-blaeser, and the motoko team. The status of the Bob subnet has caused a situation where it would be very helpful to know the status of a future in motoko.

One can do the following to ensure you eventually wait and process everything in a parallelization of an external actor you have called, but this leads to ‘chunky’ behavior where fulfilled futures stack up behind one obnoxious future perhaps on a canister that has no compute allocation.

   for(f in futures.vals()){
      await f.1;
    };

The following would be much better:

   private func fake_a_round() : async () {
     //do something silly like look up randomness or some other thing that would be better if we had a simple await ExperamentalIC.sleep() function that waited to return the next round.
   };

   //later in a function
   let futures = SetThing<async ()>();
   
   for(mydata in principals.vals()){
     let anActor = actor(mydata);
     futures.add(anActor.doAthing();
   };

   while(futures.size() >0){
     for (f in futures.vals()){
      
      if(f1.ready()){
        let result = await f.1;
        //handle response
        futures.remove(f);
      };
      await fake_a_round();
     };
   };

Any chance of having the status of a motoko future exposed? And maybe a sleep option? This is a bit different than a timer for 0ns because you wouldn’t have to specify some top-level function to come back in on.

I understand that there has been a philosophical approach to abstract away may of the underlying ways the IC works from the language, but reality keeps pushing into the ideals.

3 Likes

@claudio, @ggreif , @luc-blaeser

I wanted to put this back up to the top with perhaps a different angle on the question.

If we don’t have a functional equivalent to ‘fake a round’ or ‘await but make sure don’t execute until the next round’ in the replica/motoko, what would be the best functional way to do this? Call the system random function? I thought about querying in an random ICP balance, but I think that would actually cause me to wait more than the next round and be a bit expensive on cycles(especially with the new economics being proposed).

What is the best way to make sure your code waits one, but only one round(that may be impossible if other things come while processing, but let’s say 'await until your immediate next chance;)

I can of course set a timer for 0 seconds and that will execute the next round most of the time, but it inturrpts the ‘look’ of the program as I have to reference a another function.

1 Like

Your first post didn’t have the requirement that it is has to be the very next round. So for that I would say call a ping canister on a different subnet (if it is cheaper than calling the random beacon).

But anyway you still need fut.ready() which we don’t have.

If you are not shy of making a bunch of extra calls, which you aren’t if you looking into active waiting loops, then maybe can you rewrite your code such that you wrap your outgoing calls into individual self-calls so that they can be awaited individually? I mean instead of

futures.add(anActor.doAthing());

you do

ignore async {
  await anActor.doAthing();
  // move some code here
};

It might be possible depending on what your continuation code does.

Yes…we have a strategy for this where the calls go into a map structure with a id and a return function as one of the parameters. We await the loop to get below a threshold…say 400 because the max out queue is 500. This ‘sleep’ I’m looking for is specifically to sleep a bit so that incoming calls can be processed and get things removed from the map so that we can open up and add some more calls.

Your suggestion was the first attempt, but I couldn’t get past the roadblock of getting stuck waiting for all 400 to finish before I could start up some more. This slows you down if you run into a slow subnet and you don’t get one of the 400 back for 30 minutes.

Letting the future get called by the replica without a specific replica and handling a return call seemed to help, but I still need a way to pause a round…waiting for two isn’t the end of the world…actually now that I think about it, having more control would be nice so that we could do things like awaitAfterIngress or awaitAfterTimer so that we are sure that ingress and/or a timer has had a chance to run before we resume…but perhaps this is baking too many accidents of implementation into the language.

1 Like