Menu

MicroAgent: Correct use of IntermediateFuture

Help
Hannes
2016-09-21
2016-09-26
  • Hannes

    Hannes - 2016-09-21

    Hi Jadex Team,

    I need a Future Chain, where a process can finish while also returning a future as a handle for another process. I can't find a good way to explain it shorter, so I'll have to explain quite much: (not-quite-code)

    There are three Agents: M(anager), A1 & A2

    @Agent class A1{
     IFuture<X> do1() {
      return a2.do2();
     }
    
     IFuture<Y> do4() {
      return IFuture.DONE;
     }
    }
    
    @Agent class A2{
     IFuture<X> do2() {
      IFuture<Y> do3Fut = scheduleStep(this::do3);
      return new Future(do3Fut);
     }
    
     IFuture<Y> do3() {
      return a1.do4();
     }
    }
    

    Now M calls a1.do1().get().get(). I tested this in a minimum example and it seems to work, but I'd need a third level of Futures at a different point, and I might need to use only one level (so kinda a simple IFuture) at yet another. I'd really like to have an abstraction that can encapsulate all of these, so I could write that as return type for all functions do1..do4. The line >return new Future(d3Fut);< would then be replaced by something like IntermediateFuture.addIntermediateFuture() or similar.
    I used IntermediateFuture here because it sounded like the solution that I seek, but it doesn't seem to have any .add(IFuture) or .add(IItnermediateFuture) functions.

    Do you have an abstraction, that I can use, that solves this problem?

    Thanks for your time
    Hannes

     

    Last edit: Hannes 2016-09-21
  • Julian

    Julian - 2016-09-22

    Hi Hannes,
    I'm trying to understand why you need nested Futures.
    in A2.do2(), you are returning a Future, that is already done and has another future as result.
    As it does not contain other information, why would you need this future in between?

    The IntermediateFuture type is meant for Futures that can return "intermediate" (not final) results to the receiver.
    You may check out the following chapter in our documentation (which has been restructured with the latest nightly builds):
    https://download.actoron.com/docs/nightlies/jadex-3.0.0-RC79/jadex-mkdocs/futures/futures/#intermediate-futures

    If you want to deliver multiple result values, you can also take a look at Tuple2Future.

    I hope this helps, but i guess I didn't quite get your use case..

    Regards,
    Julian

     
  • Hannes

    Hannes - 2016-09-22

    Hi Julian,
    instead of Future.DONE I'm actually returning Values, in all ugly completeness I return IFuture<list\<ifuture\<map\<iexternalaccess, boolean="">>>>.</list\<ifuture\<map\<iexternalaccess,>

    Now the issue is that if in do2() the line
    IFuture<Y> do3Fut = scheduleStep(this::do3);
    would be replaced by
    IFuture<Y> do3Fut = do3();
    then a definite deadlock would occur, since a1 is waiting for a2 to return from the do2() call and thus blocks the call do4() (from within do3()).

    To circumvent that I use that step. The future from that step must not be called before a1.do1() returned to m, then only the step can process.

    I hope this makes it a bit clearer.

     
  • Julian

    Julian - 2016-09-22

    Would this work (import SResultListener from jadex.commons.future):

     IFuture<Y> do2() {
     Future ret =  new Future();
      IFuture<Y> do3Fut = scheduleStep(this::do3);
      do3Fut.addResultListener(SResultListener.delegate(ret));
      return ret;
     }
    

    ?
    The concept of futures is that they can be returned immediately, even if the result is not there yet. Which is exactly what you described - or maybe I misunderstood you again :)

     
  • Hannes

    Hannes - 2016-09-23

    I just tried to build a minimum concept, and now I wonder whether I misunderstood something:

    public class TestPlatform {
        public static void main(final String[] args) {
            final String[] defargs = new String[] {"-gui", "false", "-welcome", "false", "-cli", "false", "-printpass", "false", "-awareness", "false"};
            final IFuture<IExternalAccess> platfut = Starter.createPlatform(defargs);
            final IExternalAccess platform = platfut.get();
            System.out.println("Started platform: " + platform.getComponentIdentifier());
            final IComponentManagementService cms = SServiceProvider.getService(platform, IComponentManagementService.class, RequiredServiceInfo.SCOPE_PLATFORM).get();
    
            final CreationInfo creationParent = new CreationInfo(platform.getComponentIdentifier());
            final ITuple2Future<IComponentIdentifier, Map<String, Object>> componentFuture1 = cms.createComponent("a1", TestAgent.class.getName() + ".class", creationParent);
            final IComponentIdentifier component1 = componentFuture1.getFirstResult();
            final IExternalAccess external1 = cms.getExternalAccess(component1).get();
    
            final ITuple2Future<IComponentIdentifier, Map<String, Object>> componentFuture2 = cms.createComponent("a2", Test2Agent.class.getName() + ".class", creationParent);
            final IComponentIdentifier component2 = componentFuture2.getFirstResult();
            final IExternalAccess external2 = cms.getExternalAccess(component2).get();
    
            final ITestInterface service1 = SServiceProvider.getDeclaredService(external1, ITestInterface.class).get();
            service1.other(external2).get();
    
            final ITestInterface2 service2 = SServiceProvider.getDeclaredService(external2, ITestInterface2.class).get();
            service2.other(external1).get();
    
            final IFuture<IExternalAccess> do1 = service1.do1();
            final IExternalAccess ea = do1.get();
            System.out.println(ea);
    
            platform.killComponent().get();
        }
    }
    
    @Agent
    @Service
    @ProvidedServices(@ProvidedService(type = ITestInterface.class))
    public class TestAgent implements ITestInterface {
    
        @Agent
        private IInternalAccess agent;
        private ITestInterface2 a2;
    
        @Override
        public IFuture<IExternalAccess> other(final IExternalAccess ea) {
            a2 = SServiceProvider.getDeclaredService(ea, ITestInterface2.class).get();
            return new Future<>(agent.getExternalAccess());
        }
    
        @Override
        public IFuture<IExternalAccess> do1() {
            return a2.do2();
        }
    
        @Override
        public IFuture<IExternalAccess> do4() {
            return new Future<>(agent.getExternalAccess());
        }
    }
    @Agent
    @Service
    @ProvidedServices(@ProvidedService(type = ITestInterface2.class))
    public class Test2Agent implements ITestInterface2 {
    
        @Agent
        private IInternalAccess agent;
        private ITestInterface a1;
    
        @Override
        public IFuture<IExternalAccess> other(final IExternalAccess ea) {
            a1 = SServiceProvider.getDeclaredService(ea, ITestInterface.class).get();
            return new Future<>(agent.getExternalAccess());
        }
    
        @Override
        public IFuture<IExternalAccess> do2() {
            return a1.do4();
        }
    }
    

    I omitted the corresponding Interfaces, they are defined by their overriden methods.

    I thought that any agent can not be run multiple times in parallel, and I assumed that would include blocking calls. That would mean that while a1 is in method do1() and blocks while waiting for a2.do2(), a1 would not be able to run do4(), thus it couldn't return to do2().

    But it works, everything returns fine, but why?

     

    Last edit: Hannes 2016-09-23
  • Julian

    Julian - 2016-09-26

    Hi Hannes,
    the reason your code works is that calling a service is not the same as calling a java method on the component directly.
    When you call a service, a new step is scheduled on the target component implicitely, so no blocking happens. As results can be passed at any time in the future because of the Future pattern, this works seamlessly.
    You can read (much!) more about this at https://download.actoron.com/docs/nightlies/jadex-3.0.0-RC80/jadex-mkdocs/guides/ac/05%20Services/#concurrency.
    The following image may help understanding this:

     

    Last edit: Julian 2016-09-26
  • Hannes

    Hannes - 2016-09-26

    Hi Julian,
    I do understand the Future system in principal, and I see now why my code above would work.
    Sorry for taking your time and many thanks
    Hannes

     

Log in to post a comment.

MongoDB Logo MongoDB