Overview
Manual connections can either be mapped such that each connection always refers to the same target element or they can be unmapped such that a single connection is used to access many elements. This section considers both types of connection mapping:
Mapped Manual Connections
By default the translator will equip each active object element with an array of connections that has the dimensionality required for the active element to map each element of the providing object. The means that all elements of the providing element can be simultaneously opened. The disadvantage is that each connection element requires several hundred bytes or so of storage.
In the example below a method with dimensionality [N] has a manual connection to a store with dimensionality [N][M].
This would mean that by default each of the 'N' elements of the method would have a manual connection array with dimensionality [M]. This means that each connection references one of the [M] store elements, and the connection attributes would be initialized to those specified in the store connection properties window.
In order for element 'n' of the method to write to element 'n,m' of the store, the method needs to reference the appropriate connection element. Since each method element owns 'M' connections, this needs to be specified in the generated access function. Once the connection is referenced however, its attributes are already set, and the call can be made using an overload that does not require any arguments. If however, the connection arguments (timeout, keys, rules etc) are determined at execution time, then the alternative overload will be required (see 'unmapped' example below).
The following code uses a mapped connection to write to each of the 'M' store elements that each of the example method's elements have been connected to. Note that in this (mapped) case each method element owns 'M' connections and so the 'Aux1_CTst1Cxn' access function returns a unique connection for each value of 'm'. The transient store also causes an access function to be generated for its data record with the name 'Aux_CTst1Rec'.
for ( Uns m = 0; m < M; m++ )
{
Aux1_CTst1Cxn(m).OpenWrite(); // Open each connection for write access
}
for ( Uns m = 0; m < M; m++ )
{
Aux1_CTst1Rec(m).Construct(); // Construct the store's record (data object)
Populate( Aux1_CTst1Rec(m) ); // Call user defined code to populate the store's record
}
for ( Uns m = 0; m < M; m++ )
{
Aux1_CTst1Cxn(m).Close(); // Close each connection
}
Unmapped Manual Connections
If the program logic does not require multiple connections to be simultaneously opened, then there is probably no need to create more than one connection element because it can be used to access each providing element in turn. In order to direct the translator to create unmapped connections, the active object's connection dimensionality attribute needs to be set. By default it will be set to the dimensionality required for a one-to-one mapped connection and so for our method example this would be [M]. If this were set to [1] in our example however, then only one connection would be created and the generated access function would re-use the connection to access each element in turn. This would result in the following user code;
for ( Uns m = 0; m < M; m++ ){
Aux1_CTst1Cxn().SetElemNum( m ); // Set the connection to access the correct element
Aux1_CTst1Cxn().OpenWrite( k, // Key must be explicitly set in unmapped case
CLP_WAIT ); // Timeout must also be explicitly set
Aux1_CTst1Rec().Construct(); // Construct the store's record (data object)
Populate( Aux1_CTst1Rec() ); // Call user defined code to populate the store's record
Aux1_CTst1Cxn().Close(); // Close the connection
}
Notes
It is seldom necessary to use manual connections with methods but they are required for particular situations. A typical case is where the number of output 'writes', or the target for output 'writes', depends on the method's input data content (see The Variable Output Problem). If a manual connection operation blocks, for example a write to a transient store which is full, then the scheduler may need to transparently create another worker thread in order to avoid deadlock (see Process Attributes). It is usually best therefore, to collect all inputs and outputs before triggering a method, rather than using manual connections. In many cases, conditional outputs are best collected and then aborted if not required.
Examples
This example shows a connection between an 'N' element consuming collector named 'A' and a providing 'N x M' dimensional transient store named 'B' which is connected for read access. In this example the scalar thread owns an 'N' dimensional array of manual connections (default dimensionality). Each element of the collector will collect 'M' store elements. The translator will generate three access functions;
Aux1_AClx1Cxn(Uns n)
This function will return a reference to the specified manual collector connection. The connection can then be opened and interrogated using its member functions (see Collector Connections).
Aux1_BTst1Cxn(Uns n,
Uns m)
Once the connection has been opened, this function will return a reference to each store connection that provided an event to the collector. Note that this function takes two arguments because although the example collector is one dimensional, each of its elements collects a one dimension array of store connections. Unlike the collector, this connection is automatic (see Automatic Connections).
Aux1_BTst1Rec(Uns n,
Uns m)
Once the connection has been opened, this function will return a reference to each store record whose associated connection provided an event to the collector. Note that this function takes two arguments because although the example collector is one dimensional, each of its elements collects a one dimension array of store connections. Unlike the collector, this connection is automatic (see Automatic Connections).
The following code will call a user function to process each element of the [M] element store event array that each collector element collects.
// Request from each Clx element in turn;
for ( Uns n = 0; n < N; n++ )
{
// Wait for a compound event from each collector element
Aux1_AClx1Cxn( n ).WaitEvent( CLP_WAIT );
for ( Uns m = 0; m < M; m++ )
{
// Call user processing code with each store data record
Process( Aux1_BTst1Rec( n, m ) );
}
// Close the compound event
Aux1_AMpx1Cxn( n ).Close();
}
This example shows a connection between an 'N' element consuming multiplexer named 'A' and a providing 'N x M' dimensional transient store named 'B' which is connected for read access. In this example the scalar thread owns an 'N' dimensional array of manual connections (default dimensionality). Each element of the multiplexer will multiplex 'M' store elements. The translator will generate three access functions;
Aux1_AMpx1Cxn(Uns n)
This function will return a reference to the specified multiplexer connection. The connection can then be opened and interrogated using its member functions (see Multiplexer Connections).
Aux1_BTst1Cxn(Uns n)
Once the connection has been opened, this function will return a reference to the store connection that provided the event to the multiplexer. Note that this function only takes one argument because the example multiplexer is one dimensional, and multiplexors can only return one event at a time. So even though the store is two dimensional, the multiplexer event will only contain one store event.
Aux1_BTst1Rec(Uns n)
Once the connection has been opened, this function will return a reference to the store record whose associated connection provided the event to the multiplexer. Note that this function only takes one argument because the example multiplexer is one dimensional, and multiplexors can only return one event at a time. So even though the store is two dimensional, the multiplexer event will only contain one store event.
The following code will call a user function to process the received store event. It uses the multiplexed event's element number to determine the providing store's element number (second coordinate).
Uns elemNum; // Store's element number
// Request from each mpx element in turn;
for ( Uns n = 0; n < N; n++ )
{
Aux1_AMpx1Cxn( n ).WaitEvent( CLP_WAIT ); // Wait for an event
elemNum = Aux1_AMpx1Cxn( n ).ElemNum(); // Get mpx cxn element number(2nd co-ord)
// Call user processing code with store data and store element number
Process( Aux1_BTst1Rec( n ), // Store's data record
elemNum );
// Close the event
Aux1_AMpx1Cxn( n ).Close();
}
The following example illustrates the use of 'repeated' connections.
This example assumes that the 'N' element manual transient store connection has a repeat count attribute of 'M'. The dimensionality attribute is assumed to be set to the default value of '[N]', so the translator will actually create 'N x M' connections.
// Loop over store elements
for ( Uns n = 0; n < N; n++ )
{
// Now open 'M' buffers of each store element simultaneously
for ( Uns m = 0; m < M; m++ )
{
Aux1_ATst1Cxn( n * M + m ).OpenRead( 0, // No rule, so any key will do
CLP_NO_RULE, // No rule, so read all buffers
CLP_WAIT ); // Block till ready
}
// Now close all 'M' buffers of element 'n'
for ( Uns m = 0; m < M; m++ )
{
Aux1_ATst1Cxn( n * M + m ).Close();
}
}