next_inactive up previous
Up: SESAME Users Guide

PNRunner Users Guide

Joseph Coffland

jcofflan@users.sourceforge.net

April 5, 2006

Copyright ©2004 University of Amsterdam. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included with this documentation, GNU Free Documentation License.


Contents


1 Introduction

PNRunner stands for Process Network Runner and is the application simulator used in SESAME. PNRunner implements Kahn Process Networks (KPN) in C++. KPNs are networks of processes which communicate with each other via uni-directional and theoretically infinite communication channels. Kahn processes block on reads until data is available. In practice Kahn processes may also block on writes due to the space limitations of real computers. A nice feature of KPNs is that their execution is deterministic for a given input.

PNRunner also provides an API for creating KPNs and for the generation of trace events. Traces events are read, write or execute events produced by a running KPN. Communication events are automatically generated when the KPN read and write functions are called. Computation events must be added to the application code by the model creator.


2 Command Line Usage

See the PNRunner(1) man page. It is also necessary to use PNRunnerYMLTool(1) in order to create stub loaders for your application processes before running an application simulation with PNRunner.


3 YML Usage

PNRunner reads YML application descriptions files. This section describes parts of the YML description specific to PNRunner. For general YML programming see the YML Users Guide. YML mapping files are also described in the YML Users Guide.

First off the class attribute of network elements should always contain the value 'KPN' to specify Kahn Process Network. The class attribute of node elements should contain 'CPP_Process', 'C_Process's, or 'Java_Process' depending on the application code type. However, 'C_Process', and 'Java_Process' are not yet implemented.


3.1 node properties

library
This properties value specifies the location of the shared library containing the C++ code for this process.
class
This properties value specifies the C++ class name of the current process.


3.2 port properties

type
This properties value specifies the C++ data type which is communicated over this port. This may be a base type such as int or double, or a class or structure. Pointer references should not be passed over communication channels.

An example PNRunner application YML file demonstrating these properties is given below:

  <network name="Application" class="KPN">
    <node name="A" class="CPP_Process">
      <property name="class" value="AClass"/>
      <property name="library" value="libAClass.so"/>

      <port name="0" dir="in">
        <property name="type" value="integer"/>
      </port>
      <port name="1" dir="out">
        <property name="type" value="integer"/>
      </port>
    </node>
  </network>


3.3 Constructor Arguments

It is possible to pass arguments from YML or the command line to a PNRunner process via the arg property. arg properties are passed as string arguments to the process in the order they appear in the YML. The following example YML demonstrates this usage.

. . .
  <node name="ProcessA" class="CPP_Process">
  . . .
  <property name="arg" value="$inputfile"/>
  <property name="arg" value="$count"/>
  </node>
. . .

The example above will cause PNRunner to pass two arguments to the stub loader function. The arguments are variable references that must be set from the PNRunner command line as variable value pairs. An example PNRunner invocation follows:

  PNRunner inputfile=input.dat count=10 application.yml

The string value of these arguments can be accessed via the Process::getArgv() and Process::getArgc() functions. For example.

  void SomeNode::main() {
    string inputfile = "default";
    int count = 0;

    if (getArgc() > 1) inputfile = getArgv()[1];
    if (getArgc() > 2) count = atoi(getArgv()[2]);

    . . .
  }


4 C++ Programmers API

This section describes the C++ interface to PNRunner for application processes. Familiarity with C++ is assumed.


4.1 Creating a New Process

A new C++ PNRunner process named ProcessA should have the following structure:

#include "process.h"
#include "port.h"

class ProcessA : public Process {
public:
  ProcessA(Id n);

  const char* type() const {return "ProcessA";}
  void main();
}


ProcessA::ProcessA(Id n) : Process(n) {
  // Process initialization code
}

void ProcessA::main() {
  // Process execution
}

As in the above example, all processes must inherit from the class Process. ProcessA passes its Id on to the its parent class and implements the pure virtual functions type and main. The type function simply returns a constant string naming the process. When PNRunner executes the process it will first call the constructor to create a new instance. Any initialization code should go in the constructor. Then it will call the processes main function. User code should be placed here.

So far ProcessA has no ports. With out ports a process cannot communicate with other processes. The next section describes how to create ports.


4.2 Creating Ports

Ports are connected by PNRunner at construction time by passing communication objects to the processes constructor. Therefore in order to add a port it is necessary to modify the example from the previous section as follows:

#include "process.h"
#include "port.h"
#include "types.h"

class ProcessA : public Process {
  InPort<DataType1> port1;
  OutPort<DataType2> port2;

public:
  ProcessA(Id n, In<DataType1> &port1, Out<DataType2> &port2);

  const char* type() const {return "ProcessA";}
  void main();
}


ProcessA::ProcessA(Id n, InPort<DataType1> &port1, OutPort<DataType2> &port2) :
  Process(n),
  port1(id("port1"), port1),
  port2(id("port2"), port2) {

  // Process initialization code
}

void ProcessA::main() {
  // Process execution
}

The above example adds an input and and output port to ProcessA. The id string, as shown in the above example, must match the port name used in the application YML. In general input ports must be listed first followed by the output ports in the process constructor.

The InPort and OutPort classes are templates. The template parameter specifies the type of data that will be communicated over the port. Only ports of the same data type can be connected. In the above example it is assumed that DataType1 and DataType2 are structures defined in the header file types.h.

Passing pointers via ports is illegal. There are several reasons for this. First, you cannot assume that processes share the same memory space. Second, PNRunner makes a copy of the memory buffer with out calling constructors before placing the data in the message queue. Finally, processes execute concurrently and sharing memory would cause a race condition. A direct consequence of this is that you cannot pass classes such as the std::string because std::string uses pointers internally. In fact if you do you will get very nasty results.

Once created ports may be used from a processes main function. You may not read or write ports from the constructor. The next section describes how to communicate with other processes using ports.


4.3 Process Communication

PNRunner process communicate via ports. You may read from InPorts and write to OutPorts via the read and write functions. The example below shows how to read and write using the the ports created in the example from the previous section.

. . .
ProcessA::main() {
  DataType1 packet1;
  DataType2 packet2;

  while (true) {
    read(port1, packet1);

    // Do something with packet1 and packet2.
    // Also, some condition should alow ProcessA to
    // eventualy break out of this loop.

    write(port2, packet2);
  }
}
. . .


4.4 Emitting Traces

Calls to read and write will automatically emit trace events. Computation events must be emitted explicitly with the execute function. The example below demonstrates the execute function.

. . .
ProcessA::main() {
  DataType1 packet1;
  DataType2 packet2;

  while (true) {
    read(port1, packet1);

    // Do something with packet1 and packet2.
    // Also, some condition should alow ProcessA to
    // eventualy break out of this loop.

    execute("Operation1");
    // Code for operation 1

    write(port2, packet2);

    execute("Operation2");
    // Code for operation 2
  }
}
. . .

The strings passed to execute must match instructions named in the YML mapping file.


next_inactive up previous
Up: SESAME Users Guide
Joseph Coffland 2006-04-05