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