Drastic simplification of TCharm's startup sequence:
authorOrion Lawlor <olawlor@acm.org>
Fri, 27 Jun 2003 19:02:20 +0000 (19:02 +0000)
committerOrion Lawlor <olawlor@acm.org>
Fri, 27 Jun 2003 19:02:20 +0000 (19:02 +0000)
now set up TCharm libraries from *within* the threads,
via a user-callable "init" routine; instead of from
*outside* the threads in the old "Attach" routine.

This has a number of benefits:
0.) Library interface is much simpler:
tcharm's error-prone "addClient" and "ready"
calls are no longer needed.

1.) Threads are now completely self-contained,
rather than having a separate "setup" phase where
attach routines are called.  This dramatically
simplifies big programs like the CSAR codes.

2.) Startup sequence now matches MPI-- in fact,
many libraries could now be written directly to
(A)MPI, increasing portability.

3.) Startup "cookie" and "coordinator" are no
longer needed, simplifying the interface and
making checkpointing possible.

src/libs/ck-libs/tcharm/Makefile
src/libs/ck-libs/tcharm/compat_fus.c
src/libs/ck-libs/tcharm/compat_us.c
src/libs/ck-libs/tcharm/libmoduletcharmmain.dep [new file with mode: 0644]
src/libs/ck-libs/tcharm/tcharm.C
src/libs/ck-libs/tcharm/tcharm.ci
src/libs/ck-libs/tcharm/tcharm.h
src/libs/ck-libs/tcharm/tcharm_impl.h
src/libs/ck-libs/tcharm/tcharmc.h
src/libs/ck-libs/tcharm/tcharmmain.C [new file with mode: 0644]
src/libs/ck-libs/tcharm/tcharmmain.ci [new file with mode: 0644]

index 0315691ac15bc14f46cd7607b5c9edbb1f2137ca..ca79026c0006267ed4ccc4955adc2df394ea0327 100644 (file)
@@ -11,12 +11,20 @@ LIB=libmoduletcharm
 DEST=$(LIBDIR)/$(LIB).a
 COMPATLIB=$(LIBDIR)/libtcharm-compat.a
 
-all: $(DEST) $(COMPATLIB)
+OBJS2=tcharmmain.o
+LIB2=libmoduletcharmmain
+DEST2=$(LIBDIR)/$(LIB2).a
+
+all: $(DEST) $(COMPATLIB) $(DEST2)
 
 $(DEST): $(OBJS) headers
        $(CHARMC) $(OBJS) -o $@
        cp $(LIB).dep $(LIBDIR)/$(LIB).dep
 
+$(DEST2): $(OBJS2) headers
+       $(CHARMC) $(OBJS2) -o $@
+       cp $(LIB2).dep $(LIBDIR)/$(LIB2).dep
+
 $(COMPATLIB): $(COMPAT) 
        $(CHARMC) $(COMPAT) -o $@
 
@@ -38,5 +46,11 @@ tcharm.o: tcharm.C $(HEADERS)
 tcharm.decl.h: tcharm.ci
        $(CHARMC) tcharm.ci
 
+tcharmmain.o: tcharmmain.C tcharmmain.decl.h $(HEADERS)
+       $(CHARMC) -c tcharmmain.C
+
+tcharmmain.decl.h: tcharmmain.ci
+       $(CHARMC) tcharmmain.ci
+
 clean: 
        -rm -fr *.o *~ *.decl.h *.def.h gmon.out headers $(DEST) $(COMPATLIB) conv-host charmrun
index 7f74d27fa6ec7f3056bcd01e29a1fa8cdc7a45b7..c8267c2ffd5bf59228499c200c242c74a8745893 100644 (file)
@@ -1,5 +1,5 @@
 #include "charm-api.h"
 #include "tcharmc.h"
 FDECL void FTN_NAME(TCHARM_USER_SETUP,tcharm_user_setup)(void) {
-       TCharmInDefaultSetup();
+       TCHARM_User_setup();
 }
index ef432f6d48d299ba34ee7da1d26a6dd6aa023f01..112711a8d2430e34c0248275b0c735fd91203908 100644 (file)
@@ -1,5 +1,5 @@
 #include "charm-api.h"
 #include "tcharmc.h"
 CDECL void TCHARM_User_setup(void) {
-       TCharmInDefaultSetup();
+       TCHARM_Call_fallback_setup();
 }
diff --git a/src/libs/ck-libs/tcharm/libmoduletcharmmain.dep b/src/libs/ck-libs/tcharm/libmoduletcharmmain.dep
new file mode 100644 (file)
index 0000000..4bf9e85
--- /dev/null
@@ -0,0 +1 @@
+-module tcharm
index bc31a5df38e5f885295fdc21c4ad007e9ceafab6..1cdc0d460be6f8ecce289184bc3c07ad213bd885 100644 (file)
@@ -55,22 +55,21 @@ public:
 static TCharmTraceLibList tcharm_tracelibs;
 static int tcharm_nomig=0, tcharm_nothreads=0;
 static int tcharm_stacksize=1*1024*1024; /*Default stack size is 1MB*/
+static int tcharm_initted=0;
 
 void TCharm::nodeInit(void)
 {
   CtvInitialize(TCharm *,_curTCharm);
   CtvAccess(_curTCharm)=NULL;
   CkpvInitialize(inState,_stateTCharm);
-
-  TCharm::setState(inNodeSetup);
-  TCHARM_User_node_setup();
-  FTN_NAME(TCHARM_USER_NODE_SETUP,tcharm_user_node_setup)();
   TCharm::setState(inInit);
+
+  tcharm_initted=1;
 }
 
 void TCharm::procInit(void)
 {
-  // called on every pe to east these arguments
+  // called on every pe to eat these arguments
   char **argv=CkGetArgv();
   tcharm_nomig=CmiGetArgFlagDesc(argv,"+tcharm_nomig","Disable migration support (debugging)");
   tcharm_nothreads=CmiGetArgFlagDesc(argv,"+tcharm_nothread","Disable thread support (debugging)");
@@ -84,6 +83,10 @@ void TCharm::procInit(void)
        while (CmiGetArgIntDesc(argv,"-vp",&ignored,NULL)) {}
        while (CmiGetArgIntDesc(argv,"+vp",&ignored,NULL)) {}
   }
