Sessions Overview

Usually, each building block is instantiated once, as you would probably expect. But besides this normal case, there are some other multiplicity patterns (or session patterns) that are possible, and that allow to handle various cases:

  • Normal: A block with a normal session type is instantiated once.
  • Multi-session: A block with a multi-session can be executed with multiple sessions at the same time, and each session is executed independently of the others. This is useful when one needs to keep track of several instances of devices, users, or other processes that are easier to handle separately.
  • Single-Session: A block with a single-session is also instantiated exactly once, but it is realized as its own state machine, which is useful if a combined state machine in the normal case would be too complex.
  • Singleton: A singleton block exists as exactly one instance in the entire system, although several other building blocks may refer to it. This is useful if we need a resource that exists only once in the system (for example, a communication channel), and that should be shared among several other blocks.

The different session types have a different syntax:

Depending on the session type, communication with the session works differently:

  • Normal sessions are integrated with the surrounding block, so that the communication via the pins is synchronous. This means that a run-to-completion step may cross the border of the blocks, even several times, and is not interrupted or buffered.
  • Multi-sessions, single-sessions, and singletons are implemented separately from the surrounding block, and therefore the communication is buffered. This means that steps cannot continue directly when they enter or exit a block, but are interrupted.

Setting the Session Type when Using a Block

When using a block, you may in some cases select among different session type. Right-click the block and select the session type. Depending on how the block is constructed, different session types may be available. If blocks are designed and optimized for a fixed session type, they may be initialized with that session type right away, so you do not have to worry about it.

Setting the Session Type when Creating a Block

When you create a building block, you can select which kind of session type it may be used as. This is usually a good idea, since it is often clear in which session type a block should be used anyways. In addition, with the session type set, the Reactive Blocks SDK can execute additional inspection and find more errors. To determine the session type, go to the overview page of the editor, and choose the session pattern from the combo box.

Using Multi-Session Blocks

Multi-session blocks execute several instances of a block in parallel. This is useful when you need to keep track of several things in the real world at the same time. For example, if you would like to keep track of an entire fleet of vehicles at the same time, you need to create a data structure that keeps track of each vehicle. A multi-session block gives you such a data structure. In addition (and that’s the most useful part), you can define behavior that is executed for each of the session instances in separately. For example, each vehicle can have its own timers and other building blocks to monitor and control it, and they are all working independently of each other. The session instances are created dynamically, while the application is running. There can be as many instances as you need, and they can be created and terminated at any time. Internally, multi-session blocks can be made of any number and kind of building block. Multi-session blocks can contain very simple logic, but also complete sub-systems. Multi-session blocks may also contain further multi-session blocks. You can think of a multi-session block as a stack of cards, where each instance corresponds to one card. That is symbolized by the extra border around multi-session blocks.

Using Multi-Instance Blocks

By using multi-session blocks we mean that you integrate an existing multi-session block (for example one from a library) into your application. Of course, whenever we work with several instances of something, we need to somehow distinguish the instances from each other. This is taken care of by the internal logic of the multi-session block. Therefore, when using a multi-session block, there is nothing specific that we have to worry about, and the different parameters do different stuff with the instances. Starting parameters are used to create new session instances. Like with other blocks, there can be either one simple starting parameter, or several alternative ones. Each parameter passing creates a single session instance. While instances of a multi-session blocks are active and running, you can pass data into them using streaming parameters. In contrast to normal blocks, multi-session blocks have an annotation that reveal more about which instances will receive the parameter, that is, “all”, “some” or exactly “one”. Multi-session blocks may also have output parameters. These work in the same way as output parameters of other blocks, since they are always sent by one specific instance. Since all session instances are executed separately from each other, the communication from an application and towards a multi-session block is buffered. This means that a run-to-completion step always ends at an input parameter of a multi-session block, and a new run-to-completion step is triggered later on inside the block. The same holds for output parameters of a multi-session block, just vice-versa. (Read more on run-to-completion steps here.)

Pattern: Creating a Fixed Set of Multi-Session Instances

