if (_node_number == 1)
{
/* code to be executed by _logical_node_number 1 */
...
...
}
if (_node_number == 2)
{
/* code to be executed by _logical_node_number 2 */
...
...
}
...

Because all the tasks may have to run on the root in debug mode, testing _node_number against 1, 2 or 3 will not work. In this case _node_number will be 1 for all the tasks. How can we have the code above work then? The solution is not to use constants, but use macros instead:
if (_node_number == ONE)
{
...
}
if (_node_number == TWO)
{
...
}
You will have guessed how this will work. When the tasks are to run on the root, then the macros ONE and TWO will be defined as 1. When the application is ported to several transputers, then the macros ONE and TWO will be defined as 1 and 2, respectively. This is done, of course, as part of the map file which is given in its entirety below:
/* =======================================================================
map.h
DESCRIPTION:
Used to debug a multi-transputer network. Maps all the tasks running
on different transputers to the root.
======================================================================= */
#ifndef map_h
#define map_h
#include "conc.h"
#define NO_XPUTERS 2 /* number of logical transputers */
#undef RUN_ON_ROOT
#define TASK_WSSIZE 4096 /* default stack size allocated to tasks */
#ifdef RUN_ON_ROOT /* if run time execution is mapped to root */
Channel *Link0In[NO_XPUTERS+1]; /* define soft channels to replace */
Channel *Link0Out[NO_XPUTERS+1]; /* hard links. */
Channel *Link1In[NO_XPUTERS+1]; /* LinkXIn[0] and LinkXOut[0] not used */
Channel *Link1Out[NO_XPUTERS+1];
#define ONE 1
#define TWO 1
#else
#define ONE 1
#define TWO 2
#endif
/* -------------------------------------------------------------------- */
/* ABORTIF */
/* -------------------------------------------------------------------- */
int AbortIf(int condition, char *msg)
{
if (condition)
{
if (_node_number==ONE)
printf("*** %s ***", msg);
exit(0);
}
return 0;
}
/* -------------------------------------------------------------------- */
/* INITIALIZEMAPPING */
/* -------------------------------------------------------------------- */
int InitializeMapping(void)
{
#ifdef RUN_ON_ROOT
int i;
if (_node_number==1)
printf("\n\n<<<All tasks mapped to Root Transputer>>>\n\n");
/*--- Node 1 is special. It does have hard channels ---*/
Link0In[1] = LINK0IN;
Link0Out[1] = LINK0OUT;
AbortIf((Link1In[1] = ChanAlloc())==NULL, "Dyn Mem Fail");
AbortIf((Link1Out[1] = ChanAlloc())==NULL, "Dyn Mem Fail");
/*--- but the others don't! ---*/
for (i = 2; i<NO_XPUTERS; i++)
{
Link0In[i] = Link1Out[i-1];
Link0Out[i] = Link1In[i-1];
AbortIf((Link1In[i] = ChanAlloc())==NULL, "Dyn Mem Fail");
AbortIf((Link1Out[i] = ChanAlloc())==NULL, "Dyn Mem Fail");
}
/*--- except for last transputer ---*/
Link0In[NO_XPUTERS] = Link1Out[NO_XPUTERS-1];
Link0Out[NO_XPUTERS] = Link1In[NO_XPUTERS-1];
Link1In[NO_XPUTERS] = LINK1IN;
Link1Out[NO_XPUTERS] = LINK1OUT;
#endif
return 0;
}
/* -------------------------------------------------------------------- */
/* STARTTASK */
/* Launches a task as if it were started on its one transputer. */
/* -------------------------------------------------------------------- */
Process *StartTask(void (*f)(),
int argc, char *argv[], int _logical_node_number)
{
static Process *P;
P = ProcAlloc(f, TASK_WSSIZE, 3*((int) sizeof(int)), argc, argv,
_logical_node_number);
AbortIf(P==NULL, "ProcAlloc out of dynamic memory\n");
ProcRun(P);
return P;
}
#ifdef RUN_ON_ROOT
#undef LINK0IN
#define LINK0IN Link0In[_logical_node_number]
#undef LINK0OUT
#define LINK0OUT Link0Out[_logical_node_number]
#undef LINK1IN
#define LINK1IN Link1In[_logical_node_number]
#undef LINK1OUT
#define LINK1OUT Link1Out[_logical_node_number]
#endif
#endif
The highlighted section above contains the last part of map.h which we haven't explained. It is a simple function which we will use to launch the tasks that are assigned to different nodes. Its usage is shown in the full code for our main program: test.c.
/* ======================================================================= test.c DESCRIPTION: test program showing use of map.h mapping module. ====================================================================== */ #include <stdio.h> #include <stdlib.h> #include "conc.h"
#include "map.h"
/* =========================== DEFINITIONS ============================ */
#define INTSIZE ((int) sizeof(int))
/* =========================== PROTOTYPES ============================= */
void Task1(Process *, int, char **, int);
void Task2(Process *, int, char **, int);
/* -------------------------------------------------------------------- */
/* MAIN */
/* -------------------------------------------------------------------- */
main(int argc, char *argv[])
{
InitializeMapping();
if (_node_number==ONE)
StartTask((void(*)()) Task1, argc, argv, 1);
if (_node_number==TWO)
StartTask((void (*)()) Task2, argc, argv, 2);
while (1)
ProcReschedule();
}
/* -------------------------------------------------------------------- */
/* TASK1 */
/* Concurrent task that prints a message, receives an integer from Task2*/
/* and prints its value on the screen. */
/* -------------------------------------------------------------------- */
void Task1(Process *P, int argc, char *argv[], int _logical_node_number)
{
int i;
printf("Task %d running:\n ", _logical_node_number);
ChanIn(LINK1IN, &i, INTSIZE);
printf("Received %d from Task2\n", i);
exit(0);
}
/* -------------------------------------------------------------------- */
/* TASK2 */
/* Concurrent task that sends the integer 105 to Task1 */
/* -------------------------------------------------------------------- */
void Task2(Process *P, int argc, char *argv[], int _logical_node_number)
{
int i;
i = 105;
ChanOut(LINK0OUT, &i, INTSIZE);
}
The code of this program is very close to the typical farm programs we wrote in the previous chapters. The main differences appear in four places:
It is worth exploring this last point. The task-launching function StartTask starts the tasks with a non-blocking ProcRun. This is necessary, so that a call to StartTask can launch a task and return to the main program so that other tasks running on different nodes can be started as well. This means that main should not cause the program to stop. This is accomplished here by forcing main to remain asleep with an endless loop on ProcReschedule, and by giving control of the exit to Task1.
Task 1 running: Received 105 from Task2The program runs on the root. Let's move it to two transputers chained via LINK0 and LINK1. All we have to do is to remove the macro RUN_ON_ROOT from map.h. After compiling the program again, and loading it, we get the (unsurprising) same output. Had our program not worked in the first step, we could have included I/O statements in the tasks to trace key variables or highlight different execution phases.