PVM++: An Example

Let's have a look at the forkjoin PVM example program and its PVM++-Equivalent:

forkjoin.c (PVM-version) forkjoin.cc (PVM++-version)
// Fork Join Example 
// Demonstrates how to spawn processes and exchange messages

// defines and prototypes for the PVM library
#include <pvm3.h>

// Maximum number of children this program will spawn
#define MAXNCHILD   20

// Tag to use for the join message
#define JOINTAG     11

// Fork Join Example
// Demonstrates how to spawn processes and exchange messages with PVM++

// defines and prototypes for the PVM++ and PVM libraries
#include <pvm++/pvm++.hh>

// Maximum number of children this program will spawn
#define MAXNCHILD   20

// Tag to use for the join message
#define JOINTAG     11

// Define join message to send to parent
struct ToSendStruct : public Pvm::Struct
{
  PvmSetStructId (JOINTAG);
  PvmRegistration ()
    {
      Pvm::Register (Sender);
    }
  Pvm::Task Sender;
};
int
main (int argc, char* argv[])
{

  // number of tasks to spawn, use 3 as the default
  int ntask = 3;
  // return code from pvm calls
  int info;
  // my task id
  int mytid;
  // my parents task id
  int myparent;
  // children task id array
  int child[MAXNCHILD];
  int i, mydata, buf, len, tag, tid;

  // find out my task id number
  mytid = pvm_mytid ();

  // check for error
  if (mytid < 0) 
    { 
      // print out the error
      pvm_perror (argv[0]); 
      // exit the program
      return -1;
    }
  // find my parent's task id number
  myparent = pvm_parent ();

  // exit if there is some error other than PvmNoParent
  if ((myparent < 0) && (myparent != PvmNoParent)) 
    {
      pvm_perror (argv[0]);
      pvm_exit ();
      return -1;
    }
int
main (int argc, char* argv[])
{
  // number of tasks to spawn, use 3 as the default
  int ntask = 3;
  // if i don't have a parent then i am the parent
  if (myparent == PvmNoParent) 
    {
      // find out how many tasks to spawn
      if (argc == 2) 
        ntask = atoi (argv[1]);
      // make sure ntask is legal
      if ((ntask < 1) || (ntask > MAXNCHILD)) 
        { 
          pvm_exit (); 
          return 0; 
        }

      // spawn the child tasks
      info = pvm_spawn (argv[0], (char**)0, PvmTaskDefault, 
                        (char*)0, ntask, child);
      // print out the task ids
      for (i = 0; i < ntask; i++) 
        if (child[i] < 0) // print the error code in decimal
          printf (" %d", child[i]);
        else  // print the task id in hex
          printf ("t%x\t", child[i]);
      putchar ('\n');

      // make sure spawn succeeded
      if (info == 0) 
        { 
          pvm_exit (); 
          return -1; 
        }

      // only expect responses from those spawned correctly
      ntask = info;
  // if i don't have a parent then i am the parent
  if (!Pvm::Pvm ().I ().HasParent ()) 
    {
      // find out how many tasks to spawn
      if (argc == 2) 
        ntask = atoi (argv[1]);
      // make sure ntask is legal
      if ((ntask < 1) || (ntask > MAXNCHILD)) 
        return -1;
      // spawn the child tasks
      Pvm::TaskSet Tasks;
      Pvm::HostSet Hosts;
      // get all hosts in the PVM
      Pvm::Pvm ().Hosts (Hosts);
      // spawn the programs there
      Hosts.Spawn (argv[0], ntask, Tasks);
      // print out the task ids
      Pvm::TaskSet::const_iterator Current;
      for (Current = Tasks.begin (); Current != Tasks.end (); ++Current)
        std::cout << *Current << ", ";
      std::cout << std::endl;
      for (i = 0; i < ntask; i++) 
        {
          // recv a message from any child process
          buf = pvm_recv (-1, JOINTAG);
          if (buf < 0)  
            pvm_perror ("calling recv");
          info = pvm_bufinfo (buf, &len, &tag, &tid);
          if (info < 0) 
            pvm_perror ("calling pvm_bufinfo");
          info = pvm_upkint (&mydata, 1, 1);
          if (info < 0) 
            pvm_perror ("calling pvm_upkint");
          if (mydata != tid) 
            printf ("This should not happen!\n");
          printf ("Length %d, Tag %d, Tid t%x\n", len, tag, tid);
        }
      pvm_exit ();
      return 0;
    }
      // as long as not all children have sent data
      while (!Tasks.empty ()) 
        {
          ToSendStruct ToReceive;
          Pvm::Task From;
          // receive data from child
          ToReceive.Receive (From);
          std::cout << "received data from child " << From 
                    << "." << std::endl;
          if (From != ToReceive.Sender)
            std::cout << "This should not happen!" << std::endl;
          // remove child from list of tasks to listen to
          Tasks.erase (From);
        }
    }
  // i'm a child
  info = pvm_initsend (PvmDataDefault);
  if (info < 0) 
    {
      pvm_perror ("calling pvm_initsend"); 
      pvm_exit (); 
      return -1;
    }
  info = pvm_pkint (&mytid, 1, 1);
  if (info < 0) 
    {
      pvm_perror ("calling pvm_pkint"); 
      pvm_exit (); 
      return -1;
    }
  info = pvm_send (myparent, JOINTAG);
  if (info < 0) 
    {
      pvm_perror ("calling pvm_send"); 
      pvm_exit (); 
      return -1;
    }
  pvm_exit ();
  return 0;
}
  else
    {
      // i'm a child
      ToSendStruct ToSend;
      ToSend.Sender = Pvm::Pvm ().I ();
      ToSend.Send (Pvm::Pvm ().I ().Parent ());
    }
}

As you can see, the sending and receiving of messages have become very easy, whereas there is little overhead defining the messages, that will be sent.

The PVM++ version of this example can also be found in the distribution tarball under examples/forkjoin/forkjoin.cc