+  if (CkMyPe()==0) { // Echo various debugging options:
+    if (tcharm_nomig) CmiPrintf("TCHARM> Disabling migration support, for debugging\n");
+    if (tcharm_nothreads) CmiPrintf("TCHARM> Disabling thread support, for debugging\n");
+  }
 }
 
 void TCHARM_Api_trace(const char *routineName,const char *libraryName)
@@ -119,9 +122,9 @@ TCharm::TCharm(TCharmInitMsg *initMsg_)
   else /*Create a thread normally*/
   {
     if (tcharm_nomig) { /*Nonmigratable version, for debugging*/
-      tid=CthCreate((CthVoidFn)startTCharmThread,initMsg,initMsg->stackSize);
+      tid=CthCreate((CthVoidFn)startTCharmThread,initMsg,initMsg->opts.stackSize);
     } else {
-      tid=CthCreateMigratable((CthVoidFn)startTCharmThread,initMsg,initMsg->stackSize);
+      tid=CthCreateMigratable((CthVoidFn)startTCharmThread,initMsg,initMsg->opts.stackSize);
     }
 #if CMK_BLUEGENE_CHARM
     BgAttach(tid);
@@ -131,13 +134,14 @@ TCharm::TCharm(TCharmInitMsg *initMsg_)
   TCharm::setState(inInit);
   isStopped=true;
   resumeAfterMigration=false;
+  exitWhenDone=initMsg->opts.exitWhenDone;
   threadInfo.tProxy=CProxy_TCharm(thisArrayID);
   threadInfo.thisElement=thisIndex;
   threadInfo.numElements=initMsg->numElements;
   heapBlocks=CmiIsomallocBlockListNew();
   nUd=0;
   usesAtSync=CmiTrue;
-  ready();
+  run();
 }
 
 TCharm::TCharm(CkMigrateMessage *msg)
@@ -145,7 +149,6 @@ TCharm::TCharm(CkMigrateMessage *msg)
 {
   initMsg=NULL;
   tid=NULL;
-  resumeAfterMigration=false;
   threadInfo.tProxy=CProxy_TCharm(thisArrayID);
 }
 
@@ -153,7 +156,7 @@ void TCharm::pup(PUP::er &p) {
 //Pup superclass
   ArrayElement1D::pup(p);
 
-  p(isStopped); p(resumeAfterMigration);
+  p(isStopped); p(resumeAfterMigration); p(exitWhenDone);
   p(threadInfo.thisElement);
   p(threadInfo.numElements);
   
@@ -340,22 +343,6 @@ void TCharm::check(void)
 }
 #endif
 
