/*
 * $Id: example.c,v 1.2 1999/01/08 01:13:23 ymmt Exp $
 * Copyright (C) 1999 Hirotaka Yamamoto <ymmt@is.s.u-tokyo.ac.jp>
 * All rights reserved.
 *
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
 *
 * Permission is hereby granted to use or copy this program for any
 * purpose, provided the above notices are retained on all copies.
 * Permission to modify the code and to distribute modified code is
 * granted, provided the above notices are retained, and a notice that
 * the code was modified is included with the above copyright notice.
 */

/* example.c - a small example of DGC */


#include <dgc.h>		/* include DGC API */
#include <dgc_stdmsg.h>		/* use DGC standard message-passing API */

/* prototypes */
void *make_remote_reference (void);
void *make_object_and_export (void);
void *import_reference (void);
void test_pointer (void *pointer);
void dereference (void *pointer);

#if defined(MP_SCORE)
int score_main (int argc, char **argv)	/* for SCore-S */
#else
int main (int argc, char **argv)
#endif
{
  void *pointer;

  /* initialization */
  DGC_init(&argc, &argv);	/* initialize DGC & network */

  /* need 2 processors to run */
  if (DGC_get_num_PE() != 2) {
    fprintf(stderr, "wrong # of processors\n");
    DGC_abort();
  }

  /* print my ID */
  printf("I am %d\n", DGC_get_my_ID());

  /* create a public object and a remote reference to it */
  pointer = make_remote_reference();

  /* test whether POINTER is a pointer to stub or not */
  test_pointer(pointer);

  /* dereference a remote reference  */
  dereference(pointer);

  printf("<%d>: done\n", DGC_get_my_ID());

  DGC_finalize();		/* finish DGC */
  return 0;
}


/* This first create a public object on processor 0 and then create
   a remote reference to the object on processor 1. */
void *make_remote_reference ()
{
  if (DGC_get_my_ID() == 0) {
    return make_object_and_export();
  } else {
    return import_reference();
  }
}


/* Create a public object and export the reference to the object
   using DGC_export.  DGC_export packs information of the object
   into UID structure passed as its second argument.  UID should
   be passed to DGC_import later to create a remote reference. */
#define OBJSIZ	100
void *make_object_and_export ()
{
  void *object;
  DGC_uid uid;

  object = GC_malloc(OBJSIZ);		/* create a public object */
  DGC_export(object, &uid);		/* then export it */

  DGC_send_msg(1, &uid, sizeof(DGC_uid));	/* send UID to processor 1 */

  return object;
}


/* Receive a UID structure from remote processor and create a
   remote reference.  DGC_import will create a STUB and return
   the pointer to it, which can be distinguished from other local
   pointers as described below, thus one can see if a pointer
   represents a remote reference or not. */
void *import_reference ()
{
  void *remote_ref;
  DGC_uid *uid;

  uid = (DGC_uid*)DGC_receive_msg(0);	/* receive a message as a UID */
  remote_ref = DGC_import(uid);		/* and pass it to DGC_import */
  DGC_receive_done(uid);		/* declare the end of receiving */

  return remote_ref;
}


/* Test a pointer if it is a pointer that represents a remote
   reference or not (pointer to a local object).  This is a silly
   example in this context because we clearly know that POINTER
   on processor 1 is a pointer to a stub and on processor 0 to a
   local object; just to explain how to use DGC_is_local. */
void test_pointer (void *pointer)
{
  /* DGC_is_local will return 1 when POINTER is a pointer
   to a non-stub object or NULL; otherwise it returns 0. */
  if (DGC_is_local(pointer) == 0) {
    printf("<%d>: pointer(%p) is a pointer to a stub\n",
	   DGC_get_my_ID(), pointer);
  } else {
    printf("<%d>: pointer(%p) is NOT a pointer to a stub\n",
	   DGC_get_my_ID(), pointer);
  }
}


/* Dereference a remote reference; dereferencing a remote reference
   can be achieved in the following steps.  1) get the HOME processor
   where the public object exists, 2) export the STUB by DGC_export and
   send UID to the home processor, 3) on the home processor, receive
   the UID and pass it to DGC_import, which returns the local pointer to
   the public object. */
void dereference (void *pointer)
{
  if (DGC_is_local(pointer)) {
    /* for processor 0 */
    DGC_uid *uid;
    void *imported_obj;

    uid = DGC_receive_msg(0);		/* receive UID from 1 */
    imported_obj = DGC_import(uid);	/* import */
    DGC_receive_done(uid);		/* the end of receiving */

    printf("<%d>: pointer(%p), imported object(%p)\n",
	   DGC_get_my_ID(), pointer, imported_obj);
  } else {
    /* POINTER is a pointer to a stub; dereference it (for processor 1) */
    DGC_PE home;
    DGC_uid uid;

    home = DGC_get_home(pointer);	/* where is the object? */
    DGC_export(pointer, &uid);		/* export POINTER */

    DGC_send_msg(home, &uid, sizeof(DGC_uid));	/* send UID to object's home */
  }
}
