Legion 1.4
Reference Manual


4.0 Example translations: the simple class

The first example is a nonsense class in that it does no meaningful computation. However, it exercises the full functionality of the library and also illustrates how our compiler will generate translations. We give the C++ class, it's translation, and a sample program that illustrates how we invoke methods.

To write the translation we define a wrapper class called Legion_Simple. This wrapper contains a protected data member that is an object of the C++ class being translated. We call this the wrapped object. For each member function in the wrapped object we define a corresponding member function in Legion_Simple. The job of these member functions is to take a work unit, build the corresponding call to the wrapped object's member function, execute the call, and package up any return values.

Also in Legion_Simple are additional member functions to figure out which wrapper member function should be called (invoke_method()), indicate to the invocation store which member functions will be accepted (enable_functions()), and perform the server loop (accept_member_functions()).

4.1 Simple.h and Simple.c


%-----------------Simple.h-------------------------%
// A very simple class definition
#ifndef _H_Simple_
#define _H_Simple_

#include <stdio.h>

class Simple
{
   int data;
 public:
   Simple();
   ~Simple();
   int op1(int foo);
   int op2(int &foo, int &bar);
};

#endif
%-----------------Simple.c-------------------------%
// A very simple class definition
#ifndef _C_Simple_
#define _C_Simple_

#include <stdio.h>
#include "Simple.h"

Simple::Simple() {
   data = 0;
}

Simple::op1(int foo) {
   data = foo;
}

int 
Simple::op2(int &foo, int &bar) {
   foo = data*data;
   bar = data+data;
   return foo+bar;
}

Simple::
~Simple()
{
}
#endif

4.2 Simple.trans.h and Simple.trans.c


%--------------Simple.trans.h----------------------------%
// Legion `wrapper' class definition for the Simple 
// class
#ifndef _H_Simple_trans_
#include <stdio.h>
#include "legion/Legion.h"
#include "Simple.h"


#define SIMPLE_OBJECT_CLASS_ID   "Simple"

#define SIMPLE_OP1_FUNCTION_NUMBER 1
#define SIMPLE_OP2_FUNCTION_NUMBER 2

// class Legion_Simple
class Legion_Simple
{
 private: 
   // the object being wrapped
   Simple *object;         

   // For generating methodDone events
   virtual void generate_MethodDoneEvent(); 
   
 public:
   Legion_Simple();
   ~Legion_Simple();

   // wrapper member functions for each member 
   // function in `object'
   void Legion_op1(UVaL_Reference<LegionWorkUnit> wu);
   void Legion_op2(UVaL_Reference<LegionWorkUnit> wu);

   // auxiliary member functions needed
   virtual void enable_functions
     (LegionInvocationStore *);
   virtual int invoke_method
     (UVaL_Reference<LegionWorkUnit> wu);
   virtual void accept_member_functions(); 
};
#endif
%--------------Simple.trans.c----------------------------%
// The `wrapper' class definition
#ifndef _C_Simple_trans_

#include <stdio.h>
#include <unistd.h>
#include "legion/Legion.h"

#include "Simple.trans.h"

// This is the wrapped object
static Legion_Simple *wrapper;

// This is the event handler for invoking a method
static int LegionMethodInvoke
   (UVaL_Reference<LegionEvent>);

// ---------------------------------------------------
// Generates a MethodDone event. Should be called 
// right after the call to the wrapped object's 
// member function.
void
Legion_Simple::
generate_MethodDoneEvent()
{
   UVaL_Reference<LegionEvent> methodDoneEvent;

   methodDoneEvent = new LegionEvent
     (LegionEvent_MethodDone, (void *) NULL);
   LegionEventManagerDefault.announce
     (methodDoneEvent, LegionEventAnnounceNow);
}

// ---------------------------------------------------
// Allocates the wrapped object and enables the 
// corresponding functions in the invocation store.   
Legion_Simple::
Legion_Simple()
{
   object = new Simple();
   wrapper = this;
   enable_functions(LegionInvocationStoreLL_Default);
}

// ---------------------------------------------------
// De-allocates the wrapped object
Legion_Simple::
~Legion_Simple()
{
   delete object;
   wrapper = NULL;
}

