Changed "iargc/getarg" to "tcharm_iargc/tcharm_getarg".
[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 <ctype.h>
9
10 #if 0
11     /*Many debugging statements:*/
12 #    define DBG(x) ckout<<"["<<thisIndex<<"] TCHARM> "<<x<<endl;
13 #    define DBGX(x) ckout<<"PE("<<CkMyPe()<<") TCHARM> "<<x<<endl;
14 #else
15     /*No debugging statements*/
16 #    define DBG(x) /*empty*/
17 #    define DBGX(x) /*empty*/
18 #endif
19
20 CtvDeclare(TCharm *,_curTCharm);
21 CpvDeclare(inState,_stateTCharm);
22
23 static int lastNumChunks=0;
24 /*readonly*/ int tcharm_nomig=0, tcharm_nothreads=0;
25
26 void TCharm::nodeInit(void)
27 {
28   CtvInitialize(TCharm *,_curTCharm);
29   CtvAccess(_curTCharm)=NULL;
30   CpvInitialize(inState,_stateTCharm);
31   TCharm::setState(inNodeSetup);
32   TCharmUserNodeSetup();
33   FTN_NAME(TCHARM_USER_NODE_SETUP,tcharm_user_node_setup)();
34   TCharm::setState(inInit);
35 }
36
37 class TCharmTraceLibList {
38         enum {maxLibs=20,maxLibNameLen=15};
39         //List of libraries we want to trace:
40         int curLibs;
41         char libNames[maxLibs][maxLibNameLen];
42         int checkIfTracing(const char *lib) const
43         {
44                 for (int i=0;i<curLibs;i++) 
45                         if (0==strcmp(lib,libNames[i]))
46                                 return 1;
47                 return 0;
48         }
49 public:
50         TCharmTraceLibList() {curLibs=0;}
51         void addTracing(const char *lib) 
52         { //We want to trace this library-- add its name to the list.
53                 CkPrintf("TCHARM> Will trace calls to library %s\n",lib);
54                 int i;
55                 for (i=0;0!=*lib;i++,lib++)
56                         libNames[curLibs][i]=tolower(*lib);
57                 libNames[curLibs][i]=0;
58                 curLibs++;
59         }
60         inline int isTracing(const char *lib) const {
61                 if (curLibs==0) return 0; //Common case
62                 else return checkIfTracing(lib);
63         }
64 };
65 TCharmTraceLibList tcharm_tracelibs;
66
67 void TCharmApiTrace(const char *routineName,const char *libraryName)
68 {
69         if (!tcharm_tracelibs.isTracing(libraryName)) return;
70         TCharm *tc=CtvAccess(_curTCharm);
71         char where[100];
72         if (tc==NULL) sprintf(where,"[serial context on %d]",CkMyPe());
73         else sprintf(where,"[vp %d, p %d]",tc->getElement(),CkMyPe());
74         CmiPrintf("%s Called routine %s\n",where,routineName);
75 }
76
77 static void startTCharmThread(TCharmInitMsg *msg)
78 {
79         TCharm::setState(inDriver);
80         CtvAccess(_curTCharm)->activateHeap();
81         typedef void (*threadFn_t)(void *);
82         ((threadFn_t)msg->threadFn)(msg->data);
83         CmiIsomallocBlockListActivate(NULL); //Turn off migratable memory
84         CtvAccess(_curTCharm)->done();
85 }
86
87 TCharm::TCharm(TCharmInitMsg *initMsg_)
88 {
89   initMsg=initMsg_;
90   timeOffset=0.0;
91   if (tcharm_nothreads) 
92   { //Don't even make a new thread-- just use main thread
93     tid=CthSelf();
94   } 
95   else /*Create a thread normally*/
96   {
97     if (tcharm_nomig) /*Nonmigratable version, for debugging*/ 
98       tid=CthCreate((CthVoidFn)startTCharmThread,initMsg,initMsg->stackSize);
99     else
100       tid=CthCreateMigratable((CthVoidFn)startTCharmThread,initMsg,initMsg->stackSize);
101   }
102   CtvAccessOther(tid,_curTCharm)=this;
103   TCharm::setState(inInit);
104   isStopped=true;
105   threadInfo.tProxy=CProxy_TCharm(thisArrayID);
106   threadInfo.thisElement=thisIndex;
107   threadInfo.numElements=initMsg->numElements;
108   heapBlocks=CmiIsomallocBlockListNew();
109   nUd=0;
110   usesAtSync=CmiTrue;
111   ready();
112 }
113
114 TCharm::TCharm(CkMigrateMessage *msg)
115         :ArrayElement1D(msg)
116 {
117   initMsg=NULL;
118   tid=NULL;
119   threadInfo.tProxy=CProxy_TCharm(thisArrayID);  
120 }
121
122 void TCharm::pup(PUP::er &p) {
123 //Pup superclass
124   ArrayElement1D::pup(p);  
125
126   p(isStopped);
127   p(threadInfo.thisElement);
128   p(threadInfo.numElements);
129
130 #ifndef CMK_OPTIMIZE
131   DBG("Packing thread");
132   if (!isStopped)
133     CkAbort("Cannot pup a running thread.  You must suspend before migrating.\n");
134   if (tcharm_nomig) CkAbort("Cannot migrate with the +tcharm_nomig option!\n");
135 #endif
136
137 //Pup thread (EVIL & UGLY):
138   //This seekBlock allows us to reorder the packing/unpacking--
139   // This is needed because the userData depends on the thread's stack
140   // and heap data both at pack and unpack time.
141   PUP::seekBlock s(p,2);
142   if (p.isUnpacking()) 
143   {//In this case, unpack the thread & heap before the user data
144     s.seek(1);
145     tid = CthPup((pup_er) &p, tid);
146     CtvAccessOther(tid,_curTCharm)=this;
147     CmiIsomallocBlockListPup((pup_er) &p,&heapBlocks);
148     //Restart our clock: set it up so packTime==CkWallTimer+timeOffset
149     double packTime;
150     p(packTime);
151     timeOffset=packTime-CkWallTimer();
152   }
153   
154   //Pack all user data
155   TCharm::setState(inPup);
156   s.seek(0);
157   p(nUd);
158   for(int i=0;i<nUd;i++) 
159     ud[i].pup(p);
160   TCharm::setState(inFramework);
161
162   if (!p.isUnpacking()) 
163   {//In this case, pack the thread & heap after the user data
164     s.seek(1);
165     tid = CthPup((pup_er) &p, tid);
166     CmiIsomallocBlockListPup((pup_er) &p,&heapBlocks);
167     //Stop our clock:
168     double packTime=CkWallTimer()+timeOffset;
169     p(packTime);
170   }
171   s.endBlock(); //End of seeking block
172 }
173
174 //Pup one group of user data
175 void TCharm::UserData::pup(PUP::er &p)
176 {
177   pup_er pext=(pup_er)(&p);
178   p(isC);
179   //Save address of userdata-- assumes user data is on the stack
180   p((void*)&data,sizeof(data));
181   if (isC) { //C version
182     //FIXME: function pointers may not be valid across processors
183     p((void*)&cfn, sizeof(TCpupUserDataC));
184     cfn(pext,data);
185   } 
186   else { //Fortran version
187     //FIXME: function pointers may not be valid across processors
188     p((void*)&ffn, sizeof(TCpupUserDataF));        
189     ffn(pext,data);
190   }
191 }
192
193 TCharm::~TCharm() 
194 {
195   CmiIsomallocBlockListDelete(heapBlocks);
196   CthFree(tid);
197   delete initMsg;
198 }
199
200 //Register user data to be packed with the thread
201 int TCharm::add(const TCharm::UserData &d)
202 {
203   if (nUd>=maxUserData)
204     CkAbort("TCharm: Registered too many user data fields!\n");
205   int nu=nUd++;
206   ud[nu]=d;
207   return nu;
208 }
209 void *TCharm::lookupUserData(int i) {
210         if (i<0 || i>=nUd)
211                 CkAbort("Bad user data index passed to TCharmGetUserdata!\n");
212         return ud[i].getData();
213 }
214
215 //Start the thread running
216 void TCharm::run(void)
217 {
218   DBG("TCharm::run()");
219   start();
220 }
221
222 //Block the thread until start()ed again.
223 void TCharm::stop(void)
224 {
225   if (isStopped) return; //Nothing to do
226 #ifndef CMK_OPTIMIZE
227   DBG("suspending thread");
228   if (tid != CthSelf())
229     CkAbort("Called TCharm::stop from outside TCharm thread!\n");
230   if (tcharm_nothreads)
231     CkAbort("Cannot make blocking calls using +tcharm_nothreads!\n");
232 #endif
233   isStopped=true;
234   stopTiming();
235   TCharm::setState(inFramework);
236   CthSuspend();
237   TCharm::setState(inDriver);
238   /*We have to do the get() because "this" may have changed
239     during a migration-suspend.*/
240   TCharm::get()->startTiming();
241 }
242
243 //Resume the waiting thread
244 void TCharm::start(void)
245 {
246   if (!isStopped) return; //Already started
247   isStopped=false;
248   TCharm::setState(inDriver);
249   DBG("awakening thread");
250   if (tcharm_nothreads) /*Call user routine directly*/
251           startTCharmThread(initMsg);
252   else /*Jump to thread normally*/
253           CthAwaken(tid);
254 }
255
256 //Go to sync, block, possibly migrate, and then resume
257 void TCharm::migrate(void)
258 {
259 #if CMK_LBDB_ON
260   DBG("going to sync");  
261   AtSync();
262   stop();
263 #else
264   DBG("skipping sync, because there is no load balancer");
265 #endif
266 }
267
268 //Resume from sync: start the thread again
269 void TCharm::ResumeFromSync(void)
270 {
271   start();
272 }
273
274 #ifndef CMK_OPTIMIZE
275 //Make sure we're actually in driver
276 void TCharm::check(void)
277 {
278         if (getState()!=inDriver)
279                 ::CkAbort("TCharm> Can only use that routine from within driver!\n");
280 }
281 #endif
282
283 static int propMapCreated=0;
284 static CkGroupID propMapID;
285 CkGroupID CkCreatePropMap(void);
286
287 static void TCharmBuildThreads(TCharmInitMsg *msg,TCharmSetupCookie &cook)
288 {
289         CkArrayOptions opts(msg->numElements);
290         if (!propMapCreated) {
291                 propMapCreated=1;
292                 propMapID=CkCreatePropMap();
293         }
294         opts.setMap(propMapID);
295         int nElem=msg->numElements; //<- save it because msg will be deleted.
296         CkArrayID id=CProxy_TCharm::ckNew(msg,opts);
297         cook.setThreads(id,nElem);
298 }
299
300 /****** TcharmClient ******/
301 void TCharmClient1D::ckJustMigrated(void) {
302   ArrayElement1D::ckJustMigrated();
303   tcharmClientInit();
304 }
305
306 void TCharmClient1D::pup(PUP::er &p) {
307   ArrayElement1D::pup(p);
308   p|threadProxy;
309 }
310
311
312 /****** Readonlys *****/
313 CkVec<TCpupReadonlyGlobal> TCharmReadonlys::entries;
314 void TCharmReadonlys::add(TCpupReadonlyGlobal fn)
315 {
316         entries.push_back(fn);
317 }
318
319 //Pups all registered readonlys
320 void TCharmReadonlys::pupAllReadonlys(PUP::er &p) {
321         //Pup the globals for this node:
322         int i,n=entries.length();
323         p.comment("TCharm Readonly global variables:");
324         p(n);
325         if (n!=entries.length())
326                 CkAbort("TCharmReadonly list length mismatch!\n");
327         for (i=0;i<n;i++)
328                 (entries[i])((pup_er)&p);
329 }
330
331 void TCharmReadonlys::pup(PUP::er &p) {
332         if (p.isUnpacking()) {
333                 //HACK: Rather than sending this message only where its needed,
334                 // we send it everywhere and just ignore it if it's not needed.
335                 if (CkMyPe()==0) return; //Processor 0 is the source-- no unpacking needed
336                 if (CkMyRank()!=0) return; //Some other processor will do the unpacking
337         }
338         pupAllReadonlys(p);
339 }
340
341 CDECL void TCharmReadonlyGlobals(TCpupReadonlyGlobal fn)
342 {
343         TCHARMAPI("TCharmReadonlyGlobals");
344         if (TCharm::getState()!=inNodeSetup)
345                 CkAbort("Can only call TCharmReadonlyGlobals from in TCharmUserNodeSetup!\n");
346         TCharmReadonlys::add(fn);
347 }
348 FDECL void FTN_NAME(TCHARM_READONLY_GLOBALS,tcharm_readonly_globals)
349         (TCpupReadonlyGlobal fn)
350 {
351         TCharmReadonlyGlobals(fn);
352 }
353
354 /************* Startup/Shutdown Coordination Support ************/
355
356 enum {TC_READY=23, TC_BARRIER=87, TC_DONE=42};
357
358 //Called when a client is ready to run
359 void TCharm::ready(void) {
360         DBG("TCharm thread "<<thisIndex<<" ready")
361         int vals[2]={0,1};
362         if (thisIndex==0) vals[0]=TC_READY;
363         //Contribute to a synchronizing reduction
364         contribute(sizeof(vals),&vals,CkReduction::sum_int);
365 }
366
367 //Called when we want to go to a barrier
368 void TCharm::barrier(void) {
369         int vals[2]={0,1};
370         if (thisIndex==0) vals[0]=TC_BARRIER;
371         //Contribute to a synchronizing reduction
372         contribute(sizeof(vals),&vals,CkReduction::sum_int);
373         stop();
374 }
375
376 //Called when the thread is done running
377 void TCharm::done(void) {
378         DBG("TCharm thread "<<thisIndex<<" done")
379         int vals[2]={0,1};
380         if (thisIndex==0) vals[0]=TC_DONE;
381         //Contribute to a synchronizing reduction
382         contribute(sizeof(vals),&vals,CkReduction::sum_int);
383         stop();
384 }
385
386 //Called when an array reduction is complete
387 static void coordinatorReduction(void *coord_,int dataLen,void *reductionData)
388 {
389         TCharmCoordinator *coord=(TCharmCoordinator *)coord_;
390         int *vals=(int *)reductionData;
391         if (dataLen!=2*sizeof(int))
392                 CkAbort("Unexpected length in TCharm array reduction!\n");
393         DBGX("Finished coordinator reduction: "<<vals[0]<<", "<<vals[1]);
394         switch (vals[0]) {
395         case TC_READY: coord->clientReady(); break;
396         case TC_BARRIER: coord->clientBarrier(); break;
397         case TC_DONE: coord->clientDone(); break;
398         default:
399                 CkAbort("Unexpected value from TCharm array reduction!\n");
400         };
401 }
402
403 int TCharmCoordinator::nArrays=0; //Total number of running thread arrays
404 TCharmCoordinator *TCharmCoordinator::head=NULL; //List of coordinators
405
406
407 TCharmCoordinator::TCharmCoordinator(CkArrayID threads_,int nThreads_)
408         :threads(threads_), nThreads(nThreads_), nClients(0), nReady(0)
409 {
410         nArrays++;
411         //Link into the coordinator list
412         next=head;
413         head=this;
414
415         threads.setReductionClient(coordinatorReduction,this);
416         nClients=1; //Thread array itself is a client
417 }
418 TCharmCoordinator::~TCharmCoordinator()
419 {
420         //Coordinators never get deleted
421 }
422 void TCharmCoordinator::addClient(const CkArrayID &client)
423 {
424         nClients++;
425 }
426 void TCharmCoordinator::clientReady(void)
427 {
428         DBGX("client "<<nReady+1<<" of "<<nClients<<" ready");
429         nReady++;
430         if (nReady>=nClients) { //All client arrays are ready-- start threads
431                 DBGX("starting threads");
432                 threads.run();
433         }
434 }
435 void TCharmCoordinator::clientBarrier(void)
436 {
437         DBGX("clients all at barrier");
438         threads.run();
439 }
440 void TCharmCoordinator::clientDone(void)
441 {
442         DBGX("clientDone");     
443         nArrays--;
444         if (nArrays<=0) { //All arrays have exited
445                 DBGX("done with computation");
446                 CkExit();
447         }
448 }
449
450 /************* Setup **************/
451
452 //Cookie used during setup
453 TCharmSetupCookie *TCharmSetupCookie::theCookie;
454
455 //Globals used to control setup process
456 static int g_numDefaultSetups=0;
457 static TCharmFallbackSetupFn g_fallbackSetup=NULL;
458 void TCharmSetFallbackSetup(TCharmFallbackSetupFn f)
459 {
460         g_fallbackSetup=f;
461 }
462 CDECL void TCharmInDefaultSetup(void) {
463         g_numDefaultSetups++;
464 }
465
466 //Tiny simple main chare
467 class TCharmMain : public Chare {
468 public:
469   TCharmMain(CkArgMsg *msg) {
470     if (0!=(tcharm_nomig=CmiGetArgFlag(msg->argv,"+tcharm_nomig")))
471         CmiPrintf("TCHARM> Disabling migration support, for debugging\n");
472     tcharm_nothreads=CmiGetArgFlag(msg->argv,"+tcharm_nothread");
473     tcharm_nothreads|=CmiGetArgFlag(msg->argv,"+tcharm_nothreads");
474     if (0!=tcharm_nothreads)
475        CmiPrintf("TCHARM> Disabling thread support, for debugging\n");
476     char *traceLibName=NULL;
477     while (CmiGetArgString(msg->argv,"+tcharm_trace",&traceLibName))
478        tcharm_tracelibs.addTracing(traceLibName);
479
480     TCharmSetupCookie cookie(msg->argv);
481     TCharmSetupCookie::theCookie=&cookie;
482     g_numDefaultSetups=0;
483     
484     /*Call user-overridable C setup*/
485     TCharmUserSetup();
486     /*Call user-overridable Fortran setup*/
487     FTN_NAME(TCHARM_USER_SETUP,tcharm_user_setup)();
488     
489     if (g_numDefaultSetups==2) 
490     { //User didn't override either setup routine
491             if (g_fallbackSetup)
492                     (g_fallbackSetup)();
493             else
494                     CmiAbort("You need to override TCharmUserSetup to start your computation, or else link in a framework module\n");
495     }       
496     
497     delete msg;
498     
499     if (0==TCharmCoordinator::getTotal())
500             CkAbort("You didn't create any TCharm arrays in TCharmUserSetup!\n");
501
502     //Send out the readonly globals:
503     TCharmReadonlys r;
504     CProxy_TCharmReadonlyGroup::ckNew(r);
505   }
506 };
507
508 #ifndef CMK_OPTIMIZE
509 /*The setup cookie, used to store global initialization state*/
510 TCharmSetupCookie &TCharmSetupCookie::check(void)
511 {
512         if (magic!=correctMagic)
513                 CkAbort("TCharm setup cookie is damaged!\n");
514         return *this;
515 }
516 #endif
517
518 void TCharmSetupCookie::setThreads(const CkArrayID &aid,int nel)
519 {
520         coord=new TCharmCoordinator(aid,nel);
521         tc=aid; numElements=nel;
522 }
523
524 TCharmSetupCookie::TCharmSetupCookie(char **argv_)
525 {
526         magic=correctMagic;
527         argv=argv_;
528         coord=NULL;
529         stackSize=1*1024*1024; /*Default stack size is 1MB*/
530         CmiGetArgInt(argv,"+tcharm_stacksize",&stackSize);
531 }
532
533 CkArrayOptions TCharmAttachStart(CkArrayID *retTCharmArray,int *retNumElts)
534 {
535         TCharmSetupCookie *tc=TCharmSetupCookie::get();
536         if (!tc->hasThreads())
537                 CkAbort("You must create a thread array with TCharmCreate before calling Attach!\n");
538         int nElts=tc->getNumElements();
539         if (retNumElts!=NULL) *retNumElts=nElts;
540         *retTCharmArray=tc->getThreads();
541         CkArrayOptions opts(nElts);
542         opts.bindTo(tc->getThreads());
543         return opts;
544 }
545 void TCharmAttachFinish(const CkArrayID &libraryArray)
546 {
547         TCharmSetupCookie *tc=TCharmSetupCookie::get();
548         tc->addClient(libraryArray);
549 }
550
551
552 /************** User API ***************/
553
554 #define cookie (*TCharmSetupCookie::get())
555
556 /**********************************
557 Callable from UserSetup: 
558 */
559
560 /*Set the size of the thread stack*/
561 CDECL void TCharmSetStackSize(int newStackSize)
562 {
563         TCHARMAPI("TCharmSetStackSize");
564         if (TCharm::getState()!=inInit)
565                 CkAbort("TCharm> Can only set stack size from in init!\n");
566         cookie.setStackSize(newStackSize);
567 }
568 FDECL void FTN_NAME(TCHARM_SET_STACK_SIZE,tcharm_set_stack_size)
569         (int *newSize)
570 { TCharmSetStackSize(*newSize); }
571
572
573 /*Create a new array of threads, which will be bound to by subsequent libraries*/
574 CDECL void TCharmCreate(int nThreads,
575                         TCharmThreadStartFn threadFn)
576 {
577         TCHARMAPI("TCharmCreate");
578         TCharmCreateData(nThreads,
579                          (TCharmThreadDataStartFn)threadFn,NULL,0);
580 }
581 FDECL void FTN_NAME(TCHARM_CREATE,tcharm_create)
582         (int *nThreads,TCharmThreadStartFn threadFn)
583 { TCharmCreate(*nThreads,threadFn); }
584
585
586 /*As above, but pass along (arbitrary) data to threads*/
587 CDECL void TCharmCreateData(int nThreads,
588                   TCharmThreadDataStartFn threadFn,
589                   void *threadData,int threadDataLen)
590 {
591         TCHARMAPI("TCharmCreateData");
592         if (TCharm::getState()!=inInit)
593                 CkAbort("TCharm> Can only create threads from in init!\n");
594         TCharmSetupCookie &cook=cookie;
595         TCharmInitMsg *msg=new (threadDataLen,0) TCharmInitMsg(
596                 (CthVoidFn)threadFn,cook.getStackSize());
597         msg->numElements=nThreads;
598         memcpy(msg->data,threadData,threadDataLen);
599         TCharmBuildThreads(msg,cook);
600 }
601
602 FDECL void FTN_NAME(TCHARM_CREATE_DATA,tcharm_create_data)
603         (int *nThreads,
604                   TCharmThreadDataStartFn threadFn,
605                   void *threadData,int *threadDataLen)
606 { TCharmCreateData(*nThreads,threadFn,threadData,*threadDataLen); }
607
608
609 CDECL int TCharmGetNumChunks(void)
610 {
611         TCHARMAPI("TCharmGetNumChunks");
612         int nChunks=CkNumPes();
613         char **argv=CkGetArgv();
614         CmiGetArgInt(argv,"-vp",&nChunks);
615         CmiGetArgInt(argv,"+vp",&nChunks);
616         lastNumChunks=nChunks;
617         return nChunks;
618 }
619 FDECL int FTN_NAME(TCHARM_GET_NUM_CHUNKS,tcharm_get_num_chunks)(void)
620 {
621         return TCharmGetNumChunks();
622 }
623
624
625 /***********************************
626 Callable from worker thread
627 */
628 CDECL int TCharmElement(void)
629
630         TCHARMAPI("TCharmElement");
631         return TCharm::get()->getElement();
632 }
633 CDECL int TCharmNumElements(void)
634
635         TCHARMAPI("TCharmNumElements");
636         if (TCharm::getState()==inDriver)
637                 return TCharm::get()->getNumElements();
638         else
639                 return lastNumChunks;
640 }
641
642 FDECL int FTN_NAME(TCHARM_ELEMENT,tcharm_element)(void) 
643 { return TCharmElement();}
644 FDECL int FTN_NAME(TCHARM_NUM_ELEMENTS,tcharm_num_elements)(void) 
645 { return TCharmNumElements();}
646
647 //Make sure this address will migrate with us when we move:
648 static void checkAddress(void *data)
649 {
650         if (tcharm_nomig||tcharm_nothreads) return; //Stack is not isomalloc'd
651         if (!CmiIsomallocInRange(data))
652             CkAbort("The UserData you register must be allocated on the stack!\n");
653 }
654
655 CDECL int TCharmRegister(void *data,TCharmPupFn pfn)
656
657         TCHARMAPI("TCharmRegister");
658         checkAddress(data);
659         return TCharm::get()->add(TCharm::UserData(pfn,data));
660 }
661 FDECL int FTN_NAME(TCHARM_REGISTER,tcharm_register)
662         (void *data,TCpupUserDataF pfn)
663
664         TCHARMAPI("TCharm_Register");
665         checkAddress(data);
666         return TCharm::get()->add(TCharm::UserData(
667                 pfn,data,TCharm::UserData::isFortran()));
668 }
669
670 CDECL void *TCharmGetUserdata(int id)
671 {
672         TCHARMAPI("TCharmGetUserdata");
673         return TCharm::get()->lookupUserData(id);
674 }
675 FDECL void *FTN_NAME(TCHARM_GET_USERDATA,tcharm_get_userdata)(int *id)
676 { return TCharmGetUserdata(*id); }
677
678 CDECL void TCharmMigrate(void)
679 {
680         TCHARMAPI("TCharmMigrate");
681         TCharm::get()->migrate();
682 }
683 FDECL void FTN_NAME(TCHARM_MIGRATE,tcharm_migrate)(void)
684 {
685         TCHARMAPI("TCharmMigrate");
686         TCharm::get()->migrate();
687 }
688
689 CDECL void TCharmBarrier(void)
690 {
691         TCHARMAPI("TCharmBarrier");
692         TCharm::get()->barrier();
693 }
694 FDECL void FTN_NAME(TCHARM_BARRIER,tcharm_barrier)(void)
695 {
696         TCharmBarrier();
697 }
698
699 CDECL void TCharmDone(void)
700 {
701         TCHARMAPI("TCharmDone");
702         if (TCharm::getState()!=inDriver) CkExit();
703         else TCharm::get()->done();
704 }
705 FDECL void FTN_NAME(TCHARM_DONE,tcharm_done)(void)
706 {
707         TCharmDone();
708 }
709
710 CDECL double TCharmWallTimer(void) 
711 {
712   TCHARMAPI("TCharmWallTimer");
713   if(TCharm::getState()!=inDriver) return CkWallTimer();
714   else { //Have to apply current thread's time offset
715     return CkWallTimer()+TCharm::get()->getTimeOffset();
716   }
717 }
718
719 #if 1
720 /*Include Fortran-style "iargc" and "getarg" routines.
721 These are needed to get access to the command-line arguments from Fortran.
722 */
723 FDECL int FTN_NAME(TCHARM_IARGC,tcharm_iargc)(void) {
724   TCHARMAPI("tcharm_iargc");
725   return CkGetArgc()-1;
726 }
727
728 FDECL void FTN_NAME(TCHARM_GETARG,tcharm_getarg)
729         (int *i_p,char *dest,int destLen) 
730 {
731   TCHARMAPI("tcharm_getarg");
732   int i=*i_p;
733   if (i<0) CkAbort("tcharm_getarg called with negative argument!");
734   if (i>=CkGetArgc()) CkAbort("tcharm_getarg called with argument > iargc!");
735   const char *src=CkGetArgv()[i];
736   strcpy(dest,src);
737   for (i=strlen(dest);i<destLen;i++) dest[i]=' ';
738 }
739
740 #endif
741
742 //These silly routines are used for serial startup:
743 extern void _initCharm(int argc, char **argv);
744 CDECL void TCharmInit(int *argc,char ***argv) {
745         ConverseInit(*argc, *argv, (CmiStartFn) _initCharm,1,1);
746         _initCharm(*argc,*argv);
747 }
748
749 FDECL void FTN_NAME(TCHARM_INIT,tcharm_init)(void) 
750 {
751         int argc=1;
752         char *argv[2]={"foo",NULL};
753         ConverseInit(argc,argv, (CmiStartFn) _initCharm,1,1);
754         _initCharm(argc,argv);
755 }
756
757 #include "tcharm.def.h"