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.
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.
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.
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>
. . . <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]); . . . }
#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.
#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.
. . . 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); } } . . .
. . . 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.