2 Threaded Charm++ "Framework Framework"
4 Orion Sky Lawlor, olawlor@acm.org, 11/19/2001
6 #include "tcharm_impl.h"
9 #include "ckevacuation.h"
13 /*Many debugging statements:*/
14 # define DBG(x) ckout<<"["<<thisIndex<<","<<CkMyPe()<<"] TCHARM> "<<x<<endl;
15 # define DBGX(x) ckout<<"PE("<<CkMyPe()<<") TCHARM> "<<x<<endl;
17 /*No debugging statements*/
18 # define DBG(x) /*empty*/
19 # define DBGX(x) /*empty*/
22 CtvDeclare(TCharm *,_curTCharm);
24 #if CMK_USE_MEMPOOL_ISOMALLOC
27 CtvExtern(mempool_type *, threadpool);
31 static int lastNumChunks=0;
33 class TCharmTraceLibList {
34 enum {maxLibs=20,maxLibNameLen=15};
35 //List of libraries we want to trace:
37 char libNames[maxLibs][maxLibNameLen];
38 int checkIfTracing(const char *lib) const
40 for (int i=0;i<curLibs;i++)
41 if (0==strcmp(lib,libNames[i]))
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);
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;
58 inline bool isTracing(const char *lib) const {
59 if (curLibs==0) return 0; //Common case
60 else return checkIfTracing(lib);
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;
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)");
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;
100 else if (strpbrk(str,"K")) {
101 sscanf(str, "%dK", &tcharm_stacksize);
102 tcharm_stacksize *= 1024;
105 sscanf(str, "%d", &tcharm_stacksize);
108 CkPrintf("TCharm> stack size is set to %d.\n", tcharm_stacksize);
110 if (CkMyPe()!=0) { //Processor 0 eats "+vp<N>" and "-vp<N>" later:
112 while (CmiGetArgIntDesc(argv,"-vp",&ignored,NULL)) {}
113 while (CmiGetArgIntDesc(argv,"+vp",&ignored,NULL)) {}
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");
119 if (!CkpvAccess(mapCreated)) {
120 if (0!=CmiGetArgString(argv, "+mapping", &mapping)){
122 CkpvAccess(mapCreated)=true;
126 void TCHARM_Api_trace(const char *routineName,const char *libraryName)
128 if (!tcharm_tracelibs.isTracing(libraryName)) return;
129 TCharm *tc=CtvAccess(_curTCharm);
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);
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)
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);
163 TCharm::deactivateThread();
164 CtvAccess(_curTCharm)->done();
167 TCharm::TCharm(TCharmInitMsg *initMsg_)
170 initMsg->opts.sanityCheck();
172 if (tcharm_nothreads)
173 { //Don't even make a new thread-- just use main thread
176 else /*Create a thread normally*/
178 if (tcharm_nomig) { /*Nonmigratable version, for debugging*/
179 tid=CthCreate((CthVoidFn)startTCharmThread,initMsg,initMsg->opts.stackSize);
181 tid=CthCreateMigratable((CthVoidFn)startTCharmThread,initMsg,initMsg->opts.stackSize);
185 BgUnsetStartOutOfCore();
188 threadGlobals=CtgCreate(tid);
189 CtvAccessOther(tid,_curTCharm)=this;
190 asyncMigrate = false;
194 exitWhenDone=initMsg->opts.exitWhenDone;
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);
208 TCharm::TCharm(CkMigrateMessage *msg)
214 threadInfo.tProxy=CProxy_TCharm(thisArrayID);
219 void checkPupMismatch(PUP::er &p,int expected,const char *where)
224 CkError("FATAL ERROR> Mismatch %s pup routine\n",where);
225 CkAbort("FATAL ERROR: Pup direction mismatch");
229 void TCharm::pup(PUP::er &p) {
230 //BIGSIM_OOC DEBUGGING
231 //if(!p.isUnpacking()){
232 // CmiPrintf("TCharm[%d] packing: ", thisIndex);
233 // CthPrintThdStack(tid);
236 checkPupMismatch(p,5134,"before TCHARM");
237 p(isStopped); p(exitWhenDone); p(isSelfDone); p(asyncMigrate);
238 p(threadInfo.thisElement);
239 p(threadInfo.numElements);
242 CkAbort("TCharm::pup> Cannot migrate with unconsumed semaphores!\n");
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");
251 if (tcharm_nomig && !p.isSizing()) CkAbort("Cannot migrate with the +tcharm_nomig option!\n");
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);
260 {//In this case, unpack the thread & heap before the user data
263 //Restart our clock: set it up so packTime==CkWallTimer+timeOffset
266 timeOffset=packTime-CkWallTimer();
270 // Set up TCHARM context for use during user's pup routines:
272 CtvAccess(_curTCharm)=this;
277 checkPupMismatch(p,5135,"before TCHARM user data");
279 for(int i=0;i<nUd;i++) {
280 if (p.isUnpacking()) ud[i].update(tid);
283 checkPupMismatch(p,5137,"after TCHARM_Register user data");
286 if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
289 p|sud; // sud vector block can not be in isomalloc
290 checkPupMismatch(p,5138,"after TCHARM_Global user data");
292 // Tear down TCHARM context after calling user pup routines
294 if (!CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
296 CtvAccess(_curTCharm)=NULL;
299 if (!p.isUnpacking())
300 {//In this case, pack the thread & heap after the user data
304 double packTime=CkWallTimer()+timeOffset;
308 s.endBlock(); //End of seeking block
309 checkPupMismatch(p,5140,"after TCHARM");
311 //BIGSIM_OOC DEBUGGING
312 //if(p.isUnpacking()){
313 // CmiPrintf("TCharm[%d] unpacking: ", thisIndex);
314 // CthPrintThdStack(tid);
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);
326 if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
327 CmiIsomallocBlockListPup(p,&heapBlocks,tid);
329 tid = CthPup(p, tid);
330 if (pc.isUnpacking()) {
331 CtvAccessOther(tid,_curTCharm)=this;
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);
346 case 'c': { /* C mode: userdata is on the stack, so keep address */
347 // p((char*)&data,sizeof(data));
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);
354 case 'g': { /* Global mode: zero out userdata on arrival */
355 if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
357 // keep the pointer value if using isomalloc, no need to use pup
360 else if (p.isUnpacking()) // zero out userdata on arrival
363 //FIXME: function pointers may not be valid across processors
364 p((char*)&gfn, sizeof(TCHARM_Pup_global_fn));
374 //BIGSIM_OOC DEBUGGING
375 //CmiPrintf("TCharm destructor called with heapBlocks=%p!\n", heapBlocks);
377 #if CMK_USE_MEMPOOL_ISOMALLOC
378 mempool_type *mptr = NULL;
379 if(tid != NULL) mptr = CtvAccessOther(tid,threadpool);
381 if (heapBlocks) CmiIsomallocBlockListDelete(heapBlocks);
384 CtgFree(threadGlobals);
385 #if CMK_USE_MEMPOOL_ISOMALLOC
386 if(mptr != NULL) mempool_destroy(mptr);
391 void TCharm::migrateTo(int destPE) {
392 if (destPE==CkMyPe()) return;
393 if (CthMigratable() == 0) {
394 CkPrintf("Warning> thread migration is not supported!\n");
398 // Make sure migrateMe gets called *after* we suspend:
399 thisProxy[thisIndex].migrateDelayed(destPE);
402 void TCharm::migrateDelayed(int destPE) {
405 void TCharm::ckJustMigrated(void) {
406 ArrayElement::ckJustMigrated();
408 asyncMigrate = false;
413 void TCharm::ckJustRestored(void) {
414 ArrayElement::ckJustRestored();
420 If a Tcharm object is about to migrate it should be suspended first
422 void TCharm::ckAboutToMigrate(void){
423 ArrayElement::ckAboutToMigrate();
427 // clear the data before restarting from disk
430 #if CMK_USE_MEMPOOL_ISOMALLOC
431 mempool_type *mptr = NULL;
432 if(tid != NULL) mptr = CtvAccessOther(tid,threadpool);
434 if (heapBlocks) CmiIsomallocBlockListDelete(heapBlocks);
437 #if CMK_USE_MEMPOOL_ISOMALLOC
438 if(mptr != NULL) mempool_destroy(mptr);
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");
452 void *TCharm::lookupUserData(int i) {
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);
465 else /* start the thread as usual */
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");
480 DBG("thread suspended");
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)
489 TCharm *dis=TCharm::get();
490 dis->isStopped=false;
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
500 DBG("thread resuming soon");
501 //CkPrintf("TCharm[%d]::start()\n", thisIndex);
502 //CmiPrintStackTrace(0);
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)
517 DBG("going to sync");
521 DBG("skipping sync, because there is no load balancer");
527 void TCharm::evacuate(){
531 //CkClearAllArrayElementsCPP();
532 if(CkpvAccess(startedEvac)){
533 CcdCallFnAfter((CcdVoidFn)CkEmmigrateElement, (void *)myRec, 1);
541 //calls atsync with async mode
542 void TCharm::async_migrate(void)
545 DBG("going to sync at async mode");
551 DBG("skipping sync, because there is no load balancer");
557 thread can only migrate at the point when this is called
559 void TCharm::allow_migrate(void)
562 int nextPe = MigrateToPe();
567 DBG("skipping sync, because there is no load balancer");
571 //Resume from sync: start the thread again
572 void TCharm::ResumeFromSync(void)
574 DBG("thread resuming from sync");
579 /****** TcharmClient ******/
580 void TCharmClient1D::ckJustMigrated(void) {
581 ArrayElement1D::ckJustMigrated();
586 void TCharmClient1D::pup(PUP::er &p) {
587 ArrayElement1D::pup(p);
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]));
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);
609 _TRACE_BG_SET_INFO(NULL, "TCHARM_Barrier_END", &curLog, 1);
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")
624 //Contribute to a synchronizing reduction
625 contribute(CkCallback(CkReductionTarget(TCharm, atExit), thisProxy[0]));
630 //Called when all threads are done running
631 void TCharm::atExit(void) {
632 DBGX("TCharm::atExit1> exiting");
633 //thisProxy.unsetFlags();
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)
646 void TCHARM_Call_fallback_setup(void) {
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;
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)
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,
704 TCHARMAPI("TCHARM_Create");
705 TCHARM_Create_data(nThreads,
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,
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);
726 // Reset the thread options:
727 g_tcharmOptions=TCHARM_Thread_options(1);
730 FDECL void FTN_NAME(TCHARM_CREATE_DATA,tcharm_create_data)
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){
748 mapID=CProxy_BlockMap::ckNew();
751 mapID=CProxy_RRMap::ckNew();
753 mapID=CkCreatePropMap();
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();
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();
778 CkAbort("You must call TCHARM initialization routines from a TCHARM thread!");
779 int nElts=tc->getNumElements();
781 //CmiPrintf("TCHARM Elements = %d\n", nElts);
783 if (retNumElts!=NULL) *retNumElts=nElts;
784 *retTCharmArray=tc->getProxy();
785 CkArrayOptions opts(nElts);
786 opts.bindTo(tc->getProxy());
790 void TCHARM_Suspend(void) {
791 TCharm *tc=TCharm::get();
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");
825 CkPrintf("Warning> checkAddress failed because isomalloc not supported.\n");
829 /* Old "register"-based userdata: */
830 CDECL int TCHARM_Register(void *data,TCHARM_Pup_fn pfn)
832 TCHARMAPI("TCHARM_Register");
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");
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);
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) {
878 CkPrintf("Warning> thread migration is not supported!\n");
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();
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();
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");
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];
966 for (i=strlen(dest);i<destLen;i++) dest[i]=' ';
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);
980 FDECL void FTN_NAME(TCHARM_INIT,tcharm_init)(void)
983 const char *argv_sto[2]={"foo",NULL};
984 char **argv=(char **)argv_sto;
985 TCHARM_Init(&argc,&argv);
988 /***********************************
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++)
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) {
1007 for (unsigned int s=0;s<sema.size();s++)
1008 if (sema[s].id==id) {
1009 sema[s]=sema[sema.length()-1];
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);
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
1025 if (s->data==NULL) CkAbort("TCharm::semaGet awoken too early!");
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!");
1036 DBG("semaPut "<<id<<" "<<data);
1037 if (s->thread!=NULL) {//Awaken the thread
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);
1049 DBG("semaGet "<<id<<" "<<ret);
1050 // Now remove the semaphore from the list:
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);
1062 /// Retreive data from the semaphore "id", or returns NULL.
1063 void *TCharm::semaPeek(int id) {
1064 TCharmSemaphore *s=findSema(id);
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().
1080 TCHARM_System(const char *shell_command)
1082 return TCharm::get()->system(shell_command);
1084 int TCharm::system(const char *cmd)
1090 thisProxy[thisIndex].callSystem(s);
1095 void TCharm::callSystem(const callSystemStruct &s)
1097 *s.ret = ::system(s.cmd);
1103 #include "tcharm.def.h"