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