// ---------------------------------------------------
// wrapper member function for wrapped.op1()
void
Legion_Simple::
Legion_op1(UVaL_Reference<LegionWorkUnit> wu)
{
   // get the parameter from the work unit
   int parm1;
   UVaL_Reference<LegionBuffer> lb;
   lb = wu->get_parameter(1);
   lb->get_int(&parm1, 1);
   
   // Invoke the wrapped member function
   int result = object->op1(parm1);
   generate_MethodDoneEvent();
   
   // Return the result.
   UVaL_Reference<LegionBuffer> return_lb;
   return_lb = new LegionBuffer();
   return_lb->put_int(&result, 1);
   Legion_return(UVaL_METHOD_RETURN_VALUE, 
     *(wu->get_continuation_list()), return_lb);
}

// ---------------------------------------------------
// wrapper member function for wrapped.op2()
void
Legion_Simple::
Legion_op2(UVaL_Reference<LegionWorkUnit> wu)
{
   int parm1, parm2;
   UVaL_Reference<LegionBuffer> lb;
   lb = wu->get_parameter(1);
   lb->get_int(&parm1, 1);
   lb = wu->get_parameter(2);
   lb->get_int(&parm2, 1);
   
   int result = object->op2(parm1, parm2);
   generate_MethodDoneEvent();
   
   // Return all results. In this case, there are 
   // three of them (return value + 3 in/out 
   // parameters).
   UVaL_Reference<LegionBuffer> return_lb;
   return_lb = new LegionBuffer();
   return_lb->put_int(&result, 1);
   Legion_return(UVaL_METHOD_RETURN_VALUE, 
     *(wu->get_continuation_list()), return_lb);
   return_lb = new LegionBuffer();
   return_lb->put_int(&parm1, 1);
   Legion_return(1, 
     *(wu->get_continuation_list()), return_lb);
   return_lb = new LegionBuffer();
   return_lb->put_int(&parm2, 1);
   Legion_return(2, 
     *(wu->get_continuation_list()), return_lb);
}

// ---------------------------------------------------
// Invokes the appropriate method based on the 
// function number in the supplied work unit.
int
Legion_Simple::
invoke_method(UVaL_Reference<LegionWorkUnit> wu)
{
   switch (wu->get_function_number()) {
     case SIMPLE_OP1_FUNCTION_NUMBER: 
       Legion_op1(wu);
       break;
     case SIMPLE_OP2_FUNCTION_NUMBER: 
       Legion_op2(wu);
       break;
     default:
   fprintf(stderr,"Legion_Simple::invoke_method()\n");
   fprintf(stderr,"This object does not export 
    function number %d\n", 
    wu->get_function_number());
       exit(0); 
       break;
   }
}

// ---------------------------------------------------
// This is the server loop.
// EventMgr.serverLoop() continuously flushes events
// and then blocks waiting for events to become 
// available.
void
Legion_Simple::
accept_member_functions()
{
  LegionEventManagerDefault.serverLoop();
}
 
// ---------------------------------------------------
// Enable the wrapped object's functions. The LIS 
// must be explicitly told which functions to accept.
// There will eventually be some object mandatory
// functions in here too.
void
Legion_Simple::enable_functions(LegionInvocationStore *LIS) 
{
   // Enable the function numbers that I can handle...
   LegionInvocationStoreLL_Default->enable_function(
     SIMPLE_OP1_FUNCTION_NUMBER, DEFAULT_PRIORITY);
   LegionInvocationStoreLL_Default->enable_function(
     SIMPLE_OP2_FUNCTION_NUMBER, DEFAULT_PRIORITY);

// Register my event handler...
   LegionEvent_MethodReady.addHandler
     (LegionMethodInvoke, 1.0);
}

// ---------------------------------------------------
// This is the event handler that get called on a 
// MethodReady event. A MethodReady event is 
// generated every time a ready invocation is 
// inserted into the invocation store.
static int
LegionMethodInvoke(UVaL_Reference<LegionEvent> event)
{
   UVaL_Reference<LegionBuffer> lb;
   int parameter;

   if (LegionInvocationStoreLL_Default->any_ready()) {
      UVaL_Reference<LegionWorkUnit> wu;
      wu  = LegionInvocationStoreLL_Default->next_matched();
      wrapper->invoke_method(wu);
   }
   return 0;
}

// ---------------------------------------------------
int
main (int argc, char **argv)
{
   Legion.init();
   wrapper = new Legion_Simple();
   Legion.AcceptMethods();
   wrapper->accept_member_functions();
}
#endif