A new instance of a multi-session is created each time a token passes the starting parameter. The incoming parameter node needs to provide the session with its alias, which can be used to identify it. To create a fixed number of session instances, you can use the block Constructor. With its instance parameters it can be configured to issue a number of strings consisting of a prefix, a running integer and a suffix.

Pattern: Detecting Session Termination Consistently

To be sure that a shutdown happens only after all sessions have terminated, you can use an allocator block that keeps track of all session instances, as shown below.

This example works as follows:

  • The constructor first creates a number of sessions by passing a newly created string through the allocator towards the session. Since all strings are unique, the allocator emits them via created, so that new sessions are created via the starting pin.
  • Once the constructor is finished, it invokes stop of the allocator, to tell that the system is interested in stopping once all sessions terminated. (This can in a real application be any other event that may require the system to stop, it does not have to come from the constructor.)
  • The sessions in this example terminate on their own by an internal timeout. Once they terminate, they deliver their alias String to the allocator. The allocator keeps track of the remaining sessions and terminates once the last session ended.

Using Singleton Blocks

A singleton block is used to consistently handle a single centralized resource (e.g.,communication channel) which needs to be shared among several blocks in an application. In a system, there exists exactly one instance of a singleton block. You can, however, drag-and-drop a singleton block multiple times and connect them to other blocks. The Reactive Blocks SDK will consider the singleton blocks of the same name as the same instance. Singleton blocks have a dual block contract:

  • The client contract describes the behavior of the singleton block towards its surrounding block.
  • The provider contract takes into account the possibility that there exists other singleton blocks of the same name in another part of a system block.

Consequently, a client contact is simpler than a provider contract. When using a singleton block you only need to take into account the simple one, i.e., the client contract. Moreover, you don't need to consider that other singleton blocks of the same name exist. With its client contract, a singleton block can be treated as a normal local block. The rest of this page shows an example of a singleton block, its usage and about data to and from the block.

An Example: Singleton Block Sender

The following figure depicts an example of a singleton block with its client contract.

As shown by its client contract, the singleton block Sender can receive a message to be sent via pin send. Thereafter, a report about the message transmission can be emitted by the block, or, is 'delayed' while a code is requested via pin reqCode instead. For the latter option, the result is reported when a code is received by the block. The request may be denied by sending an event via pin cancelled. It is important to note that the client contract also shows that the singleton block can only handle one send event at a time. Subsequent send event can be received after the previous is handled.

An Example: Using Singleton Block Sender

In order to use a singleton block you need to fulfill its client contract. The upper part of the figure below shows an example of using the singleton block Sender.

Here, the blocks Client and Get Code respect the client contract of singleton block Sender since data from block Client enters the singleton block one after another one is handled. This is illustrated by the contract of block Client shown at the bottom part of the above figure. An event via pin data is emitted when the block is in state idle. The state is reached via an initial transition start/ or when event ready is received. Further, by traversing the flows to pin ready in the upper part of the previous figure, we see that event ready follows either a token from pin report form the singleton block Sender or a token from pin cancel from block Get Code. Both alternatives bring the singleton block to a final state; the first option through pin report while the second one via pin cancelled which receives a token from the pin cancel of block Get Code.

Sending/Receiving Events to/from a Singleton

As you may notice from the figure above, all the pins of a singleton block are of type SingletonData. A SingletonData object contains:

  • addressing information that is required for the singleton to send an event to its surrounding,
  • data that is sent to or received from a singleton.

When using a singleton block you do not need to pay attention to the addressing part. To send data to a singleton block, you can create an object of type SingletonData with a parameter as illustrated in the following example.

  String message = "Hi there!";
  SingletonData send = new SingletonData(message);

If no data is required to be sent to a singleton block, simply create an object without parameter.

  SingletonData cancelled = new SingletonData();

To get data from the singleton block, you can use the getData() method as shown in the following example.

  public void displayReport(SingletonData sd) {
    if (sd.getData() instanceof Report) {
      Report report = (Report) sd.getData();
      System.out.println("Sending result: " + report.getResult());
      System.out.println("Report message: " + report.getMessage());
    }
  }

To find out which type of object is expected by (or given out from) a singleton block, check the Parameter Nodes part in the Documentation page. The following figure shows the objects required and provided by the singleton block Sender.