-static int propMapCreated=0;
-static CkGroupID propMapID;
-CkGroupID CkCreatePropMap(void);
-
-static void TCHARM_Build_threads(TCharmInitMsg *msg,TCharmSetupCookie &cook)
-{
-       CkArrayOptions opts(msg->numElements);
-       if (!propMapCreated) {
-               propMapCreated=1;
-               propMapID=CkCreatePropMap();
-       }
-       opts.setMap(propMapID);
-       int nElem=msg->numElements; //<- save it because msg will be deleted.
-       CkArrayID id=CProxy_TCharm::ckNew(msg,opts);
-       cook.setThreads(id,nElem);
-}
 
 /****** TcharmClient ******/
 void TCharmClient1D::ckJustMigrated(void) {
@@ -377,40 +364,99 @@ CkArrayID TCHARM_Get_threads(void) {
 }
 
 /****** Readonlys *****/
-CkVec<TCpupReadonlyGlobal> TCharmReadonlys::entries;
+static int tcharm_readonlygroup_created=0;
+static TCharmReadonlys initial_readonlies;
+CProxy_TCharmReadonlyGroup tcharm_readonlygroup;
+
+class TCharmReadonlyGroup : public CBase_TCharmReadonlyGroup {
+public:
+       TCharmReadonlys all;
+       
+       TCharmReadonlyGroup(TCharmReadonlys &r,int len,const char *data)
+       {
+               add(r,len,data);
+       }
+       
+       void add(TCharmReadonlys &r,int len,const char *data) {
+               // Unpack these readonlies:
+               PUP::fromMem p(data);
+               r.pupData(p);
+               // Add to our list:
+               all.add(r);
+       }
+       
+       void pup(PUP::er &p) {
+               all.pup(p);
+               all.pupData(p);
+       }
+};
+
+// Send out this set of readonlies to the readonly group:
+static void send_readonlies(TCharmReadonlys &r) {
+       int len; {PUP::sizer p; r.pupData(p); len=p.size();}
+       char *data=new char[len];
+       {PUP::toMem p(data); r.pupData(p);}
+       tcharm_readonlygroup.add(r,len,data);
+       delete[] data;
+}
+
+class TCharmReadonlyMain : public CBase_TCharmReadonlyMain {
+public:
+    TCharmReadonlyMain(void) {
+       TCharmReadonlys &r=initial_readonlies;
+       int len; {PUP::sizer p; r.pupData(p); len=p.size();}
+       char *data=new char[len];
+       {PUP::toMem p(data); r.pupData(p);}
+        tcharm_readonlygroup=CProxy_TCharmReadonlyGroup::ckNew(r,len,data);
+       delete[] data;
+       tcharm_readonlygroup_created=1;
+    }
+};
+
 void TCharmReadonlys::add(TCpupReadonlyGlobal fn)
 {
        entries.push_back(fn);
 }
+void TCharmReadonlys::add(const TCharmReadonlys &r) {
+       for (unsigned int i=0;i<r.entries.size();i++)
+               entries.push_back(r.entries[i]);
+}
 
-//Pups all registered readonlys
-void TCharmReadonlys::pupAllReadonlys(PUP::er &p) {
-       //Pup the globals for this node:
-       int i,n=entries.length();
-       p.comment("TCharm Readonly global variables:");
-       p(n);
-       if (n!=entries.length())
-               CkAbort("TCharmReadonly list length mismatch!\n");
-       for (i=0;i<n;i++)
+//Pup the readonly *functions* (for shipping)
+void TCharmReadonlys::pup(PUP::er &p) {
+       p|entries;
+}
+
+//Pups the readonly *data*
+void TCharmReadonlys::pupData(PUP::er &p) {
+       for (unsigned int i=0;i<entries.size();i++)
                (entries[i])((pup_er)&p);
 }
 
-void TCharmReadonlys::pup(PUP::er &p) {
-       if (p.isUnpacking()) {
-               //HACK: Rather than sending this message only where its needed,
-               // we send it everywhere and just ignore it if it's not needed.
-               if (CkMyPe()==0) return; //Processor 0 is the source-- no unpacking needed
-               if (CkMyRank()!=0) return; //Some other processor will do the unpacking
-       }
-       pupAllReadonlys(p);
+//Pups all readonly data registered so far.
+void TCharmReadonlys::pupAll(PUP::er &p) {
+       if (!tcharm_readonlygroup_created)
+               CkAbort("TCharmReadonlys::pupAll can only be called after the TCHARM main");
+       TCharmReadonlys &all=tcharm_readonlygroup.ckLocalBranch()->all;
+       int n=all.size();
+       p|n;
+       if (n!=all.size())
+               CkAbort("TCharmReadonly list length mismatch!\n");
+       all.pupData(p);
 }
 
 CDECL void TCHARM_Readonly_globals(TCpupReadonlyGlobal fn)
 {
        TCHARMAPI("TCHARM_Readonly_globals");
-       if (TCharm::getState()!=inNodeSetup)
-               CkAbort("Can only call TCHARM_ReadonlyGlobals from in TCHARM_UserNodeSetup!\n");
-       TCharmReadonlys::add(fn);
+       if (!tcharm_readonlygroup_created) 
+       { // Readonly message hasn't gone out yet: just add to list
+               initial_readonlies.add(fn);
+       } 
+       else /* tcharm_readonlygroup_created */
+       { // Late addition: Broadcast our copy of the readonly data:
+               TCharmReadonlys r; r.add(fn);
+               send_readonlies(r);
+       }
 }
 FDECL void FTN_NAME(TCHARM_READONLY_GLOBALS,tcharm_readonly_globals)
        (TCpupReadonlyGlobal fn)
@@ -418,218 +464,102 @@ FDECL void FTN_NAME(TCHARM_READONLY_GLOBALS,tcharm_readonly_globals)
        TCHARM_Readonly_globals(fn);
 }
 
-/************* Startup/Shutdown Coordination Support ************/
 
-enum {TC_READY=23, TC_BARRIER=87, TC_DONE=42};
+/************* Startup/Shutdown Coordination Support ************/
 
-//Called when a client is ready to run
-void TCharm::ready(void) {
-       DBG("TCharm thread "<<thisIndex<<" ready")
-       int vals[2]={0,1};
-       if (thisIndex==0) vals[0]=TC_READY;
-       //Contribute to a synchronizing reduction
-       contribute(sizeof(vals),&vals,CkReduction::sum_int);
-}
+// Useless values to reduce over:
+int vals[2]={0,1};
 
 //Called when we want to go to a barrier
 void TCharm::barrier(void) {
-       int vals[2]={0,1};
-       if (thisIndex==0) vals[0]=TC_BARRIER;
        //Contribute to a synchronizing reduction
-       contribute(sizeof(vals),&vals,CkReduction::sum_int);
+       CkCallback cb(index_t::atBarrier(0), thisProxy[0]);
+       contribute(sizeof(vals),&vals,CkReduction::sum_int,cb);
        stop();
 }
 
+//Called when we've reached the barrier
+void TCharm::atBarrier(CkReductionMsg *m) {
+       DBGX("clients all at barrier");
+       delete m;
+       thisProxy.run(); //Just restart everybody
+}
+
 //Called when the thread is done running
 void TCharm::done(void) {
        DBG("TCharm thread "<<thisIndex<<" done")
-       int vals[2]={0,1};
-       if (thisIndex==0) vals[0]=TC_DONE;
-       //Contribute to a synchronizing reduction
-       contribute(sizeof(vals),&vals,CkReduction::sum_int);
+       if (exitWhenDone) {
+               //Contribute to a synchronizing reduction
+               CkCallback cb(index_t::atExit(0), thisProxy[0]);
+               contribute(sizeof(vals),&vals,CkReduction::sum_int,cb);
+       }
        stop();
 }
-
-//Called when an array reduction is complete
-static void coordinatorReduction(void *coord_,int dataLen,void *reductionData)
-{
-       TCharmCoordinator *coord=(TCharmCoordinator *)coord_;
-       int *vals=(int *)reductionData;
-       if (dataLen!=2*sizeof(int))
-               CkAbort("Unexpected length in TCharm array reduction!\n");
-       DBGX("Finished coordinator reduction: "<<vals[0]<<", "<<vals[1]);
-       switch (vals[0]) {
-       case TC_READY: coord->clientReady(); break;
-       case TC_BARRIER: coord->clientBarrier(); break;
-       case TC_DONE: coord->clientDone(); break;
-       default:
-               CkAbort("Unexpected value from TCharm array reduction!\n");
-       };
+//Called when all threads are done running
+void TCharm::atExit(CkReductionMsg *m) {
+       DBGX("TCharm::atExit> exiting");
+       delete m;
+       CkExit();
 }
 
-int TCharmCoordinator::nArrays=0; //Total number of running thread arrays
-TCharmCoordinator *TCharmCoordinator::head=NULL; //List of coordinators
-
-
-TCharmCoordinator::TCharmCoordinator(CkArrayID threads_,int nThreads_)
-       :threads(threads_), nThreads(nThreads_), nClients(0), nReady(0)
-{
-       nArrays++;
-       //Link into the coordinator list
-       next=head;
-       head=this;
-
-       threads.setReductionClient(coordinatorReduction,this);
-       nClients=1; //Thread array itself is a client
-}
-TCharmCoordinator::~TCharmCoordinator()
-{
-       //Coordinators never get deleted
-}
-void TCharmCoordinator::addClient(const CkArrayID &client)
-{
-       nClients++;
-}
-void TCharmCoordinator::clientReady(void)
-{
-       DBGX("client "<<nReady+1<<" of "<<nClients<<" ready");
-       nReady++;
-       if (nReady>=nClients) { //All client arrays are ready-- start threads
-               DBGX("starting threads");
-               threads.run();
-       }
-}
-void TCharmCoordinator::clientBarrier(void)
-{
-       DBGX("clients all at barrier");
-       threads.run();
-}
-void TCharmCoordinator::clientDone(void)
-{
-       DBGX("clientDone");     
-       nArrays--;
-       if (nArrays<=0) { //All arrays have exited
-               DBGX("done with computation");
-               CkExit();
-       }
-}
 
 /************* Setup **************/
 
-//Cookie used during setup
-TCharmSetupCookie *TCharmSetupCookie::theCookie;
-
 //Globals used to control setup process
-static int g_numDefaultSetups=0;
 static TCHARM_Fallback_setup_fn g_fallbackSetup=NULL;
 void TCHARM_Set_fallback_setup(TCHARM_Fallback_setup_fn f)
 {
        g_fallbackSetup=f;
 }
-CDECL void TCharmInDefaultSetup(void) {
-       g_numDefaultSetups++;
+void TCHARM_Call_fallback_setup(void) {
+       if (g_fallbackSetup) 
+               (g_fallbackSetup)();
+       else
+               CkAbort("TCHARM: Unexpected fallback setup--missing TCHARM_User_setup routine?");
 }
 
-//Tiny simple main chare
-class TCharmMain : public Chare {
-public:
-  TCharmMain(CkArgMsg *msg) {
-    if (0!=tcharm_nomig)
-        CmiPrintf("TCHARM> Disabling migration support, for debugging\n");
-    if (0!=tcharm_nothreads)
-       CmiPrintf("TCHARM> Disabling thread support, for debugging\n");
-
-    TCharmSetupCookie cookie(msg->argv);
-    TCharmSetupCookie::theCookie=&cookie;
-    g_numDefaultSetups=0;
-    
-    /*Call user-overridable C setup*/
-    TCHARM_User_setup();
-    /*Call user-overridable Fortran setup*/
-    FTN_NAME(TCHARM_USER_SETUP,tcharm_user_setup)();
-    
-    if (g_numDefaultSetups==2) 
-    { //User didn't override either setup routine
-           if (g_fallbackSetup)
-                   (g_fallbackSetup)();
-           else
-                   CmiAbort("You need to override TCharmUserSetup to start your computation, or else link in a framework module\n");
-    }      
-    
-    delete msg;
-    
-    if (0==TCharmCoordinator::getTotal())
-           CkAbort("You didn't create any TCharm arrays in TCharmUserSetup!\n");
-
-    //Send out the readonly globals:
-    TCharmReadonlys r;
-    CProxy_TCharmReadonlyGroup::ckNew(r);
-  }
-};
-
-#ifndef CMK_OPTIMIZE
-/*The setup cookie, used to store global initialization state*/
-TCharmSetupCookie &TCharmSetupCookie::check(void)
-{
-       if (magic!=correctMagic)
-               CkAbort("TCharm setup cookie is damaged!\n");
-       return *this;
-}
-#endif
+/************** User API ***************/
+/**********************************
+Callable from UserSetup:
+*/
 
-void TCharmSetupCookie::setThreads(const CkArrayID &aid,int nel)
+// Read the command line to figure out how many threads to create:
+CDECL int TCHARM_Get_num_chunks(void)
 {
-       coord=new TCharmCoordinator(aid,nel);
-       tc=aid; numElements=nel;
+       TCHARMAPI("TCHARM_Get_num_chunks");
+       if (CkMyPe()!=0) CkAbort("TCHARM_Get_num_chunks should only be called on PE 0 during setup!");
+       int nChunks=CkNumPes();
+       char **argv=CkGetArgv();
+       CmiGetArgIntDesc(argv,"-vp",&nChunks,"Set the total number of virtual processors");
+       CmiGetArgIntDesc(argv,"+vp",&nChunks,NULL);
+       lastNumChunks=nChunks;
+       return nChunks;
 }
-
-TCharmSetupCookie::TCharmSetupCookie(char **argv_)
+FDECL int FTN_NAME(TCHARM_GET_NUM_CHUNKS,tcharm_get_num_chunks)(void)
 {
-       magic=correctMagic;
-       argv=argv_;
-       coord=NULL;
-       stackSize=tcharm_stacksize;
+       return TCHARM_Get_num_chunks();
 }
 
-CkArrayOptions TCHARM_Attach_start(CkArrayID *retTCharmArray,int *retNumElts)
-{
-       TCharmSetupCookie *tc=TCharmSetupCookie::get();
-       if (!tc->hasThreads())
-               CkAbort("You must create a thread array with TCharmCreate before calling Attach!\n");
-       int nElts=tc->getNumElements();
-       if (retNumElts!=NULL) *retNumElts=nElts;
-       *retTCharmArray=tc->getThreads();
-       CkArrayOptions opts(nElts);
-       opts.bindTo(tc->getThreads());
-       return opts;
-}
-void TCHARM_Attach_finish(const CkArrayID &libraryArray)
+// Fill out the default thread options:
+TCHARM_Thread_options::TCHARM_Thread_options(int doDefault)
 {
-       TCharmSetupCookie *tc=TCharmSetupCookie::get();
-       tc->addClient(libraryArray);
+       stackSize=tcharm_stacksize; /* default stacksize */
+       exitWhenDone=0; /* don't exit when done by default. */
 }
 
-
-/************** User API ***************/
-
-#define cookie (*TCharmSetupCookie::get())
-
-/**********************************
-Callable from UserSetup:
-*/
+TCHARM_Thread_options g_tcharmOptions(1);
 
 /*Set the size of the thread stack*/
 CDECL void TCHARM_Set_stack_size(int newStackSize)
 {
        TCHARMAPI("TCHARM_Set_stack_size");
-       if (TCharm::getState()!=inInit)
-               CkAbort("TCharm> Can only set stack size from in init!\n");
-       cookie.setStackSize(newStackSize);
+       g_tcharmOptions.stackSize=newStackSize;
 }
 FDECL void FTN_NAME(TCHARM_SET_STACK_SIZE,tcharm_set_stack_size)
        (int *newSize)
 { TCHARM_Set_stack_size(*newSize); }
 
+CDECL void TCHARM_Set_exit(void) { g_tcharmOptions.exitWhenDone=1; }
 
 /*Create a new array of threads, which will be bound to by subsequent libraries*/
 CDECL void TCHARM_Create(int nThreads,
@@ -643,6 +573,7 @@ FDECL void FTN_NAME(TCHARM_CREATE,tcharm_create)
        (int *nThreads,TCHARM_Thread_start_fn threadFn)
 { TCHARM_Create(*nThreads,threadFn); }
 
+static CProxy_TCharm TCHARM_Build_threads(TCharmInitMsg *msg);
 
 /*As above, but pass along (arbitrary) data to threads*/
 CDECL void TCHARM_Create_data(int nThreads,
@@ -652,12 +583,14 @@ CDECL void TCHARM_Create_data(int nThreads,
        TCHARMAPI("TCHARM_Create_data");
        if (TCharm::getState()!=inInit)
                CkAbort("TCharm> Can only create threads from in init!\n");
-       TCharmSetupCookie &cook=cookie;
        TCharmInitMsg *msg=new (threadDataLen,0) TCharmInitMsg(
-               (CthVoidFn)threadFn,cook.getStackSize());
+               (CthVoidFn)threadFn,g_tcharmOptions);
        msg->numElements=nThreads;
        memcpy(msg->data,threadData,threadDataLen);
-       TCHARM_Build_threads(msg,cook);
+       TCHARM_Build_threads(msg);
+       
+       // Reset the thread options:
+       g_tcharmOptions=TCHARM_Thread_options(1);
 }
 
 FDECL void FTN_NAME(TCHARM_CREATE_DATA,tcharm_create_data)
@@ -666,23 +599,40 @@ FDECL void FTN_NAME(TCHARM_CREATE_DATA,tcharm_create_data)
                  void *threadData,int *threadDataLen)
 { TCHARM_Create_data(*nThreads,threadFn,threadData,*threadDataLen); }
 
+static int propMapCreated=0;
+static CkGroupID propMapID;
+CkGroupID CkCreatePropMap(void);
 
-CDECL int TCHARM_Get_num_chunks(void)
+static CProxy_TCharm TCHARM_Build_threads(TCharmInitMsg *msg)
 {
-       TCHARMAPI("TCHARM_Get_num_chunks");
-       if (CkMyPe()!=0) CkAbort("TCHARM_Get_num_chunks should only be called on PE 0 during setup!");
-       int nChunks=CkNumPes();
-       char **argv=CkGetArgv();
-       CmiGetArgIntDesc(argv,"-vp",&nChunks,"Set the total number of virtual processors");
-       CmiGetArgIntDesc(argv,"+vp",&nChunks,NULL);
-       lastNumChunks=nChunks;
-       return nChunks;
+       CkArrayOptions opts(msg->numElements);
+       if (!propMapCreated) {
+               propMapCreated=1;
+               propMapID=CkCreatePropMap();
+       }
+       opts.setMap(propMapID);
+       int nElem=msg->numElements; //<- save it because msg will be deleted.
+       return CProxy_TCharm::ckNew(msg,opts);
 }
-FDECL int FTN_NAME(TCHARM_GET_NUM_CHUNKS,tcharm_get_num_chunks)(void)
+
+// Helper used when creating a new array bound to the TCHARM threads:
+CkArrayOptions TCHARM_Attach_start(CkArrayID *retTCharmArray,int *retNumElts)
 {
-       return TCHARM_Get_num_chunks();
+       TCharm *tc=TCharm::get();
+       if (!tc)
+               CkAbort("You must call TCHARM initialization routines from a TCHARM thread!");
+       int nElts=tc->getNumElements();
+       if (retNumElts!=NULL) *retNumElts=nElts;
+       *retTCharmArray=tc->getProxy();
+       CkArrayOptions opts(nElts);
+       opts.bindTo(tc->getProxy());
+       return opts;
 }
 
+void TCHARM_Suspend(void) {
+       TCharm *tc=TCharm::get();
+       tc->suspend();
+}
 
 /***********************************
 Callable from worker thread
@@ -833,19 +783,28 @@ FDECL void FTN_NAME(TCHARM_GETARG,tcharm_getarg)
 //These silly routines are used for serial startup:
 extern void _initCharm(int argc, char **argv);
 CDECL void TCHARM_Init(int *argc,char ***argv) {
-       ConverseInit(*argc, *argv, (CmiStartFn) _initCharm,1,1);
-       _initCharm(*argc,*argv);
+       if (!tcharm_initted) {
+         ConverseInit(*argc, *argv, (CmiStartFn) _initCharm,1,1);
+         _initCharm(*argc,*argv);
+       }
 }
 
 FDECL void FTN_NAME(TCHARM_INIT,tcharm_init)(void)
 {
        int argc=1;
-       char *argv[2]={"foo",NULL};
-       ConverseInit(argc,argv, (CmiStartFn) _initCharm,1,1);
-       _initCharm(argc,argv);
+       char *argv_sto[2]={"foo",NULL};
+       char **argv=argv_sto;
+       TCHARM_Init(&argc,&argv);
 }
 
-// TCHARM Semaphores
+/***********************************
+* TCHARM Semaphores:
+* The idea is one side "puts", the other side "gets"; 
+* but the calls can come in any order--
+* if the "get" comes first, it blocks until the put.
+* This makes a convenient, race-condition-free way to do
+* onetime initializations.  
+*/
 /// Find this semaphore, or insert if there isn't one:
 TCharm::TCharmSemaphore *TCharm::findSema(int id) {
        for (int s=0;s<sema.size();s++)
@@ -866,33 +825,54 @@ void TCharm::freeSema(TCharmSemaphore *doomed) {
        CkAbort("Tried to free nonexistent TCharm semaphore");
 }
 
+/// Block until this semaphore has data:
+TCharm::TCharmSemaphore *TCharm::getSema(int id) {
+       TCharmSemaphore *s=findSema(id);
+       if (s->data==NULL) 
+       { //Semaphore isn't filled yet: wait until it is
+               s->thread=CthSelf();
+               suspend(); //Will be woken by semaPut
+               // Semaphore may have moved-- find it again
+               s=findSema(id);
+               if (s->data==NULL) CkAbort("TCharm::semaGet awoken too early!");
+       }
+       return s;
+}
+
 /// Store data at the semaphore "id".
 ///  The put can come before or after the get.
 void TCharm::semaPut(int id,void *data) {
        TCharmSemaphore *s=findSema(id);
        if (s->data!=NULL) CkAbort("Duplicate calls to TCharm::semaPut!");
        s->data=data;
-       if (s->thread!=NULL) //Awaken the thread
+       if (s->thread!=NULL) {//Awaken the thread
+               s->thread=NULL;
                resume();
+       }
 }
 
 /// Retreive data from the semaphore "id".
 ///  Blocks if the data is not immediately available.
 ///  Consumes the data, so another put will be required for the next get.
 void *TCharm::semaGet(int id) {
-       TCharmSemaphore *s=findSema(id);
-       if (s->data==NULL) 
-       { //Semaphore isn't filled yet: wait until it is
-               s->thread=CthSelf();
-               suspend(); //Will be woken by semaPut
-               // Semaphore may have moved-- find it again
-               s=findSema(id);
-               if (s->data==NULL) CkAbort("TCharm::semaGet awoken too early!");
-       }
+       TCharmSemaphore *s=getSema(id);
        void *ret=s->data;
        // Now remove the semaphore from the list:
        freeSema(s);
        return ret;
 }
 
+/// Retreive data from the semaphore "id".
+///  Blocks if the data is not immediately available.
+void *TCharm::semaGets(int id) {
+       TCharmSemaphore *s=getSema(id);
+       return s->data;
+}
+
+/// Retreive data from the semaphore "id", or returns NULL.
+void *TCharm::semaPeek(int id) {
+       TCharmSemaphore *s=findSema(id);
+       return s->data;
+}
+
 #include "tcharm.def.h"
index 5a500e3b5020ae44a339ee54244e1b57cae5516e..17696ca3a8cdf9591d5b2c2bad42bb768f093d6a 100644 (file)
@@ -5,14 +5,19 @@ module tcharm {
   array [1D] TCharm {
     entry TCharm(TCharmInitMsg *initMsg);
     entry void run(void);
+    entry void atBarrier(CkReductionMsg *);
+    entry void atExit(CkReductionMsg *);
     entry void migrateDelayed(int destPE);
     initproc void procInit(void);
     initnode void nodeInit(void);
   };
-  mainchare TCharmMain {
-    entry TCharmMain();
-  };
+  
+  readonly CProxy_TCharmReadonlyGroup tcharm_readonlygroup;
   group TCharmReadonlyGroup {
-    entry void TCharmReadonlyGroup(TCharmReadonlys r);
+    entry TCharmReadonlyGroup(TCharmReadonlys r,int len,char data[len]);
+    entry void add(TCharmReadonlys r,int len,char data[len]);
+  };
+  mainchare TCharmReadonlyMain {
+    entry TCharmReadonlyMain(void);
   };
 };
index 28c18121c8cc9cc6869815d4a2b94ae2fd8fa8e2..baeb6277c413474dd14c8d9cd068b2fa03e006f7 100644 (file)
@@ -16,37 +16,39 @@ Library "fallback setup" routine.
 From an initcall, you register one of your routines 
 here in case the user doesn't write a TCHARM_User_setup
 routine.  Your fallback version sets up (only) your 
-library by creating some TCharm threads and attaching to them,
-like:
+library by creating some TCharm threads with the appropriate
+"start running" routine, like this:
 
 void myLibFallbackSetupFn(void) {
        TCHARM_Create(TCHARM_Get_num_chunks(),myLibUserStart);
-        myLib_Attach();
 }
+
+In case of multiple fallback setups, the last one wins.
 */
 typedef void (*TCHARM_Fallback_setup_fn)(void);
 void TCHARM_Set_fallback_setup(TCHARM_Fallback_setup_fn f);
 
-
 /*
-These two routines need to be called from your Attach routine.
-The "start" call finds the last-created set of tcharm threads,
+This "start" call finds the currently running set of tcharm threads,
 copies their arrayID into retTCharmArray, and returns an 
-opts object bound the the tcharm threads.  You pass this opts
-object to your array's ckNew.
-The "finish" call registers your newly-created array as a
-client of this set of TCharm threads.
+opts object bound the the tcharm threads.  In your library
+"Init" routine, you normally pass this opts object to your 
+array's ckNew, then block until the startup is complete 
+(often using TCharm::semaGet).
 */
 CkArrayOptions TCHARM_Attach_start(CkArrayID *retTCharmArray,int *retNumElts=0);
-void TCHARM_Attach_finish(const CkArrayID &libraryArray);
 
+/* Get the currently running TCharm threads: */
+CkArrayID TCHARM_Get_threads(void);
+
+/// Suspend the current thread.  Resume by calling thread->resume().
+void TCHARM_Suspend(void);
 
 /*
 A simple client array that can be bound to a tcharm array.
 You write a TCharm library by inheriting your array from this class.
 You'll have to pass the TCharm arrayID to our constructor, and
 then call tcharmClientInit().
-As soon as your startup work is done, you MUST call "thread->ready();"
 */
 class TCharmClient1D : public ArrayElement1D {
   CProxy_TCharm threadProxy; //Proxy for our bound TCharm array
@@ -92,10 +94,10 @@ class TCharmClient1D : public ArrayElement1D {
 /*
 Library API Calls.  The pattern:
   TCHARM_API_TRACE("myRoutineName","myLibName");
-MUST be put at the start of every
-user-callable library routine.  The string name is
-used for debugging printouts, and eventually for
-tracing (once tracing is generalized).
+MUST be put at the start of every user-callable library 
+routine, to turn off isomalloc'd heaps before jumping
+into regular Charm.  The string routineName is
+used for debugging printouts, with "+tcharm_trace myLibName".
 */
 #ifndef CMK_OPTIMIZE
 #  define TCHARM_API_TRACE(routineName,libraryName) \
@@ -108,7 +110,4 @@ tracing (once tracing is generalized).
 void TCHARM_Api_trace(const char *routineName,const char *libraryName);
 
 
-/* Get the currently running TCharm threads: */
-CkArrayID TCHARM_Get_threads(void);
-
 #endif /*def(thisHeader)*/
index 183bdbbe41ccd98f0509580830ae6c5a37fac761..5feecdde48fd5bbd7bc37ec0ab9871ce52cf5c72 100644 (file)
@@ -17,17 +17,25 @@ Orion Sky Lawlor, olawlor@acm.org, 11/19/2001
 #include "cklists.h"
 #include "memory-isomalloc.h"
 
+PUPbytes(TCpupReadonlyGlobal);
+
 //User's readonly global variables, set exactly once after initialization
 class TCharmReadonlys {
-       //There's only one (shared) list per node, so no CpvAccess here
-       static CkVec<TCpupReadonlyGlobal> entries;
+       CkVec<TCpupReadonlyGlobal> entries;
  public:
-       static void add(TCpupReadonlyGlobal fn);
-       //Pups all registered readonlys 
-       static void pupAllReadonlys(PUP::er &p);
-
-       //Used by parameter marshalling:
+       void add(TCpupReadonlyGlobal fn);
+       void add(const TCharmReadonlys &r);
+       
+       inline int size(void) const {return entries.size();}
+       
+       //Pup the readonly *functions* (for shipping)
        void pup(PUP::er &p);
+
+       //Pups the readonly *data*
+       void pupData(PUP::er &p);
+       
+       //Pups all registered readonlys 
+       static void pupAll(PUP::er &p);
 };
 PUPmarshall(TCharmReadonlys);
 
@@ -35,27 +43,33 @@ class TCharmTraceLibList;
 
 #include "tcharm.decl.h"
 
-class TCharmReadonlyGroup : public Group {
- public:
-       //Just unpacking the parameter is enough:
-       TCharmReadonlyGroup(const TCharmReadonlys &r) { /*nothing needed*/ }
-};
-
 class TCharm;
 
+// This little class holds values between a call to TCHARM_Set_* 
+//   and the subsequent TCHARM_Create_*.  It should be moved
+//   into a parameter to TCHARM_Create.
+class TCHARM_Thread_options {
+public:
+       int stackSize; /* size of thread execution stack, in bytes */
+       int exitWhenDone; /* flag: call CkExit when thread is finished. */
+       // Fill out the default thread options:
+       TCHARM_Thread_options(int doDefault);
+       TCHARM_Thread_options() {}
+};
+
 class TCharmInitMsg : public CMessage_TCharmInitMsg {
  public:
        //Function to start thread with:
        CthVoidFn threadFn;
-       //Initial stack size, in bytes:
-       int stackSize;
+       //Initial thread parameters:
+       TCHARM_Thread_options opts;
        //Array size (number of elements)
        int numElements;
        //Data to pass to thread:
        char *data;
 
-       TCharmInitMsg(CthVoidFn threadFn_,int stackSize_)
-               :threadFn(threadFn_), stackSize(stackSize_) {}
+       TCharmInitMsg(CthVoidFn threadFn_,const TCHARM_Thread_options &opts_)
+               :threadFn(threadFn_), opts(opts_) {}
 };
 
 //Current computation location
@@ -72,7 +86,7 @@ class TCharm: public CBase_TCharm
 {
  public:
 
-       //User's heap-allocated/global data:
+//User's heap-allocated/global data:
        class UserData {
                void *data; //user data pointer
                bool isC;
@@ -92,7 +106,7 @@ class TCharm: public CBase_TCharm
        //New interface for user data:
        CkVec<UserData> sud;
        
-       //Tiny semaphore-like pointer producer/consumer
+//Tiny semaphore-like pointer producer/consumer
        class TCharmSemaphore {
        public:
                int id; //User-defined identifier
@@ -105,17 +119,26 @@ class TCharm: public CBase_TCharm
        /// Short, unordered list of waiting semaphores.
        CkVec<TCharmSemaphore> sema;
        TCharmSemaphore *findSema(int id);
+       TCharmSemaphore *getSema(int id);
        void freeSema(TCharmSemaphore *);
        
        /// Store data at the semaphore "id".
        ///  The put can come before or after the get.
        void semaPut(int id,void *data);
+
+       /// Retreive data from the semaphore "id", returning NULL if not there.
+       void *semaPeek(int id);
+       
+       /// Retreive data from the semaphore "id".
+       ///  Blocks if the data is not immediately available.
+       void *semaGets(int id);
+       
        /// Retreive data from the semaphore "id".
        ///  Blocks if the data is not immediately available.
        ///  Consumes the data, so another put will be required for the next get.
        void *semaGet(int id);
 
-       //One-time initialization
+//One-time initialization
        static void nodeInit(void);
        static void procInit(void);
  private:
@@ -132,7 +155,7 @@ class TCharm: public CBase_TCharm
        friend class TCharmAPIRoutine; //So he can get to heapBlocks:
        CmiIsomallocBlockList *heapBlocks; //Migratable heap data
 
-       bool isStopped, resumeAfterMigration;
+       bool isStopped, resumeAfterMigration, exitWhenDone;
        ThreadInfo threadInfo;
        double timeOffset; //Value to add to CkWallTimer to get my clock
 
@@ -156,6 +179,8 @@ class TCharm: public CBase_TCharm
        
        virtual void ckJustMigrated(void);
        void migrateDelayed(int destPE);
+       void atBarrier(CkReductionMsg *);
+       void atExit(CkReductionMsg *);
 
        void clear();
 
@@ -168,9 +193,6 @@ class TCharm: public CBase_TCharm
        inline double getTimeOffset(void) const { return timeOffset; }
 
 //Client-callable routines:
-       //One client is ready to run
-       void ready(void);
-
        //Sleep till entire array is here
        void barrier(void);
        
@@ -224,69 +246,6 @@ class TCharm: public CBase_TCharm
 };
 
 
-
-//TCharm internal class: Controls array startup, ready, run and shutdown
-class TCharmCoordinator {
-       static int nArrays; //Total number of running thread arrays
-       static TCharmCoordinator *head; //List of coordinators
-
-       TCharmCoordinator *next; //Next coordinator in list
-       CProxy_TCharm threads;//The threads I coordinate
-       int nThreads;//Number of threads (array elements)
-       int nClients; //Number of bound client arrays
-       int nReady; //Number of ready clients
-public:
-       TCharmCoordinator(CkArrayID threads,int nThreads);
-       ~TCharmCoordinator();
-       void addClient(const CkArrayID &client);
-       void clientReady(void);
-       void clientBarrier(void);
-       void clientDone(void);
-       
-       static int getTotal(void) {
-               return nArrays;
-       }
-};
-
-//Controls initial setup (main::main & init routines)
-class TCharmSetupCookie {
-       enum {correctMagic=0x5432abcd};
-       int magic; //To make sure this is actually a cookie
-       
-       int stackSize; //Thread stack size, in bytes
-       char **argv; //Command-line arguments
-       
-       CkArrayID tc; //Handle to last-created TCharm array
-       int numElements; //Number of elements in last-created TCharm
-       TCharmCoordinator *coord; 
-
-       //Points to the active cookie
-       static TCharmSetupCookie *theCookie;
-       friend class TCharmMain;
- public:
-       TCharmSetupCookie(char **argv_);
-       
-#ifdef CMK_OPTIMIZE //Skip check, for speed
-       inline TCharmSetupCookie &check(void) {return *this;}
-#else
-       TCharmSetupCookie &check(void);
-#endif
-       void setStackSize(int sz) {stackSize=sz;}
-       int getStackSize(void) const {return stackSize;}
-       char **getArgv(void) {return argv;}
-       
-       bool hasThreads(void) const {return 0!=coord;}
-       const CkArrayID &getThreads(void) const {return tc;}
-       TCharmCoordinator *getCoordinator(void) {return coord;}
-       int getNumElements(void) const {return numElements;}
-
-       void addClient(const CkArrayID &client) {coord->addClient(client);}
-       
-       void setThreads(const CkArrayID &aid,int nel);
-
-       static TCharmSetupCookie *get(void) {return theCookie;}
-};
-
 //Created in all API routines: disables/enables migratable malloc
 class TCharmAPIRoutine {
  public:
index 816f54048383fb91a2cd687ba0ccfccc20d2b985..e8e18d6baa98d7a9050fee143f8c17a9bbab67c9 100644 (file)
@@ -16,6 +16,7 @@ extern "C" {
 void TCHARM_User_node_setup(void);
 void TCHARM_User_setup(void);
 
+void TCHARM_Call_fallback_setup(void);
 
 /**** Routines you can call from UserNodeSetup: ****/
 
@@ -29,6 +30,9 @@ void TCHARM_Readonly_globals(TCpupReadonlyGlobal fn);
 /*Set the size of the thread stack*/
 void TCHARM_Set_stack_size(int newStackSize);
 
+/*Exit the program when these threads are finished. */
+void TCHARM_Set_exit(void);
+
 /*Create a new array of threads, which will be bound to by subsequent libraries*/
 typedef void (*TCHARM_Thread_start_fn)(void);
 void TCHARM_Create(int nThreads,TCHARM_Thread_start_fn threadFn);
diff --git a/src/libs/ck-libs/tcharm/tcharmmain.C b/src/libs/ck-libs/tcharm/tcharmmain.C
new file mode 100644 (file)
index 0000000..f0e133c
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+Threaded Charm++ "Framework Framework" Startup:
+This file controls the startup process when writing
+to a tcharm-based language, like AMPI or the FEM framework.
+
+Note that you could also start a bunch of AMPI threads
+from a regular Charm program, which would use its own
+main instead of this one.
+
+Orion Sky Lawlor, olawlor@acm.org, 2003/6/20
+ */
+#include "tcharm_impl.h"
+#include "tcharm.h"
+#include "tcharmmain.decl.h"
+
+//Tiny simple main chare
+class TCharmMain : public Chare {
+public:
+  static void nodeInit(void) {
+    TCharm::setState(inNodeSetup);
+    TCHARM_User_node_setup();
+    FTN_NAME(TCHARM_USER_NODE_SETUP,tcharm_user_node_setup)();
+    TCharm::setState(inInit);
+  }
+  
+  
+  TCharmMain(CkArgMsg *msg) {
+    delete msg;
+    
+    TCHARM_Set_exit(); // Exit when done running these threads.
+    
+    /*Call user-overridable Fortran setup.
+      If not overridden, this will call the overridable C setup,
+      which unless overridden will call the library "fallback" setup,
+      which usually starts a bunch of TCharm threads running
+      something like "MPI_Main" (AMPI) or "driver" (FEM).
+    */
+    FTN_NAME(TCHARM_USER_SETUP,tcharm_user_setup)();
+  }
+};
+
+#include "tcharmmain.def.h"
diff --git a/src/libs/ck-libs/tcharm/tcharmmain.ci b/src/libs/ck-libs/tcharm/tcharmmain.ci
new file mode 100644 (file)
index 0000000..6267c15
--- /dev/null
@@ -0,0 +1,6 @@
+module tcharmmain {
+  mainchare TCharmMain {
+    entry TCharmMain();
+    initnode void nodeInit();
+  };
+};