4.3 ex1_Simple.c

%--------------ex1_Simple.c---------------%
#include <stdio.h>
#include "Simple.trans.h"
#include "legion/Legion.h" 

UVaL_Reference<LegionParameter>
make_int_parameter(int parm_value, int parm_number)
{
   UVaL_Reference<LegionBuffer> lb;
   UVaL_Reference<LegionParameter> parm;

   lb = (LegionBuffer *) new LegionBuffer();
   lb->put_int(&parm_value, 1);
   parm = (LegionParameter *) 
     new LegionParameter(parm_number, lb);

   return parm;
}


int 
main(int argc, char **argv)
{
   // Variables for the `user' code
   int a = 10, b = 15;
   int x, y, z;

   // Initialize legion state
   // All of the below to get a random instance number
   int my_instance_number;
   struct timeval tv;
   gettimeofday(&tv,NULL);
   srand(tv.tv_sec ^ tv.tv_usec);
   my_instance_number = 
     rand() ^ tv.tv_sec ^ tv.tv_usec;

   // Initialize Legion Library
   Legion.init();

   // Manufacture my own loid because I'm a command
   // line object
   Legion.SetMyLOID
    (make_loid(UVaL_CLASS_ID_COMMANDLINE, 
      my_instance_number));

   // Tell my creator I'm ready to go
   Legion.AcceptMethods();

   // Create an empty program graph
   LegionProgramGraph G(Legion.GetMyLOID());

   // Create a couple of `Simple' objects
   UVaL_Reference<LegionLOID> A_name, B_name;
   A_name = Legion.CreateObject
     (SIMPLE_OBJECT_CLASS_ID);
   B_name = Legion.CreateObject
     (SIMPLE_OBJECT_CLASS_ID);

   // Get handles for each object
   LegionCoreHandle A_handle(A_name), 
      B_handle(B_name);

   // First call: x = A.op1(a);
   // invoke()'s signature is 
   // invoke(function_num, num_parms, num_results);
   UVaL_Reference<LegionInvocation> inv1;
   inv1 = A_handle.invoke
     (SIMPLE_OP1_FUNCTION_NUMBER, 1, 1);
   G.add_invocation(inv1);
   UVaL_Reference<LegionParameter> parm1;
   parm1 = make_int_parameter(a, 1);
   G.add_constant_parameter(inv1, parm1, 1);

   // Second call: y = B.op1(b);
   UVaL_Reference<LegionInvocation> inv2;
   inv2 = B_handle.invoke
     (SIMPLE_OP1_FUNCTION_NUMBER, 1, 1);
   G.add_invocation(inv2);
   UVaL_Reference<LegionParameter> parm2;
   parm2 = make_int_parameter(b, 1);
   G.add_constant_parameter(inv2, parm2, 1);

   // Third call: z = A.op1(x, y);
   // Both parameters are values yet to be computed,
   // so they must be invocation parameters.
   UVaL_Reference<LegionInvocation> inv3;
   inv3 = A_handle.invoke
     (SIMPLE_OP2_FUNCTION_NUMBER, 2, 3);
   G.add_invocation(inv3);
   G.add_invocation_parameter
      (inv3, inv1, 1, UVaL_METHOD_RETURN_VALUE);
   G.add_invocation_parameter
      (inv3, inv2, 2, UVaL_METHOD_RETURN_VALUE);

   // We specifically ask for the in/out parameters. 
   // Don't get them otherwise.
   G.add_result_dependency(inv3, 1);
   G.add_result_dependency(inv3, 2);

   // printf ("%d\n", z);
   // We need `z', so we must execute 
   G.execute();

   // and wait for the return.
   UVaL_Reference<LegionBuffer> lb;
   lb = G.get_value (inv3, UVaL_METHOD_RETURN_VALUE);
   lb->get_int(&z, 1);
   printf ("z is%d\n", z);

   // Since we asked for them, we can get the other 
   // values too.   
   G.release_all_values();
   lb = G.get_value (inv3, 1);
   lb->get_int(&x, 1);
   printf ("x is %d\n", x);
   lb = G.get_value (inv3, 2);
   lb->get_int(&y, 1);
   printf ("y is %d\n", y);

   Legion.DestroyObject(A_name);
   Legion.DestroyObject(B_name);
}


Back to Reference Manual Table of Contents