Blocking Plan Execution

Intentions are parallel plan executions. that is, each intention is a plan that the agent is executing, and the plans can be executed in parallel. Sometimes the next step in a plan can depend upon information that provided by another intention. For example, consider an agent has two intentions: one that is downloading data from a range of sources, and another that is processing the data.

The second intention must wait until there is data to process, process the data, and then await the next piece of data. It is of course possible to do this sequentially, but the advantage of doing in parallel is that the agent can be downloading the next piece of data while it is processing the current piece.

As a result, there is a need to provide statements in the language that allow an intention to be blocked until some pre-defined condition arises.

Waiting for information

ASTRA includes a wait(…) statement that allows the intention to be suspended until the condition specified in the guard is satisfied.

  package examples;
  
  agent Waiter {
      module Console console;
      
      initial !block(), !count();
      
      rule +!block() {
          console.println("waiting...");
          wait(count(5));
          console.println("done waiting!");
      }
      
      rule +!count() {
          int X = 0;
          while (X < 6) {
              console.println("X="+X);
              -count(X-1);
              +count(X);
              X = X + 1;
          }
      }
  }

The above program defines an agent with two initial goals – one to !block() and another to !count(). The intentions that result from these goals are executed concurrently, with individual statements interleaved. The expected behaviour is that the !block() intention prints the waiting message to the console and then waits for the agent to believe that the count is 5. The !count() intention performs a loop in which the count(X:int) belief is updated in each iteration, with the argument of the belief increasing in the range 0-5. When the second intention adopts the belief that the count is 5, the wait condition on the first intention is satisfied, allowing execution of the first intention to progress, with the “finished” message being output to the console.

Variables declared in a wait(…) statement only have a scope that is the wait statement. This means that the intention is only notified that the condition has been satisfied and does not have access to the variable bindings that resulted in the conditions satisfaction. If you wish to be able to access the variable bindings, then you must use a when(…) statement.

Acting When you know Something

The when(…) statement is an alternative to the wait(…) statement that allows you access to the variable bindings (if they exist) that were generated when the condition was satisfied.

  package examples;
  
  agent Wheny {
      module Console console;
      module System system;
      
      initial !blocker(), !delayer();
      
      rule +!blocker() {
          console.println("waiting...");
          when( is(string X, "hungry") ) {
              console.println(X + " is ready to eat!");
          }
          console.println("finished");
      }
      
      rule +!delayer() {
          system.sleep(2000);
          console.println("sleep is over");
          +is("rem", "hungry");
      }
  }

The above program again introduces two concurrently executed intentions. The first !blocker() intention is similar to the previous example – it blocks the intention until the is(X, “hungry”) belief is satisfied, and then executed the body of the when statement. The !delayer() intention sleeps for 2 seconds and then adds a belief that satisifes the condition on the when statement.

As was indicated earlier, the main difference between when(…) and wait(…) is that any variable bindings generated when the condition of a wait(…) statement is satisfied are not available to the intention, while they are available in a when(…) statement. As a general rule, wait(…) statements should be used for ground conditions (no variables) and when(…) statements should be used variables are involved. Overall, wait(…) statements can be seen as being equivalent to a when(…) statement whose body is an empty block:

  wait(...); 
  //and
  when (...) {}; 
  //are equivalent!

Synchronized Blocks