Parallel Programming in C for the Transputer
© D. Thiébaut, 1995



Assigning Tasks to nodes

The last problem we have to solve to make map.h fully operational is that of making it automatically assign the tasks to the nodes, be they logically mapped to the root or to individual transputers. The solution we have adopted here is to follow the general model of farm programming as closely as possible:

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 */
     ...
     ...
} 
...


Figure 6-2: Assignment of soft channels via the Link0In, Link0Out, Link1In and Link1Out arrays. Array cells must match on a one-to-one basis for the exchange of information to be possible. The soft channel pointer stored in the array element Link1Out[2], for example, must also be stored in Link0In[3] for Node 2 to be able to send information to Node 3.

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

Listing 6-1: listing of map.h

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);
}

Listing 6-2: listing of test program test.c examplifying the use of map.h module.

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.

Putting test.c to the test

Let's run test.c in debug mode. The map.h file is already set with the RUN_ON_ROOT macro defined. After compiling the program and loading it, we get the output:


Task 1 running:
Received 105 from Task2

The 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.





[Previous] [HOME] [NEXT]