libs/ck-libs/tcharm/tcharm.C

Go to the documentation of this file.
00001 /*
00002 Threaded Charm++ "Framework Framework"
00003 
00004 Orion Sky Lawlor, olawlor@acm.org, 11/19/2001
00005  */
00006 #include "tcharm_impl.h"
00007 #include "tcharm.h"
00008 #include <ctype.h>
00009 
00010 #if 0
00011     /*Many debugging statements:*/
00012 #    define DBG(x) ckout<<"["<<thisIndex<<","<<CkMyPe()<<"] TCHARM> "<<x<<endl;
00013 #    define DBGX(x) ckout<<"PE("<<CkMyPe()<<") TCHARM> "<<x<<endl;
00014 #else
00015     /*No debugging statements*/
00016 #    define DBG(x) /*empty*/
00017 #    define DBGX(x) /*empty*/
00018 #endif
00019 
00020 CtvDeclare(TCharm *,_curTCharm);
00021 
00022 static int lastNumChunks=0;
00023 
00024 class TCharmTraceLibList {
00025         enum {maxLibs=20,maxLibNameLen=15};
00026         //List of libraries we want to trace:
00027         int curLibs;
00028         char libNames[maxLibs][maxLibNameLen];
00029         int checkIfTracing(const char *lib) const
00030         {
00031                 for (int i=0;i<curLibs;i++) 
00032                         if (0==strcmp(lib,libNames[i]))
00033                                 return 1;
00034                 return 0;
00035         }
00036 public:
00037         TCharmTraceLibList() {curLibs=0;}
00038         void addTracing(const char *lib) 
00039         { //We want to trace this library-- add its name to the list.
00040                 CkPrintf("TCHARM> Will trace calls to library %s\n",lib);
00041                 int i;
00042                 for (i=0;0!=*lib;i++,lib++)
00043                         libNames[curLibs][i]=tolower(*lib);
00044                 libNames[curLibs][i]=0;
00045                 // if already tracing, skip
00046                 if (checkIfTracing(libNames[curLibs])) return;
00047                 curLibs++;
00048         }
00049         inline int isTracing(const char *lib) const {
00050                 if (curLibs==0) return 0; //Common case
00051                 else return checkIfTracing(lib);
00052         }
00053 };
00054 static TCharmTraceLibList tcharm_tracelibs;
00055 static int tcharm_nomig=0, tcharm_nothreads=0;
00056 static int tcharm_stacksize=1*1024*1024; /*Default stack size is 1MB*/
00057 static int tcharm_initted=0;
00058 CkpvDeclare(int, mapCreated);
00059 static CkGroupID mapID;
00060 static char* mapping = NULL;
00061 
00062 void TCharm::nodeInit(void)
00063 {
00064 }
00065 
00066 void TCharm::procInit(void)
00067 {
00068   CtvInitialize(TCharm *,_curTCharm);
00069   CtvAccess(_curTCharm)=NULL;
00070   tcharm_initted=1;
00071   CtgInit();
00072 
00073   CkpvInitialize(int, mapCreated);
00074   CkpvAccess(mapCreated) = 0;
00075 
00076   // called on every pe to eat these arguments
00077   char **argv=CkGetArgv();
00078   tcharm_nomig=CmiGetArgFlagDesc(argv,"+tcharm_nomig","Disable migration support (debugging)");
00079   tcharm_nothreads=CmiGetArgFlagDesc(argv,"+tcharm_nothread","Disable thread support (debugging)");
00080   tcharm_nothreads|=CmiGetArgFlagDesc(argv,"+tcharm_nothreads",NULL);
00081   char *traceLibName=NULL;
00082   while (CmiGetArgStringDesc(argv,"+tcharm_trace",&traceLibName,"Print each call to this library"))
00083       tcharm_tracelibs.addTracing(traceLibName);
00084   CmiGetArgIntDesc(argv,"+tcharm_stacksize",&tcharm_stacksize,"Set the thread stack size (default 1MB)");
00085   if (CkMyPe()!=0) { //Processor 0 eats "+vp<N>" and "-vp<N>" later:
00086         int ignored;
00087         while (CmiGetArgIntDesc(argv,"-vp",&ignored,NULL)) {}
00088         while (CmiGetArgIntDesc(argv,"+vp",&ignored,NULL)) {}
00089   }
00090   if (CkMyPe()==0) { // Echo various debugging options:
00091     if (tcharm_nomig) CmiPrintf("TCHARM> Disabling migration support, for debugging\n");
00092     if (tcharm_nothreads) CmiPrintf("TCHARM> Disabling thread support, for debugging\n");
00093   }
00094   if (CkpvAccess(mapCreated)==0) {
00095     if (0!=CmiGetArgString(argv, "+mapping", &mapping)){
00096     }
00097     CkpvAccess(mapCreated)=1;
00098   }
00099 }
00100 
00101 void TCHARM_Api_trace(const char *routineName,const char *libraryName)
00102 {
00103         if (!tcharm_tracelibs.isTracing(libraryName)) return;
00104         TCharm *tc=CtvAccess(_curTCharm);
00105         char where[100];
00106         if (tc==NULL) sprintf(where,"[serial context on %d]",CkMyPe());
00107         else sprintf(where,"[%p> vp %d, p %d]",(void *)tc,tc->getElement(),CkMyPe());
00108         CmiPrintf("%s Called routine %s\n",where,routineName);
00109         CmiPrintStackTrace(1);
00110         CmiPrintf("\n");
00111 }
00112 
00113 #if CMK_TCHARM_FNPTR_HACK
00114 CDECL void AMPI_threadstart(void *data);
00115 #endif
00116 
00117 static void startTCharmThread(TCharmInitMsg *msg)
00118 {
00119         DBGX("thread started");
00120         TCharm::activateThread();
00121         typedef void (*threadFn_t)(void *);
00122 #if CMK_TCHARM_FNPTR_HACK
00123         ((threadFn_t)AMPI_threadstart)(msg->data);
00124 #else
00125         ((threadFn_t)msg->threadFn)(msg->data);
00126 #endif
00127         TCharm::deactivateThread();
00128         CtvAccess(_curTCharm)->done();
00129 }
00130 
00131 TCharm::TCharm(TCharmInitMsg *initMsg_)
00132 {
00133   initMsg=initMsg_;
00134   initMsg->opts.sanityCheck();
00135   timeOffset=0.0;
00136   threadGlobals=CtgCreate();
00137   if (tcharm_nothreads)
00138   { //Don't even make a new thread-- just use main thread
00139     tid=CthSelf();
00140   }
00141   else /*Create a thread normally*/
00142   {
00143     if (tcharm_nomig) { /*Nonmigratable version, for debugging*/
00144       tid=CthCreate((CthVoidFn)startTCharmThread,initMsg,initMsg->opts.stackSize);
00145     } else {
00146       tid=CthCreateMigratable((CthVoidFn)startTCharmThread,initMsg,initMsg->opts.stackSize);
00147     }
00148 #if CMK_BLUEGENE_CHARM
00149     BgAttach(tid);
00150 #endif
00151   }
00152   CtvAccessOther(tid,_curTCharm)=this;
00153   isStopped=true;
00154   resumeAfterMigration=false;
00155         /* FAULT_EVAC*/
00156         AsyncEvacuate(CmiTrue);
00157   skipResume=false;
00158   exitWhenDone=initMsg->opts.exitWhenDone;
00159   threadInfo.tProxy=CProxy_TCharm(thisArrayID);
00160   threadInfo.thisElement=thisIndex;
00161   threadInfo.numElements=initMsg->numElements;
00162   if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
00163         heapBlocks=CmiIsomallocBlockListNew();
00164   else
00165         heapBlocks=0;
00166   nUd=0;
00167   usesAtSync=CmiTrue;
00168   run();
00169 }
00170 
00171 TCharm::TCharm(CkMigrateMessage *msg)
00172         :CBase_TCharm(msg)
00173 {
00174   initMsg=NULL;
00175   tid=NULL;
00176   threadGlobals=NULL;
00177   threadInfo.tProxy=CProxy_TCharm(thisArrayID);
00178         AsyncEvacuate(CmiTrue);
00179   heapBlocks=0;
00180 }
00181 
00182 void checkPupMismatch(PUP::er &p,int expected,const char *where)
00183 {
00184         int v=expected;
00185         p|v;
00186         if (v!=expected) {
00187                 CkError("FATAL ERROR> Mismatch %s pup routine\n",where);
00188                 CkAbort("FATAL ERROR: Pup direction mismatch");
00189         }
00190 }
00191 
00192 void TCharm::pup(PUP::er &p) {
00193 //Pup superclass
00194   ArrayElement1D::pup(p);
00195 
00196   checkPupMismatch(p,5134,"before TCHARM");
00197   p(isStopped); p(resumeAfterMigration); p(exitWhenDone); p(skipResume);
00198   p(threadInfo.thisElement);
00199   p(threadInfo.numElements);
00200   
00201   if (sema.size()>0) 
00202         CkAbort("TCharm::pup> Cannot migrate with unconsumed semaphores!\n");
00203 
00204 #ifndef CMK_OPTIMIZE
00205   DBG("Packing thread");
00206   if (!isStopped && !CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC)){
00207     CkAbort("Cannot pup a running thread.  You must suspend before migrating.\n");
00208         }       
00209   if (tcharm_nomig) CkAbort("Cannot migrate with the +tcharm_nomig option!\n");
00210 #endif
00211 
00212   //This seekBlock allows us to reorder the packing/unpacking--
00213   // This is needed because the userData depends on the thread's stack
00214   // and heap data both at pack and unpack time.
00215   PUP::seekBlock s(p,2);
00216   
00217   if (p.isUnpacking())
00218   {//In this case, unpack the thread & heap before the user data
00219     s.seek(1);
00220     pupThread(p);
00221     //Restart our clock: set it up so packTime==CkWallTimer+timeOffset
00222     double packTime;
00223     p(packTime);
00224     timeOffset=packTime-CkWallTimer();
00225   }
00226   
00227 //Pack all user data
00228   // Set up TCHARM context for use during user's pup routines:
00229   CtvAccess(_curTCharm)=this;
00230   activateThread();
00231 
00232   s.seek(0);
00233   checkPupMismatch(p,5135,"before TCHARM user data");
00234   p(nUd);
00235   for(int i=0;i<nUd;i++) {
00236     if (p.isUnpacking()) ud[i].update(tid);
00237     ud[i].pup(p);
00238   }
00239   checkPupMismatch(p,5137,"after TCHARM_Register user data");
00240 
00241   if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
00242     deactivateThread();
00243   p|sud;           //  sud vector block can not be in isomalloc
00244   checkPupMismatch(p,5138,"after TCHARM_Global user data");
00245   
00246   // Tear down TCHARM context after calling user pup routines
00247   if (!CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
00248     deactivateThread();
00249   CtvAccess(_curTCharm)=NULL;
00250   
00251   if (!p.isUnpacking())
00252   {//In this case, pack the thread & heap after the user data
00253     s.seek(1);
00254     pupThread(p);
00255     //Stop our clock:
00256     double packTime=CkWallTimer()+timeOffset;
00257     p(packTime);
00258   }
00259   
00260   s.endBlock(); //End of seeking block
00261   checkPupMismatch(p,5140,"after TCHARM");
00262 }
00263 
00264 // Pup our thread and related data
00265 void TCharm::pupThread(PUP::er &pc) {
00266     pup_er p=(pup_er)&pc;
00267     checkPupMismatch(pc,5138,"before TCHARM thread");
00268     tid = CthPup(p, tid);
00269     if (pc.isUnpacking()) {
00270       CtvAccessOther(tid,_curTCharm)=this;
00271 #if CMK_BLUEGENE_CHARM
00272       BgAttach(tid);
00273 #endif
00274     }
00275     if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
00276       CmiIsomallocBlockListPup(p,&heapBlocks);
00277     threadGlobals=CtgPup(p,threadGlobals);
00278     checkPupMismatch(pc,5139,"after TCHARM thread");
00279 }
00280 
00281 //Pup one group of user data
00282 void TCharm::UserData::pup(PUP::er &p)
00283 {
00284   pup_er pext=(pup_er)(&p);
00285   p(mode);
00286   switch(mode) {
00287   case 'c': { /* C mode: userdata is on the stack, so keep address */
00288 //     p((char*)&data,sizeof(data));
00289      p(pos);
00290      //FIXME: function pointers may not be valid across processors
00291      p((char*)&cfn, sizeof(TCHARM_Pup_fn));
00292      char *data = CthPointer(t, pos);
00293      if (cfn) cfn(pext,data);
00294      } break;
00295   case 'g': { /* Global mode: zero out userdata on arrival */
00296      if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
00297      {
00298         // keep the pointer value if using isomalloc, no need to use pup
00299        p(pos);
00300      }
00301      else if (p.isUnpacking())      //  zero out userdata on arrival
00302        pos=0;
00303 
00304        //FIXME: function pointers may not be valid across processors
00305      p((char*)&gfn, sizeof(TCHARM_Pup_global_fn));
00306      if (gfn) gfn(pext);
00307      } break;
00308   default:
00309      break;
00310   };
00311 }
00312 
00313 TCharm::~TCharm()
00314 {
00315   if (heapBlocks) CmiIsomallocBlockListDelete(heapBlocks);
00316   CthFree(tid);
00317   CtgFree(threadGlobals);
00318   delete initMsg;
00319 }
00320 
00321 void TCharm::migrateTo(int destPE) {
00322         if (destPE==CkMyPe()) return;
00323         // Make sure migrateMe gets called *after* we suspend:
00324         thisProxy[thisIndex].migrateDelayed(destPE);
00325 //      resumeAfterMigration=true;
00326         suspend();
00327 }
00328 void TCharm::migrateDelayed(int destPE) {
00329         migrateMe(destPE);
00330 }
00331 void TCharm::ckJustMigrated(void) {
00332         ArrayElement::ckJustMigrated();
00333         if (resumeAfterMigration) {
00334                 resumeAfterMigration=false;
00335                 resume(); //Start the thread running
00336         }
00337 }
00338 
00339 /*
00340         FAULT_EVAC
00341 
00342         If a Tcharm object is about to migrate it should be suspended first
00343 */
00344 void TCharm::ckAboutToMigrate(void){
00345         ArrayElement::ckAboutToMigrate();
00346         resumeAfterMigration = true;
00347         isStopped = true;
00348 //      suspend();
00349 }
00350 
00351 // clear the data before restarting from disk
00352 void TCharm::clear()
00353 {
00354   if (heapBlocks) CmiIsomallocBlockListDelete(heapBlocks);
00355   CthFree(tid);
00356   delete initMsg;
00357 }
00358 
00359 //Register user data to be packed with the thread
00360 int TCharm::add(const TCharm::UserData &d)
00361 {
00362   if (nUd>=maxUserData)
00363     CkAbort("TCharm: Registered too many user data fields!\n");
00364   int nu=nUd++;
00365   ud[nu]=d;
00366   return nu;
00367 }
00368 void *TCharm::lookupUserData(int i) {
00369         if (i<0 || i>=nUd)
00370                 CkAbort("Bad user data index passed to TCharmGetUserdata!\n");
00371         return ud[i].getData();
00372 }
00373 
00374 //Start the thread running
00375 void TCharm::run(void)
00376 {
00377   DBG("TCharm::run()");
00378   if (tcharm_nothreads) {/*Call user routine directly*/
00379           startTCharmThread(initMsg);
00380   } 
00381   else /* start the thread as usual */
00382           start();
00383 }
00384 
00385 //Block the thread until start()ed again.
00386 void TCharm::stop(void)
00387 {
00388 #ifndef CMK_OPTIMIZE
00389   if (tid != CthSelf())
00390     CkAbort("Called TCharm::stop from outside TCharm thread!\n");
00391   if (tcharm_nothreads)
00392     CkAbort("Cannot make blocking calls using +tcharm_nothreads!\n");
00393 #endif
00394   stopTiming();
00395   isStopped=true;
00396   DBG("thread suspended");
00397   CthSuspend();
00398 //      DBG("thread resumed");
00399   /*SUBTLE: We have to do the get() because "this" may have changed
00400     during a migration-suspend.  If you access *any* members
00401     from this point onward, you'll cause heap corruption if
00402     we're resuming from migration!  (OSL 2003/9/23)
00403    */
00404   TCharm *dis=TCharm::get();
00405   dis->isStopped=false;
00406   dis->startTiming();
00407 //      printf("[%d] Thread resumed  for tid %p\n",dis->thisIndex,dis->tid);
00408 }
00409 
00410 //Resume the waiting thread
00411 void TCharm::start(void)
00412 {
00413   //  since this thread is scheduled, it is not a good idea to migrate 
00414   isStopped=false;
00415   DBG("thread resuming soon");
00416   CthAwaken(tid);
00417 }
00418 
00419 //Block our thread, schedule, and come back:
00420 void TCharm::schedule(void) {
00421   DBG("thread schedule");
00422   start(); // Calls CthAwaken
00423   stop(); // Calls CthSuspend
00424 }
00425 
00426 //Go to sync, block, possibly migrate, and then resume
00427 void TCharm::migrate(void)
00428 {
00429 #if CMK_LBDB_ON
00430   DBG("going to sync");
00431   AtSync();
00432   stop();
00433 #else
00434   DBG("skipping sync, because there is no load balancer");
00435 #endif
00436 }
00437 
00438 
00439 void TCharm::evacuate(){
00440         /*
00441                 FAULT_EVAC
00442         */
00443         //CkClearAllArrayElementsCPP();
00444         if(CpvAccess(startedEvac)){
00445                 int nextPE = getNextPE(CkArrayIndex1D(thisIndex));
00446 //              resumeAfterMigration=true;
00447                 CcdCallFnAfter((CcdVoidFn)CkEmmigrateElement, (void *)myRec, 1);
00448                 suspend();
00449                 return;
00450         }
00451         return;
00452 
00453 }
00454 
00455 //calls atsync with async mode
00456 void TCharm::async_migrate(void)
00457 {
00458 #if CMK_LBDB_ON
00459   DBG("going to sync at async mode");
00460   skipResume = true;            // we resume immediately
00461   ReadyMigrate(false);
00462   AtSync(0);
00463   schedule();
00464 //  allow_migrate();
00465 #else
00466   DBG("skipping sync, because there is no load balancer");
00467 #endif
00468 }
00469 
00470 /*
00471 Note:
00472  thread can only migrate at the point when this is called
00473 */
00474 void TCharm::allow_migrate(void)
00475 {
00476 #if CMK_LBDB_ON
00477 //  ReadyMigrate(true);
00478   int nextPe = MigrateToPe();
00479   if (nextPe != -1) {
00480     migrateTo(nextPe);
00481   }
00482 #else
00483   DBG("skipping sync, because there is no load balancer");
00484 #endif
00485 }
00486 
00487 //Resume from sync: start the thread again
00488 void TCharm::ResumeFromSync(void)
00489 {
00490   if (!skipResume) start();
00491 }
00492 
00493 
00494 /****** TcharmClient ******/
00495 void TCharmClient1D::ckJustMigrated(void) {
00496   ArrayElement1D::ckJustMigrated();
00497   findThread();
00498   tcharmClientInit();
00499 }
00500 
00501 void TCharmClient1D::pup(PUP::er &p) {
00502   ArrayElement1D::pup(p);
00503   p|threadProxy;
00504 }
00505 
00506 CkArrayID TCHARM_Get_threads(void) {
00507         TCHARMAPI("TCHARM_Get_threads");
00508         return TCharm::get()->getProxy();
00509 }
00510 
00511 /************* Startup/Shutdown Coordination Support ************/
00512 
00513 // Useless values to reduce over:
00514 int vals[2]={0,1};
00515 
00516 //Called when we want to go to a barrier
00517 void TCharm::barrier(void) {
00518         //Contribute to a synchronizing reduction
00519         CkCallback cb(index_t::atBarrier(0), thisProxy[0]);
00520         contribute(sizeof(vals),&vals,CkReduction::sum_int,cb);
00521 #if CMK_BLUEGENE_CHARM
00522         void *curLog;           // store current log in timeline
00523         _TRACE_BG_TLINE_END(&curLog);
00524         TRACE_BG_AMPI_BREAK(NULL, "TCharm_Barrier_START", NULL, 0);
00525 #endif
00526         stop();
00527 #if CMK_BLUEGENE_CHARM
00528          _TRACE_BG_SET_INFO(NULL, "TCHARM_Barrier_END",  &curLog, 1);
00529 #endif
00530 }
00531 
00532 //Called when we've reached the barrier
00533 void TCharm::atBarrier(CkReductionMsg *m) {
00534         DBGX("clients all at barrier");
00535         delete m;
00536         thisProxy.start(); //Just restart everybody
00537 }
00538 
00539 //Called when the thread is done running
00540 void TCharm::done(void) {
00541         DBG("TCharm thread "<<thisIndex<<" done")
00542         if (exitWhenDone) {
00543                 //Contribute to a synchronizing reduction
00544                 CkCallback cb(index_t::atExit(0), thisProxy[0]);
00545                 contribute(sizeof(vals),&vals,CkReduction::sum_int,cb);
00546         }
00547         stop();
00548 }
00549 //Called when all threads are done running
00550 void TCharm::atExit(CkReductionMsg *m) {
00551         DBGX("TCharm::atExit> exiting");
00552         delete m;
00553         CkExit();
00554 }
00555 
00556 
00557 /************* Setup **************/
00558 
00559 //Globals used to control setup process
00560 static TCHARM_Fallback_setup_fn g_fallbackSetup=NULL;
00561 void TCHARM_Set_fallback_setup(TCHARM_Fallback_setup_fn f)
00562 {
00563         g_fallbackSetup=f;
00564 }
00565 void TCHARM_Call_fallback_setup(void) {
00566         if (g_fallbackSetup) 
00567                 (g_fallbackSetup)();
00568         else
00569                 CkAbort("TCHARM: Unexpected fallback setup--missing TCHARM_User_setup routine?");
00570 }
00571 
00572 /************** User API ***************/
00573 /**********************************
00574 Callable from UserSetup:
00575 */
00576 
00577 // Read the command line to figure out how many threads to create:
00578 CDECL int TCHARM_Get_num_chunks(void)
00579 {
00580         TCHARMAPI("TCHARM_Get_num_chunks");
00581         if (CkMyPe()!=0) CkAbort("TCHARM_Get_num_chunks should only be called on PE 0 during setup!");
00582         int nChunks=CkNumPes();
00583         char **argv=CkGetArgv();
00584         CmiGetArgIntDesc(argv,"-vp",&nChunks,"Set the total number of virtual processors");
00585         CmiGetArgIntDesc(argv,"+vp",&nChunks,NULL);
00586         lastNumChunks=nChunks;
00587         return nChunks;
00588 }
00589 FDECL int FTN_NAME(TCHARM_GET_NUM_CHUNKS,tcharm_get_num_chunks)(void)
00590 {
00591         return TCHARM_Get_num_chunks();
00592 }
00593 
00594 // Fill out the default thread options:
00595 TCHARM_Thread_options::TCHARM_Thread_options(int doDefault)
00596 {
00597         stackSize=0; /* default stacksize */
00598         exitWhenDone=0; /* don't exit when done by default. */
00599 }
00600 void TCHARM_Thread_options::sanityCheck(void) {
00601         if (stackSize<=0) stackSize=tcharm_stacksize;
00602 }
00603 
00604 
00605 TCHARM_Thread_options g_tcharmOptions(1);
00606 
00607 /*Set the size of the thread stack*/
00608 CDECL void TCHARM_Set_stack_size(int newStackSize)
00609 {
00610         TCHARMAPI("TCHARM_Set_stack_size");
00611         g_tcharmOptions.stackSize=newStackSize;
00612 }
00613 FDECL void FTN_NAME(TCHARM_SET_STACK_SIZE,tcharm_set_stack_size)
00614         (int *newSize)
00615 { TCHARM_Set_stack_size(*newSize); }
00616 
00617 CDECL void TCHARM_Set_exit(void) { g_tcharmOptions.exitWhenDone=1; }
00618 
00619 /*Create a new array of threads, which will be bound to by subsequent libraries*/
00620 CDECL void TCHARM_Create(int nThreads,
00621                         TCHARM_Thread_start_fn threadFn)
00622 {
00623         TCHARMAPI("TCHARM_Create");
00624         TCHARM_Create_data(nThreads,
00625                          (TCHARM_Thread_data_start_fn)threadFn,NULL,0);
00626 }
00627 FDECL void FTN_NAME(TCHARM_CREATE,tcharm_create)
00628         (int *nThreads,TCHARM_Thread_start_fn threadFn)
00629 { TCHARM_Create(*nThreads,threadFn); }
00630 
00631 static CProxy_TCharm TCHARM_Build_threads(TCharmInitMsg *msg);
00632 
00633 /*As above, but pass along (arbitrary) data to threads*/
00634 CDECL void TCHARM_Create_data(int nThreads,
00635                   TCHARM_Thread_data_start_fn threadFn,
00636                   void *threadData,int threadDataLen)
00637 {
00638         TCHARMAPI("TCHARM_Create_data");
00639         TCharmInitMsg *msg=new (threadDataLen,0) TCharmInitMsg(
00640                 (CthVoidFn)threadFn,g_tcharmOptions);
00641         msg->numElements=nThreads;
00642         memcpy(msg->data,threadData,threadDataLen);
00643         TCHARM_Build_threads(msg);
00644         
00645         // Reset the thread options:
00646         g_tcharmOptions=TCHARM_Thread_options(1);
00647 }
00648 
00649 FDECL void FTN_NAME(TCHARM_CREATE_DATA,tcharm_create_data)
00650         (int *nThreads,
00651                   TCHARM_Thread_data_start_fn threadFn,
00652                   void *threadData,int *threadDataLen)
00653 { TCHARM_Create_data(*nThreads,threadFn,threadData,*threadDataLen); }
00654 
00655 CkGroupID CkCreatePropMap(void);
00656 
00657 static CProxy_TCharm TCHARM_Build_threads(TCharmInitMsg *msg)
00658 {
00659   CkArrayOptions opts(msg->numElements);
00660   CkAssert(CkpvAccess(mapCreated)==1);
00661   if(mapping==NULL){
00662     mapID=CkCreatePropMap();
00663   }else if(0==strcmp(mapping,"BLOCK_MAP")){
00664     mapID=CProxy_BlockMap::ckNew();
00665   }else if(0==strcmp(mapping,"RR_MAP")){
00666     mapID=CProxy_RRMap::ckNew();
00667   }else{  // "PROP_MAP" or anything else
00668     mapID=CkCreatePropMap();
00669   }
00670   opts.setMap(mapID);
00671   int nElem=msg->numElements; //<- save it because msg will be deleted.
00672   return CProxy_TCharm::ckNew(msg,opts);
00673 }
00674 
00675 // Helper used when creating a new array bound to the TCHARM threads:
00676 CkArrayOptions TCHARM_Attach_start(CkArrayID *retTCharmArray,int *retNumElts)
00677 {
00678         TCharm *tc=TCharm::get();
00679         if (!tc)
00680                 CkAbort("You must call TCHARM initialization routines from a TCHARM thread!");
00681         int nElts=tc->getNumElements();
00682         if (retNumElts!=NULL) *retNumElts=nElts;
00683         *retTCharmArray=tc->getProxy();
00684         CkArrayOptions opts(nElts);
00685         opts.bindTo(tc->getProxy());
00686         return opts;
00687 }
00688 
00689 void TCHARM_Suspend(void) {
00690         TCharm *tc=TCharm::get();
00691         tc->suspend();
00692 }
00693 
00694 /***********************************
00695 Callable from worker thread
00696 */
00697 CDECL int TCHARM_Element(void)
00698 { 
00699         TCHARMAPI("TCHARM_Element");
00700         return TCharm::get()->getElement();
00701 }
00702 CDECL int TCHARM_Num_elements(void)
00703 { 
00704         TCHARMAPI("TCHARM_Num_elements");
00705         return TCharm::get()->getNumElements();
00706 }
00707 
00708 FDECL int FTN_NAME(TCHARM_ELEMENT,tcharm_element)(void) 
00709 { return TCHARM_Element();}
00710 FDECL int FTN_NAME(TCHARM_NUM_ELEMENTS,tcharm_num_elements)(void) 
00711 { return TCHARM_Num_elements();}
00712 
00713 //Make sure this address will migrate with us when we move:
00714 static void checkAddress(void *data)
00715 {
00716         if (tcharm_nomig||tcharm_nothreads) return; //Stack is not isomalloc'd
00717         if (CmiThreadIs(CMI_THREAD_IS_ALIAS)||CmiThreadIs(CMI_THREAD_IS_STACKCOPY)) return; // memory alias thread
00718         if (!CmiIsomallocInRange(data))
00719             CkAbort("The UserData you register must be allocated on the stack!\n");
00720 }
00721 
00722 /* Old "register"-based userdata: */
00723 CDECL int TCHARM_Register(void *data,TCHARM_Pup_fn pfn)
00724 { 
00725         TCHARMAPI("TCHARM_Register");
00726         checkAddress(data);
00727         return TCharm::get()->add(TCharm::UserData(pfn,TCharm::get()->getThread(),data));
00728 }
00729 FDECL int FTN_NAME(TCHARM_REGISTER,tcharm_register)
00730         (void *data,TCHARM_Pup_fn pfn)
00731 { 
00732         TCHARMAPI("TCHARM_Register");
00733         checkAddress(data);
00734         return TCharm::get()->add(TCharm::UserData(pfn,TCharm::get()->getThread(),data));
00735 }
00736 
00737 CDECL void *TCHARM_Get_userdata(int id)
00738 {
00739         TCHARMAPI("TCHARM_Get_userdata");
00740         return TCharm::get()->lookupUserData(id);
00741 }
00742 FDECL void *FTN_NAME(TCHARM_GET_USERDATA,tcharm_get_userdata)(int *id)
00743 { return TCHARM_Get_userdata(*id); }
00744 
00745 /* New hardcoded-ID userdata: */
00746 CDECL void TCHARM_Set_global(int globalID,void *new_value,TCHARM_Pup_global_fn pup_or_NULL)
00747 {
00748         TCHARMAPI("TCHARM_Set_global");
00749         TCharm *tc=TCharm::get();
00750         if (tc->sud.length()<=globalID)
00751         { //We don't have room for this ID yet: make room
00752                 int newLen=2*globalID;
00753                 tc->sud.resize(newLen);
00754         }
00755         tc->sud[globalID]=TCharm::UserData(pup_or_NULL,tc->getThread(),new_value);
00756 }
00757 CDECL void *TCHARM_Get_global(int globalID)
00758 {
00759         //Skip TCHARMAPI("TCHARM_Get_global") because there's no dynamic allocation here,
00760         // and this routine should be as fast as possible.
00761         CkVec<TCharm::UserData> &v=TCharm::get()->sud;
00762         if (v.length()<=globalID) return NULL; //Uninitialized global
00763         return v[globalID].getData();
00764 }
00765 
00766 CDECL void TCHARM_Migrate(void)
00767 {
00768         TCHARMAPI("TCHARM_Migrate");
00769         TCharm::get()->migrate();
00770 }
00771 FORTRAN_AS_C(TCHARM_MIGRATE,TCHARM_Migrate,tcharm_migrate,(void),())
00772 
00773 CDECL void TCHARM_Async_Migrate(void)
00774 {
00775         TCHARMAPI("TCHARM_Async_Migrate");
00776         TCharm::get()->async_migrate();
00777 }
00778 FORTRAN_AS_C(TCHARM_ASYNC_MIGRATE,TCHARM_Async_Migrate,tcharm_async_migrate,(void),())
00779 
00780 CDECL void TCHARM_Allow_Migrate(void)
00781 {
00782         TCHARMAPI("TCHARM_Allow_Migrate");
00783         TCharm::get()->allow_migrate();
00784 }
00785 FORTRAN_AS_C(TCHARM_ALLOW_MIGRATE,TCHARM_Allow_Migrate,tcharm_allow_migrate,(void),())
00786 
00787 CDECL void TCHARM_Migrate_to(int destPE)
00788 {
00789         TCHARMAPI("TCHARM_Migrate_to");
00790         TCharm::get()->migrateTo(destPE);
00791 }
00792 
00793 CDECL void TCHARM_Evacuate()
00794 {
00795         TCHARMAPI("TCHARM_Migrate_to");
00796         TCharm::get()->evacuate();
00797 }
00798 
00799 FORTRAN_AS_C(TCHARM_MIGRATE_TO,TCHARM_Migrate_to,tcharm_migrate_to,
00800         (int *destPE),(*destPE))
00801 
00802 CDECL void TCHARM_Yield(void)
00803 {
00804         TCHARMAPI("TCHARM_Yield");
00805         TCharm::get()->schedule();
00806 }
00807 FORTRAN_AS_C(TCHARM_YIELD,TCHARM_Yield,tcharm_yield,(void),())
00808 
00809 CDECL void TCHARM_Barrier(void)
00810 {
00811         TCHARMAPI("TCHARM_Barrier");
00812         TCharm::get()->barrier();
00813 }
00814 FORTRAN_AS_C(TCHARM_BARRIER,TCHARM_Barrier,tcharm_barrier,(void),())
00815 
00816 CDECL void TCHARM_Done(void)
00817 {
00818         TCHARMAPI("TCHARM_Done");
00819         TCharm *c=TCharm::getNULL();
00820         if (!c) CkExit();
00821         else c->done();
00822 }
00823 FORTRAN_AS_C(TCHARM_DONE,TCHARM_Done,tcharm_done,(void),())
00824 
00825 
00826 CDECL double TCHARM_Wall_timer(void)
00827 {
00828   TCHARMAPI("TCHARM_Wall_timer");
00829   TCharm *c=TCharm::getNULL();
00830   if(!c) return CkWallTimer();
00831   else { //Have to apply current thread's time offset
00832     return CkWallTimer()+c->getTimeOffset();
00833   }
00834 }
00835 
00836 #if 1
00837 /*Include Fortran-style "iargc" and "getarg" routines.
00838 These are needed to get access to the command-line arguments from Fortran.
00839 */
00840 FDECL int FTN_NAME(TCHARM_IARGC,tcharm_iargc)(void) {
00841   TCHARMAPI("tcharm_iargc");
00842   return CkGetArgc()-1;
00843 }
00844 
00845 FDECL void FTN_NAME(TCHARM_GETARG,tcharm_getarg)
00846         (int *i_p,char *dest,int destLen)
00847 {
00848   TCHARMAPI("tcharm_getarg");
00849   int i=*i_p;
00850   if (i<0) CkAbort("tcharm_getarg called with negative argument!");
00851   if (i>=CkGetArgc()) CkAbort("tcharm_getarg called with argument > iargc!");
00852   const char *src=CkGetArgv()[i];
00853   strcpy(dest,src);
00854   for (i=strlen(dest);i<destLen;i++) dest[i]=' ';
00855 }
00856 
00857 #endif
00858 
00859 //These silly routines are used for serial startup:
00860 extern void _initCharm(int argc, char **argv);
00861 CDECL void TCHARM_Init(int *argc,char ***argv) {
00862         if (!tcharm_initted) {
00863           ConverseInit(*argc, *argv, (CmiStartFn) _initCharm,1,1);
00864           _initCharm(*argc,*argv);
00865         }
00866 }
00867 
00868 FDECL void FTN_NAME(TCHARM_INIT,tcharm_init)(void)
00869 {
00870         int argc=1;
00871         char *argv_sto[2]={"foo",NULL};
00872         char **argv=argv_sto;
00873         TCHARM_Init(&argc,&argv);
00874 }
00875 
00876 /***********************************
00877 * TCHARM Semaphores:
00878 * The idea is one side "puts", the other side "gets"; 
00879 * but the calls can come in any order--
00880 * if the "get" comes first, it blocks until the put.
00881 * This makes a convenient, race-condition-free way to do
00882 * onetime initializations.  
00883 */
00885 TCharm::TCharmSemaphore *TCharm::findSema(int id) {
00886         for (int s=0;s<sema.size();s++)
00887                 if (sema[s].id==id) 
00888                         return &sema[s];
00889         sema.push_back(TCharmSemaphore(id));
00890         return &sema[sema.size()-1];
00891 }
00893 void TCharm::freeSema(TCharmSemaphore *doomed) {
00894         int id=doomed->id;
00895         for (int s=0;s<sema.size();s++)
00896                 if (sema[s].id==id) {
00897                         sema[s]=sema[sema.length()-1];
00898                         sema.length()--;
00899                         return;
00900                 }
00901         CkAbort("Tried to free nonexistent TCharm semaphore");
00902 }
00903 
00905 TCharm::TCharmSemaphore *TCharm::getSema(int id) {
00906         TCharmSemaphore *s=findSema(id);
00907         if (s->data==NULL) 
00908         { //Semaphore isn't filled yet: wait until it is
00909