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