TCharm: do not abort when an LB is linked but +tcharm_nomig is used without +balancer
[charm.git] / src / libs / ck-libs / tcharm / tcharm.C
blobc3c72b5e173cc15514f96223895267e1159f2095
1 /*
2 Threaded Charm++ "Framework Framework"
4 Orion Sky Lawlor, olawlor@acm.org, 11/19/2001
5  */
6 #include "tcharm_impl.h"
7 #include "tcharm.h"
8 #include "mempool.h"
9 #include "ckevacuation.h"
10 #include <ctype.h>
12 #if 0 
13     /*Many debugging statements:*/
14 #    define DBG(x) ckout<<"["<<thisIndex<<","<<CkMyPe()<<"] TCHARM> "<<x<<endl;
15 #    define DBGX(x) ckout<<"PE("<<CkMyPe()<<") TCHARM> "<<x<<endl;
16 #else
17     /*No debugging statements*/
18 #    define DBG(x) /*empty*/
19 #    define DBGX(x) /*empty*/
20 #endif
22 CtvDeclare(TCharm *,_curTCharm);
24 #if CMK_USE_MEMPOOL_ISOMALLOC
25 extern "C"
27 CtvExtern(mempool_type *, threadpool);
29 #endif
31 static int lastNumChunks=0;
33 class TCharmTraceLibList {
34         enum {maxLibs=20,maxLibNameLen=15};
35         //List of libraries we want to trace:
36         int curLibs;
37         char libNames[maxLibs][maxLibNameLen];
38         int checkIfTracing(const char *lib) const
39         {
40                 for (int i=0;i<curLibs;i++) 
41                         if (0==strcmp(lib,libNames[i]))
42                                 return 1;
43                 return 0;
44         }
45 public:
46         TCharmTraceLibList() {curLibs=0;}
47         void addTracing(const char *lib) 
48         { //We want to trace this library-- add its name to the list.
49                 CkPrintf("TCHARM> Will trace calls to library %s\n",lib);
50                 int i;
51                 for (i=0;0!=*lib;i++,lib++)
52                         libNames[curLibs][i]=tolower(*lib);
53                 libNames[curLibs][i]=0;
54                 // if already tracing, skip
55                 if (checkIfTracing(libNames[curLibs])) return;
56                 curLibs++;
57         }
58         inline bool isTracing(const char *lib) const {
59                 if (curLibs==0) return 0; //Common case
60                 else return checkIfTracing(lib);
61         }
63 static TCharmTraceLibList tcharm_tracelibs;
64 static bool tcharm_nomig=false, tcharm_nothreads=false;
65 static int tcharm_stacksize=1*1024*1024; /*Default stack size is 1MB*/
66 static bool tcharm_initted=false;
67 CkpvDeclare(bool, mapCreated);
68 static CkGroupID mapID;
69 static char* mapping = NULL;
71 void TCharm::nodeInit(void)
75 void TCharm::procInit(void)
77   CtvInitialize(TCharm *,_curTCharm);
78   CtvAccess(_curTCharm)=NULL;
79   tcharm_initted=true;
80   CtgInit();
82   CkpvInitialize(bool, mapCreated);
83   CkpvAccess(mapCreated) = false;
85   // called on every pe to eat these arguments
86   char **argv=CkGetArgv();
87   tcharm_nomig=CmiGetArgFlagDesc(argv,"+tcharm_nomig","Disable migration support (debugging)");
88   tcharm_nothreads=CmiGetArgFlagDesc(argv,"+tcharm_nothread","Disable thread support (debugging)");
89   tcharm_nothreads|=CmiGetArgFlagDesc(argv,"+tcharm_nothreads",NULL);
90   char *traceLibName=NULL;
91   while (CmiGetArgStringDesc(argv,"+tcharm_trace",&traceLibName,"Print each call to this library"))
92       tcharm_tracelibs.addTracing(traceLibName);
93   // CmiGetArgIntDesc(argv,"+tcharm_stacksize",&tcharm_stacksize,"Set the thread stack size (default 1MB)");
94   char *str;
95   if (CmiGetArgStringDesc(argv,"+tcharm_stacksize",&str,"Set the thread stack size (default 1MB)"))  {
96     if (strpbrk(str,"M")) {
97       sscanf(str, "%dM", &tcharm_stacksize);
98       tcharm_stacksize *= 1024*1024;
99     }
100     else if (strpbrk(str,"K")) {
101       sscanf(str, "%dK", &tcharm_stacksize);
102       tcharm_stacksize *= 1024;
103     }
104     else {
105       sscanf(str, "%d", &tcharm_stacksize);
106     }
107     if (CkMyPe() == 0)
108       CkPrintf("TCharm> stack size is set to %d.\n", tcharm_stacksize);
109   }
110   if (CkMyPe()!=0) { //Processor 0 eats "+vp<N>" and "-vp<N>" later:
111         int ignored;
112         while (CmiGetArgIntDesc(argv,"-vp",&ignored,NULL)) {}
113         while (CmiGetArgIntDesc(argv,"+vp",&ignored,NULL)) {}
114   }
115   if (CkMyPe()==0) { // Echo various debugging options:
116     if (tcharm_nomig) CmiPrintf("TCHARM> Disabling migration support, for debugging\n");
117     if (tcharm_nothreads) CmiPrintf("TCHARM> Disabling thread support, for debugging\n");
118   }
119   if (!CkpvAccess(mapCreated)) {
120     if (0!=CmiGetArgString(argv, "+mapping", &mapping)){
121     }
122     CkpvAccess(mapCreated)=true;
123   }
126 void TCHARM_Api_trace(const char *routineName,const char *libraryName)
128         if (!tcharm_tracelibs.isTracing(libraryName)) return;
129         TCharm *tc=CtvAccess(_curTCharm);
130         char where[100];
131         if (tc==NULL) sprintf(where,"[serial context on %d]",CkMyPe());
132         else sprintf(where,"[%p> vp %d, p %d]",(void *)tc,tc->getElement(),CkMyPe());
133         CmiPrintf("%s Called routine %s\n",where,routineName);
134         CmiPrintStackTrace(1);
135         CmiPrintf("\n");
138 // register thread start functions to get a function handler
139 // this is portable across heterogeneous platforms, or on machines with
140 // random stack/function pointer
142 static CkVec<TCHARM_Thread_data_start_fn> threadFnTable;
144 int TCHARM_Register_thread_function(TCHARM_Thread_data_start_fn fn)
146   int idx = threadFnTable.size();
147   threadFnTable.push_back(fn);
148   return idx+1;                     // make 0 invalid number
151 TCHARM_Thread_data_start_fn getTCharmThreadFunction(int idx)
153   CmiAssert(idx > 0);
154   return threadFnTable[idx-1];
157 static void startTCharmThread(TCharmInitMsg *msg)
159         DBGX("thread started");
160         TCharm::activateThread();
161        TCHARM_Thread_data_start_fn threadFn = getTCharmThreadFunction(msg->threadFn);
162         threadFn(msg->data);
163         TCharm::deactivateThread();
164         CtvAccess(_curTCharm)->done();
167 TCharm::TCharm(TCharmInitMsg *initMsg_)
169   initMsg=initMsg_;
170   initMsg->opts.sanityCheck();
171   timeOffset=0.0;
172   if (tcharm_nothreads)
173   { //Don't even make a new thread-- just use main thread
174     tid=CthSelf();
175   }
176   else /*Create a thread normally*/
177   {
178     if (tcharm_nomig) { /*Nonmigratable version, for debugging*/
179       tid=CthCreate((CthVoidFn)startTCharmThread,initMsg,initMsg->opts.stackSize);
180     } else {
181       tid=CthCreateMigratable((CthVoidFn)startTCharmThread,initMsg,initMsg->opts.stackSize);
182     }
183 #if CMK_BIGSIM_CHARM
184     BgAttach(tid);
185     BgUnsetStartOutOfCore();
186 #endif
187   }
188   threadGlobals=CtgCreate(tid);
189   CtvAccessOther(tid,_curTCharm)=this;
190   asyncMigrate = false;
191   isStopped=true;
192         /* FAULT_EVAC*/
193         AsyncEvacuate(true);
194   exitWhenDone=initMsg->opts.exitWhenDone;
195   isSelfDone = false;
196   threadInfo.tProxy=CProxy_TCharm(thisArrayID);
197   threadInfo.thisElement=thisIndex;
198   threadInfo.numElements=initMsg->numElements;
199   if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC)) {
200         heapBlocks=CmiIsomallocBlockListNew(tid);
201   } else
202         heapBlocks=0;
203   nUd=0;
204   usesAtSync=true;
205   run();
208 TCharm::TCharm(CkMigrateMessage *msg)
209         :CBase_TCharm(msg)
211   initMsg=NULL;
212   tid=NULL;
213   threadGlobals=NULL;
214   threadInfo.tProxy=CProxy_TCharm(thisArrayID);
215         AsyncEvacuate(true);
216   heapBlocks=0;
219 void checkPupMismatch(PUP::er &p,int expected,const char *where)
221         int v=expected;
222         p|v;
223         if (v!=expected) {
224                 CkError("FATAL ERROR> Mismatch %s pup routine\n",where);
225                 CkAbort("FATAL ERROR: Pup direction mismatch");
226         }
229 void TCharm::pup(PUP::er &p) {
230   //BIGSIM_OOC DEBUGGING
231   //if(!p.isUnpacking()){
232   //  CmiPrintf("TCharm[%d] packing: ", thisIndex);
233   //  CthPrintThdStack(tid);
234   //}
236   checkPupMismatch(p,5134,"before TCHARM");
237   p(isStopped); p(exitWhenDone); p(isSelfDone); p(asyncMigrate);
238   p(threadInfo.thisElement);
239   p(threadInfo.numElements);
240   
241   if (sema.size()>0){
242         CkAbort("TCharm::pup> Cannot migrate with unconsumed semaphores!\n");
243   }
245   DBG("Packing thread");
246 #if CMK_ERROR_CHECKING
247   if (!p.isSizing() && !isStopped && !CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC)){
248     if(_BgOutOfCoreFlag==0) //not doing out-of-core scheduling
249         CkAbort("Cannot pup a running thread.  You must suspend before migrating.\n");
250   }     
251   if (tcharm_nomig && !p.isSizing()) CkAbort("Cannot migrate with the +tcharm_nomig option!\n");
252 #endif
254   //This seekBlock allows us to reorder the packing/unpacking--
255   // This is needed because the userData depends on the thread's stack
256   // and heap data both at pack and unpack time.
257   PUP::seekBlock s(p,2);
258   
259   if (p.isUnpacking())
260   {//In this case, unpack the thread & heap before the user data
261     s.seek(1);
262     pupThread(p);
263     //Restart our clock: set it up so packTime==CkWallTimer+timeOffset
264     double packTime;
265     p(packTime);
266     timeOffset=packTime-CkWallTimer();
267   }
268   
269 //Pack all user data
270   // Set up TCHARM context for use during user's pup routines:
271   if(isStopped) {
272     CtvAccess(_curTCharm)=this;
273     activateThread();
274   }
276   s.seek(0);
277   checkPupMismatch(p,5135,"before TCHARM user data");
278   p(nUd);
279   for(int i=0;i<nUd;i++) {
280     if (p.isUnpacking()) ud[i].update(tid);
281     ud[i].pup(p);
282   }
283   checkPupMismatch(p,5137,"after TCHARM_Register user data");
285   if(isStopped) {
286     if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
287       deactivateThread();
288   }
289   p|sud;           //  sud vector block can not be in isomalloc
290   checkPupMismatch(p,5138,"after TCHARM_Global user data");
291   
292   // Tear down TCHARM context after calling user pup routines
293   if(isStopped) {
294     if (!CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
295       deactivateThread();
296     CtvAccess(_curTCharm)=NULL;
297   }
299   if (!p.isUnpacking())
300   {//In this case, pack the thread & heap after the user data
301     s.seek(1);
302     pupThread(p);
303     //Stop our clock:
304     double packTime=CkWallTimer()+timeOffset;
305     p(packTime);
306   }
307   
308   s.endBlock(); //End of seeking block
309   checkPupMismatch(p,5140,"after TCHARM");
310   
311   //BIGSIM_OOC DEBUGGING
312   //if(p.isUnpacking()){
313   //  CmiPrintf("TCharm[%d] unpacking: ", thisIndex);
314   //  CthPrintThdStack(tid);
315   //}
319 // Pup our thread and related data
320 void TCharm::pupThread(PUP::er &pc) {
321     pup_er p=(pup_er)&pc;
322     checkPupMismatch(pc,5138,"before TCHARM thread"); 
323 #if CMK_USE_MEMPOOL_ISOMALLOC
324     CmiIsomallocBlockListPup(p,&heapBlocks,tid);
325 #else
326     if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
327       CmiIsomallocBlockListPup(p,&heapBlocks,tid);
328 #endif
329     tid = CthPup(p, tid);
330     if (pc.isUnpacking()) {
331       CtvAccessOther(tid,_curTCharm)=this;
332 #if CMK_BIGSIM_CHARM
333       BgAttach(tid);
334 #endif
335     }
336     threadGlobals=CtgPup(p,threadGlobals);
337     checkPupMismatch(pc,5139,"after TCHARM thread");
340 //Pup one group of user data
341 void TCharm::UserData::pup(PUP::er &p)
343   pup_er pext=(pup_er)(&p);
344   p(mode);
345   switch(mode) {
346   case 'c': { /* C mode: userdata is on the stack, so keep address */
347 //     p((char*)&data,sizeof(data));
348      p(pos);
349      //FIXME: function pointers may not be valid across processors
350      p((char*)&cfn, sizeof(TCHARM_Pup_fn));
351      char *data = CthPointer(t, pos);
352      if (cfn) cfn(pext,data);
353      } break;
354   case 'g': { /* Global mode: zero out userdata on arrival */
355      if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
356      {
357         // keep the pointer value if using isomalloc, no need to use pup
358        p(pos);
359      }
360      else if (p.isUnpacking())      //  zero out userdata on arrival
361        pos=0;
363        //FIXME: function pointers may not be valid across processors
364      p((char*)&gfn, sizeof(TCHARM_Pup_global_fn));
365      if (gfn) gfn(pext);
366      } break;
367   default:
368      break;
369   };
372 TCharm::~TCharm()
374   //BIGSIM_OOC DEBUGGING
375   //CmiPrintf("TCharm destructor called with heapBlocks=%p!\n", heapBlocks);
376   
377 #if CMK_USE_MEMPOOL_ISOMALLOC
378   mempool_type *mptr = NULL;
379   if(tid != NULL) mptr = CtvAccessOther(tid,threadpool);
380 #else
381   if (heapBlocks) CmiIsomallocBlockListDelete(heapBlocks);
382 #endif
383   CthFree(tid);
384   CtgFree(threadGlobals);
385 #if CMK_USE_MEMPOOL_ISOMALLOC 
386   if(mptr != NULL) mempool_destroy(mptr);
387 #endif
388   delete initMsg;
391 void TCharm::migrateTo(int destPE) {
392         if (destPE==CkMyPe()) return;
393         if (CthMigratable() == 0) {
394             CkPrintf("Warning> thread migration is not supported!\n");
395             return;
396         }
397         asyncMigrate = true;
398         // Make sure migrateMe gets called *after* we suspend:
399         thisProxy[thisIndex].migrateDelayed(destPE);
400         suspend();
402 void TCharm::migrateDelayed(int destPE) {
403         migrateMe(destPE);
405 void TCharm::ckJustMigrated(void) {
406         ArrayElement::ckJustMigrated();
407     if (asyncMigrate) {
408         asyncMigrate = false;
409         resume();
410     }
413 void TCharm::ckJustRestored(void) {
414         ArrayElement::ckJustRestored();
418         FAULT_EVAC
420         If a Tcharm object is about to migrate it should be suspended first
422 void TCharm::ckAboutToMigrate(void){
423         ArrayElement::ckAboutToMigrate();
424         isStopped = true;
427 // clear the data before restarting from disk
428 void TCharm::clear()
430 #if CMK_USE_MEMPOOL_ISOMALLOC
431   mempool_type *mptr = NULL;
432   if(tid != NULL) mptr = CtvAccessOther(tid,threadpool);
433 #else 
434   if (heapBlocks) CmiIsomallocBlockListDelete(heapBlocks);
435 #endif
436   CthFree(tid);
437 #if CMK_USE_MEMPOOL_ISOMALLOC
438   if(mptr != NULL) mempool_destroy(mptr);
439 #endif
440   delete initMsg;
443 //Register user data to be packed with the thread
444 int TCharm::add(const TCharm::UserData &d)
446   if (nUd>=maxUserData)
447     CkAbort("TCharm: Registered too many user data fields!\n");
448   int nu=nUd++;
449   ud[nu]=d;
450   return nu;
452 void *TCharm::lookupUserData(int i) {
453         if (i<0 || i>=nUd)
454                 CkAbort("Bad user data index passed to TCharmGetUserdata!\n");
455         return ud[i].getData();
458 //Start the thread running
459 void TCharm::run(void)
461   DBG("TCharm::run()");
462   if (tcharm_nothreads) {/*Call user routine directly*/
463           startTCharmThread(initMsg);
464   } 
465   else /* start the thread as usual */
466           start();
469 //Block the thread until start()ed again.
470 void TCharm::stop(void)
472 #if CMK_ERROR_CHECKING
473   if (tid != CthSelf())
474     CkAbort("Called TCharm::stop from outside TCharm thread!\n");
475   if (tcharm_nothreads)
476     CkAbort("Cannot make blocking calls using +tcharm_nothreads!\n");
477 #endif
478   stopTiming();
479   isStopped=true;
480   DBG("thread suspended");
482   CthSuspend();
483 //      DBG("thread resumed");
484   /*SUBTLE: We have to do the get() because "this" may have changed
485     during a migration-suspend.  If you access *any* members
486     from this point onward, you'll cause heap corruption if
487     we're resuming from migration!  (OSL 2003/9/23)
488    */
489   TCharm *dis=TCharm::get();
490   dis->isStopped=false;
491   dis->startTiming();
492   //CkPrintf("[%d] Thread resumed  for tid %p\n",dis->thisIndex,dis->tid);
495 //Resume the waiting thread
496 void TCharm::start(void)
498   //  since this thread is scheduled, it is not a good idea to migrate 
499   isStopped=false;
500   DBG("thread resuming soon");
501   //CkPrintf("TCharm[%d]::start()\n", thisIndex);
502   //CmiPrintStackTrace(0);
503   CthAwaken(tid);
506 //Block our thread, schedule, and come back:
507 void TCharm::schedule(void) {
508   DBG("thread schedule");
509   start(); // Calls CthAwaken
510   stop(); // Calls CthSuspend
513 //Go to sync, block, possibly migrate, and then resume
514 void TCharm::migrate(void)
516 #if CMK_LBDB_ON
517   DBG("going to sync");
518   AtSync();
519   stop();
520 #else
521   DBG("skipping sync, because there is no load balancer");
522 #endif
527 void TCharm::evacuate(){
528         /*
529                 FAULT_EVAC
530         */
531         //CkClearAllArrayElementsCPP();
532         if(CkpvAccess(startedEvac)){
533                 CcdCallFnAfter((CcdVoidFn)CkEmmigrateElement, (void *)myRec, 1);
534                 suspend();
535                 return;
536         }
537         return;
541 //calls atsync with async mode
542 void TCharm::async_migrate(void)
544 #if CMK_LBDB_ON
545   DBG("going to sync at async mode");
546   ReadyMigrate(false);
547   asyncMigrate = true;
548   AtSync(0);
549   schedule();
550 #else
551   DBG("skipping sync, because there is no load balancer");
552 #endif
556 Note:
557  thread can only migrate at the point when this is called
559 void TCharm::allow_migrate(void)
561 #if CMK_LBDB_ON
562   int nextPe = MigrateToPe();
563   if (nextPe != -1) {
564     migrateTo(nextPe);
565   }
566 #else
567   DBG("skipping sync, because there is no load balancer");
568 #endif
571 //Resume from sync: start the thread again
572 void TCharm::ResumeFromSync(void)
574   DBG("thread resuming from sync");
575   start();
579 /****** TcharmClient ******/
580 void TCharmClient1D::ckJustMigrated(void) {
581   ArrayElement1D::ckJustMigrated();
582   findThread();
583   tcharmClientInit();
586 void TCharmClient1D::pup(PUP::er &p) {
587   ArrayElement1D::pup(p);
588   p|threadProxy;
591 CkArrayID TCHARM_Get_threads(void) {
592         TCHARMAPI("TCHARM_Get_threads");
593         return TCharm::get()->getProxy();
596 /************* Startup/Shutdown Coordination Support ************/
598 //Called when we want to go to a barrier
599 void TCharm::barrier(void) {
600         //Contribute to a synchronizing reduction
601         contribute(CkCallback(CkReductionTarget(TCharm, atBarrier), thisProxy[0]));
602 #if CMK_BIGSIM_CHARM
603         void *curLog;           // store current log in timeline
604         _TRACE_BG_TLINE_END(&curLog);
605         TRACE_BG_AMPI_BREAK(NULL, "TCharm_Barrier_START", NULL, 0, 1);
606 #endif
607         stop();
608 #if CMK_BIGSIM_CHARM
609          _TRACE_BG_SET_INFO(NULL, "TCHARM_Barrier_END",  &curLog, 1);
610 #endif
613 //Called when we've reached the barrier
614 void TCharm::atBarrier(void){
615         DBGX("clients all at barrier");
616         thisProxy.start(); //Just restart everybody
619 //Called when the thread is done running
620 void TCharm::done(void) {
621         //CmiPrintStackTrace(0);
622         DBG("TCharm thread "<<thisIndex<<" done")
623         if (exitWhenDone) {
624                 //Contribute to a synchronizing reduction
625                 contribute(CkCallback(CkReductionTarget(TCharm, atExit), thisProxy[0]));
626         }
627         isSelfDone = true;
628         stop();
630 //Called when all threads are done running
631 void TCharm::atExit(void) {
632         DBGX("TCharm::atExit1> exiting");
633         //thisProxy.unsetFlags();
634         CkExit();
635         //CkPrintf("After CkExit()!!!!!!!\n");
638 /************* Setup **************/
640 //Globals used to control setup process
641 static TCHARM_Fallback_setup_fn g_fallbackSetup=NULL;
642 void TCHARM_Set_fallback_setup(TCHARM_Fallback_setup_fn f)
644         g_fallbackSetup=f;
646 void TCHARM_Call_fallback_setup(void) {
647         if (g_fallbackSetup) 
648                 (g_fallbackSetup)();
649         else
650                 CkAbort("TCHARM: Unexpected fallback setup--missing TCHARM_User_setup routine?");
653 /************** User API ***************/
654 /**********************************
655 Callable from UserSetup:
658 // Read the command line to figure out how many threads to create:
659 CDECL int TCHARM_Get_num_chunks(void)
661         TCHARMAPI("TCHARM_Get_num_chunks");
662         if (CkMyPe()!=0) CkAbort("TCHARM_Get_num_chunks should only be called on PE 0 during setup!");
663         int nChunks=CkNumPes();
664         char **argv=CkGetArgv();
665         CmiGetArgIntDesc(argv,"-vp",&nChunks,"Set the total number of virtual processors");
666         CmiGetArgIntDesc(argv,"+vp",&nChunks,NULL);
667         lastNumChunks=nChunks;
668         return nChunks;
670 FDECL int FTN_NAME(TCHARM_GET_NUM_CHUNKS,tcharm_get_num_chunks)(void)
672         return TCHARM_Get_num_chunks();
675 // Fill out the default thread options:
676 TCHARM_Thread_options::TCHARM_Thread_options(int doDefault)
678         stackSize=0; /* default stacksize */
679         exitWhenDone=false; /* don't exit when done by default. */
681 void TCHARM_Thread_options::sanityCheck(void) {
682         if (stackSize<=0) stackSize=tcharm_stacksize;
686 TCHARM_Thread_options g_tcharmOptions(1);
688 /*Set the size of the thread stack*/
689 CDECL void TCHARM_Set_stack_size(int newStackSize)
691         TCHARMAPI("TCHARM_Set_stack_size");
692         g_tcharmOptions.stackSize=newStackSize;
694 FDECL void FTN_NAME(TCHARM_SET_STACK_SIZE,tcharm_set_stack_size)
695         (int *newSize)
696 { TCHARM_Set_stack_size(*newSize); }
698 CDECL void TCHARM_Set_exit(void) { g_tcharmOptions.exitWhenDone=true; }
700 /*Create a new array of threads, which will be bound to by subsequent libraries*/
701 CDECL void TCHARM_Create(int nThreads,
702                         int threadFn)
704         TCHARMAPI("TCHARM_Create");
705         TCHARM_Create_data(nThreads,
706                          threadFn,NULL,0);
708 FDECL void FTN_NAME(TCHARM_CREATE,tcharm_create)
709         (int *nThreads,int threadFn)
710 { TCHARM_Create(*nThreads,threadFn); }
712 static CProxy_TCharm TCHARM_Build_threads(TCharmInitMsg *msg);
714 /*As above, but pass along (arbitrary) data to threads*/
715 CDECL void TCHARM_Create_data(int nThreads,
716                   int threadFn,
717                   void *threadData,int threadDataLen)
719         TCHARMAPI("TCHARM_Create_data");
720         TCharmInitMsg *msg=new (threadDataLen,0) TCharmInitMsg(
721                 threadFn,g_tcharmOptions);
722         msg->numElements=nThreads;
723         memcpy(msg->data,threadData,threadDataLen);
724         TCHARM_Build_threads(msg);
725         
726         // Reset the thread options:
727         g_tcharmOptions=TCHARM_Thread_options(1);
730 FDECL void FTN_NAME(TCHARM_CREATE_DATA,tcharm_create_data)
731         (int *nThreads,
732                   int threadFn,
733                   void *threadData,int *threadDataLen)
734 { TCHARM_Create_data(*nThreads,threadFn,threadData,*threadDataLen); }
736 CkGroupID CkCreatePropMap(void);
738 static CProxy_TCharm TCHARM_Build_threads(TCharmInitMsg *msg)
740   CkArrayOptions opts(msg->numElements);
741   CkAssert(CkpvAccess(mapCreated)==true);
743   if(haveConfigurableRRMap()){
744     CkPrintf("USING ConfigurableRRMap\n");
745     mapID=CProxy_ConfigurableRRMap::ckNew();
746   } else if(mapping==NULL){
747 #if CMK_BIGSIM_CHARM
748     mapID=CProxy_BlockMap::ckNew();
749 #else
750 #if __FAULT__
751         mapID=CProxy_RRMap::ckNew();
752 #else
753     mapID=CkCreatePropMap();
754 #endif
755 #endif
756   } else if(0 == strcmp(mapping,"BLOCK_MAP")) {
757     CkPrintf("USING BLOCK_MAP\n");
758     mapID = CProxy_BlockMap::ckNew();
759   } else if(0 == strcmp(mapping,"RR_MAP")) {
760     CkPrintf("USING RR_MAP\n");
761     mapID = CProxy_RRMap::ckNew();
762   } else if(0 == strcmp(mapping,"MAPFILE")) {
763     CkPrintf("Reading map from file\n");
764     mapID = CProxy_ReadFileMap::ckNew();
765   } else {  // "PROP_MAP" or anything else
766     mapID = CkCreatePropMap();
767   }
768   opts.setMap(mapID);
769   opts.setSectionAutoDelegate(false);
770   return CProxy_TCharm::ckNew(msg,opts);
773 // Helper used when creating a new array bound to the TCHARM threads:
774 CkArrayOptions TCHARM_Attach_start(CkArrayID *retTCharmArray,int *retNumElts)
776         TCharm *tc=TCharm::get();
777         if (!tc)
778                 CkAbort("You must call TCHARM initialization routines from a TCHARM thread!");
779         int nElts=tc->getNumElements();
780       
781         //CmiPrintf("TCHARM Elements = %d\n", nElts);  
782       
783         if (retNumElts!=NULL) *retNumElts=nElts;
784         *retTCharmArray=tc->getProxy();
785         CkArrayOptions opts(nElts);
786         opts.bindTo(tc->getProxy());
787         return opts;
790 void TCHARM_Suspend(void) {
791         TCharm *tc=TCharm::get();
792         tc->suspend();
795 /***********************************
796 Callable from worker thread
798 CDECL int TCHARM_Element(void)
800         TCHARMAPI("TCHARM_Element");
801         return TCharm::get()->getElement();
803 CDECL int TCHARM_Num_elements(void)
805         TCHARMAPI("TCHARM_Num_elements");
806         return TCharm::get()->getNumElements();
809 FDECL int FTN_NAME(TCHARM_ELEMENT,tcharm_element)(void) 
810 { return TCHARM_Element();}
811 FDECL int FTN_NAME(TCHARM_NUM_ELEMENTS,tcharm_num_elements)(void) 
812 { return TCHARM_Num_elements();}
814 //Make sure this address will migrate with us when we move:
815 static void checkAddress(void *data)
817         if (tcharm_nomig||tcharm_nothreads) return; //Stack is not isomalloc'd
818         if (CmiThreadIs(CMI_THREAD_IS_ALIAS)||CmiThreadIs(CMI_THREAD_IS_STACKCOPY)) return; // memory alias thread
819         if (CmiIsomallocEnabled()) {
820           if (!CmiIsomallocInRange(data))
821             CkAbort("The UserData you register must be allocated on the stack!\n");
822         }
823         else {
824           if(CkMyPe() == 0)
825             CkPrintf("Warning> checkAddress failed because isomalloc not supported.\n");
826         }
829 /* Old "register"-based userdata: */
830 CDECL int TCHARM_Register(void *data,TCHARM_Pup_fn pfn)
832         TCHARMAPI("TCHARM_Register");
833         checkAddress(data);
834         return TCharm::get()->add(TCharm::UserData(pfn,TCharm::get()->getThread(),data));
836 FDECL int FTN_NAME(TCHARM_REGISTER,tcharm_register)
837         (void *data,TCHARM_Pup_fn pfn)
839         TCHARMAPI("TCHARM_Register");
840         checkAddress(data);
841         return TCharm::get()->add(TCharm::UserData(pfn,TCharm::get()->getThread(),data));
844 CDECL void *TCHARM_Get_userdata(int id)
846         TCHARMAPI("TCHARM_Get_userdata");
847         return TCharm::get()->lookupUserData(id);
849 FDECL void *FTN_NAME(TCHARM_GET_USERDATA,tcharm_get_userdata)(int *id)
850 { return TCHARM_Get_userdata(*id); }
852 /* New hardcoded-ID userdata: */
853 CDECL void TCHARM_Set_global(int globalID,void *new_value,TCHARM_Pup_global_fn pup_or_NULL)
855         TCHARMAPI("TCHARM_Set_global");
856         TCharm *tc=TCharm::get();
857         if (tc->sud.length()<=globalID)
858         { //We don't have room for this ID yet: make room
859                 int newLen=2*globalID;
860                 tc->sud.resize(newLen);
861         }
862         tc->sud[globalID]=TCharm::UserData(pup_or_NULL,tc->getThread(),new_value);
864 CDECL void *TCHARM_Get_global(int globalID)
866         //Skip TCHARMAPI("TCHARM_Get_global") because there's no dynamic allocation here,
867         // and this routine should be as fast as possible.
868         CkVec<TCharm::UserData> &v=TCharm::get()->sud;
869         if (v.length()<=globalID) return NULL; //Uninitialized global
870         return v[globalID].getData();
873 CDECL void TCHARM_Migrate(void)
875         TCHARMAPI("TCHARM_Migrate");
876         if (CthMigratable() == 0) {
877           if(CkMyPe() == 0)
878             CkPrintf("Warning> thread migration is not supported!\n");
879           return;
880         }
881         TCharm::get()->migrate();
883 FORTRAN_AS_C(TCHARM_MIGRATE,TCHARM_Migrate,tcharm_migrate,(void),())
885 CDECL void TCHARM_Async_Migrate(void)
887         TCHARMAPI("TCHARM_Async_Migrate");
888         TCharm::get()->async_migrate();
890 FORTRAN_AS_C(TCHARM_ASYNC_MIGRATE,TCHARM_Async_Migrate,tcharm_async_migrate,(void),())
892 CDECL void TCHARM_Allow_Migrate(void)
894         TCHARMAPI("TCHARM_Allow_Migrate");
895         TCharm::get()->allow_migrate();
897 FORTRAN_AS_C(TCHARM_ALLOW_MIGRATE,TCHARM_Allow_Migrate,tcharm_allow_migrate,(void),())
899 CDECL void TCHARM_Migrate_to(int destPE)
901         TCHARMAPI("TCHARM_Migrate_to");
902         TCharm::get()->migrateTo(destPE);
905 CDECL void TCHARM_Evacuate()
907         TCHARMAPI("TCHARM_Migrate_to");
908         TCharm::get()->evacuate();
911 FORTRAN_AS_C(TCHARM_MIGRATE_TO,TCHARM_Migrate_to,tcharm_migrate_to,
912         (int *destPE),(*destPE))
914 CDECL void TCHARM_Yield(void)
916         TCHARMAPI("TCHARM_Yield");
917         TCharm::get()->schedule();
919 FORTRAN_AS_C(TCHARM_YIELD,TCHARM_Yield,tcharm_yield,(void),())
921 CDECL void TCHARM_Barrier(void)
923         TCHARMAPI("TCHARM_Barrier");
924         TCharm::get()->barrier();
926 FORTRAN_AS_C(TCHARM_BARRIER,TCHARM_Barrier,tcharm_barrier,(void),())
928 CDECL void TCHARM_Done(void)
930         TCHARMAPI("TCHARM_Done");
931         TCharm *c=TCharm::getNULL();
932         if (!c) CkExit();
933         else c->done();
935 FORTRAN_AS_C(TCHARM_DONE,TCHARM_Done,tcharm_done,(void),())
938 CDECL double TCHARM_Wall_timer(void)
940   TCHARMAPI("TCHARM_Wall_timer");
941   TCharm *c=TCharm::getNULL();
942   if(!c) return CkWallTimer();
943   else { //Have to apply current thread's time offset
944     return CkWallTimer()+c->getTimeOffset();
945   }
948 #if 1
949 /*Include Fortran-style "iargc" and "getarg" routines.
950 These are needed to get access to the command-line arguments from Fortran.
952 FDECL int FTN_NAME(TCHARM_IARGC,tcharm_iargc)(void) {
953   TCHARMAPI("tcharm_iargc");
954   return CkGetArgc()-1;
957 FDECL void FTN_NAME(TCHARM_GETARG,tcharm_getarg)
958         (int *i_p,char *dest,int destLen)
960   TCHARMAPI("tcharm_getarg");
961   int i=*i_p;
962   if (i<0) CkAbort("tcharm_getarg called with negative argument!");
963   if (i>=CkGetArgc()) CkAbort("tcharm_getarg called with argument > iargc!");
964   const char *src=CkGetArgv()[i];
965   strcpy(dest,src);
966   for (i=strlen(dest);i<destLen;i++) dest[i]=' ';
969 #endif
971 //These silly routines are used for serial startup:
972 extern void _initCharm(int argc, char **argv);
973 CDECL void TCHARM_Init(int *argc,char ***argv) {
974         if (!tcharm_initted) {
975           ConverseInit(*argc, *argv, (CmiStartFn) _initCharm,1,1);
976           _initCharm(*argc,*argv);
977         }
980 FDECL void FTN_NAME(TCHARM_INIT,tcharm_init)(void)
982         int argc=1;
983         const char *argv_sto[2]={"foo",NULL};
984         char **argv=(char **)argv_sto;
985         TCHARM_Init(&argc,&argv);
988 /***********************************
989 * TCHARM Semaphores:
990 * The idea is one side "puts", the other side "gets"; 
991 * but the calls can come in any order--
992 * if the "get" comes first, it blocks until the put.
993 * This makes a convenient, race-condition-free way to do
994 * onetime initializations.  
996 /// Find this semaphore, or insert if there isn't one:
997 TCharm::TCharmSemaphore *TCharm::findSema(int id) {
998         for (unsigned int s=0;s<sema.size();s++)
999                 if (sema[s].id==id) 
1000                         return &sema[s];
1001         sema.push_back(TCharmSemaphore(id));
1002         return &sema[sema.size()-1];
1004 /// Remove this semaphore from the list
1005 void TCharm::freeSema(TCharmSemaphore *doomed) {
1006         int id=doomed->id;
1007         for (unsigned int s=0;s<sema.size();s++)
1008                 if (sema[s].id==id) {
1009                         sema[s]=sema[sema.length()-1];
1010                         sema.length()--;
1011                         return;
1012                 }
1013         CkAbort("Tried to free nonexistent TCharm semaphore");
1016 /// Block until this semaphore has data:
1017 TCharm::TCharmSemaphore *TCharm::getSema(int id) {
1018         TCharmSemaphore *s=findSema(id);
1019         if (s->data==NULL) 
1020         { //Semaphore isn't filled yet: wait until it is
1021                 s->thread=CthSelf();
1022                 suspend(); //Will be woken by semaPut
1023                 // Semaphore may have moved-- find it again
1024                 s=findSema(id);
1025                 if (s->data==NULL) CkAbort("TCharm::semaGet awoken too early!");
1026         }
1027         return s;
1030 /// Store data at the semaphore "id".
1031 ///  The put can come before or after the get.
1032 void TCharm::semaPut(int id,void *data) {
1033         TCharmSemaphore *s=findSema(id);
1034         if (s->data!=NULL) CkAbort("Duplicate calls to TCharm::semaPut!");
1035         s->data=data;
1036         DBG("semaPut "<<id<<" "<<data);
1037         if (s->thread!=NULL) {//Awaken the thread
1038                 s->thread=NULL;
1039                 resume();
1040         }
1043 /// Retreive data from the semaphore "id".
1044 ///  Blocks if the data is not immediately available.
1045 ///  Consumes the data, so another put will be required for the next get.
1046 void *TCharm::semaGet(int id) {
1047         TCharmSemaphore *s=getSema(id);
1048         void *ret=s->data;
1049         DBG("semaGet "<<id<<" "<<ret);
1050         // Now remove the semaphore from the list:
1051         freeSema(s);
1052         return ret;
1055 /// Retreive data from the semaphore "id".
1056 ///  Blocks if the data is not immediately available.
1057 void *TCharm::semaGets(int id) {
1058         TCharmSemaphore *s=getSema(id);
1059         return s->data;
1062 /// Retreive data from the semaphore "id", or returns NULL.
1063 void *TCharm::semaPeek(int id) {
1064         TCharmSemaphore *s=findSema(id);
1065         return s->data;
1068 /****** System Call support ******/
1070 TCHARM_System exists to work around a bug where Linux ia32
1071 glibc2.2.x with pthreads crashes at the fork() site when 
1072 called from a user-levelthread. 
1074 The fix is to call system from the main thread, by 
1075 passing the request out of the thread to our array element 
1076 before calling system().
1079 CDECL int 
1080 TCHARM_System(const char *shell_command)
1082         return TCharm::get()->system(shell_command);
1084 int TCharm::system(const char *cmd)
1086         int ret=-1778;
1087         callSystemStruct s;
1088         s.cmd=cmd;
1089         s.ret=&ret;
1090         thisProxy[thisIndex].callSystem(s);
1091         suspend();
1092         return ret;
1095 void TCharm::callSystem(const callSystemStruct &s)
1097         *s.ret = ::system(s.cmd);
1098         resume();
1103 #include "tcharm.def.h"