Added TCharmWallTimer call, which uses a thread-specific offset to
[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 /****** Readonlys *****/
295 CkVec<TCpupReadonlyGlobal> TCharmReadonlys::entries;
296 void TCharmReadonlys::add(TCpupReadonlyGlobal fn)
297 {
298         entries.push_back(fn);
299 }
300 //Pups all registered readonlys
301 void TCharmReadonlys::pup(PUP::er &p) {
302         if (p.isUnpacking()) {
303                 //HACK: Rather than sending this message only where its needed,
304                 // we send it everywhere and just ignore it if it's not needed.
305                 if (CkMyPe()==0) return; //Processor 0 is the source-- no unpacking needed
306                 if (CkMyRank()!=0) return; //Some other processor will do the unpacking
307         }
308         //Pup the globals for this node:
309         int i,n=entries.length();
310         p(n);
311         if (n!=entries.length())
312                 CkAbort("TCharmReadonly list length mismatch!\n");
313         for (i=0;i<n;i++)
314                 (entries[i])((pup_er)&p);
315 }
316
317 CDECL void TCharmReadonlyGlobals(TCpupReadonlyGlobal fn)
318 {
319         TCHARMAPI("TCharmReadonlyGlobals");
320         if (TCharm::getState()!=inNodeSetup)
321                 CkAbort("Can only call TCharmReadonlyGlobals from in TCharmUserNodeSetup!\n");
322         TCharmReadonlys::add(fn);
323 }
324 FDECL void FTN_NAME(TCHARM_READONLY_GLOBALS,tcharm_readonly_globals)
325         (TCpupReadonlyGlobal fn)
326 {
327         TCharmReadonlyGlobals(fn);
328 }
329
330 /************* Startup/Shutdown Coordination Support ************/
331
332 enum {TC_READY=23, TC_BARRIER=87, TC_DONE=42};
333
334 //Called when a client is ready to run
335 void TCharm::ready(void) {
336         DBG("TCharm thread "<<thisIndex<<" ready")
337         int vals[2]={0,1};
338         if (thisIndex==0) vals[0]=TC_READY;
339         //Contribute to a synchronizing reduction
340         contribute(sizeof(vals),&vals,CkReduction::sum_int);
341 }
342
343 //Called when we want to go to a barrier
344 void TCharm::barrier(void) {
345         int vals[2]={0,1};
346         if (thisIndex==0) vals[0]=TC_BARRIER;
347         //Contribute to a synchronizing reduction
348         contribute(sizeof(vals),&vals,CkReduction::sum_int);
349         stop();
350 }
351
352 //Called when the thread is done running
353 void TCharm::done(void) {
354         DBG("TCharm thread "<<thisIndex<<" done")
355         int vals[2]={0,1};
356         if (thisIndex==0) vals[0]=TC_DONE;
357         //Contribute to a synchronizing reduction
358         contribute(sizeof(vals),&vals,CkReduction::sum_int);
359         stop();
360 }
361
362 //Called when an array reduction is complete
363 static void coordinatorReduction(void *coord_,int dataLen,void *reductionData)
364 {
365         TCharmCoordinator *coord=(TCharmCoordinator *)coord_;
366         int *vals=(int *)reductionData;
367         if (dataLen!=2*sizeof(int))
368                 CkAbort("Unexpected length in TCharm array reduction!\n");
369         DBGX("Finished coordinator reduction: "<<vals[0]<<", "<<vals[1]);
370         switch (vals[0]) {
371         case TC_READY: coord->clientReady(); break;
372         case TC_BARRIER: coord->clientBarrier(); break;
373         case TC_DONE: coord->clientDone(); break;
374         default:
375                 CkAbort("Unexpected value from TCharm array reduction!\n");
376         };
377 }
378
379 int TCharmCoordinator::nArrays=0; //Total number of running thread arrays
380 TCharmCoordinator *TCharmCoordinator::head=NULL; //List of coordinators
381
382
383 TCharmCoordinator::TCharmCoordinator(CkArrayID threads_,int nThreads_)
384         :threads(threads_), nThreads(nThreads_), nClients(0), nReady(0)
385 {
386         nArrays++;
387         //Link into the coordinator list
388         next=head;
389         head=this;
390
391         threads.setReductionClient(coordinatorReduction,this);
392         nClients=1; //Thread array itself is a client
393 }
394 TCharmCoordinator::~TCharmCoordinator()
395 {
396         //Coordinators never get deleted
397 }
398 void TCharmCoordinator::addClient(const CkArrayID &client)
399 {
400         nClients++;
401 }
402 void TCharmCoordinator::clientReady(void)
403 {
404         DBGX("client "<<nReady+1<<" of "<<nClients<<" ready");
405         nReady++;
406         if (nReady>=nClients) { //All client arrays are ready-- start threads
407                 DBGX("starting threads");
408                 threads.run();
409         }
410 }
411 void TCharmCoordinator::clientBarrier(void)
412 {
413         DBGX("clients all at barrier");
414         threads.run();
415 }
416 void TCharmCoordinator::clientDone(void)
417 {
418         DBGX("clientDone");     
419         nArrays--;
420         if (nArrays<=0) { //All arrays have exited
421                 DBGX("done with computation");
422                 CkExit();
423         }
424 }
425
426 /************* Setup **************/
427
428 //Cookie used during setup
429 TCharmSetupCookie *TCharmSetupCookie::theCookie;
430
431 //Globals used to control setup process
432 static int g_numDefaultSetups=0;
433 static TCharmFallbackSetupFn g_fallbackSetup=NULL;
434 void TCharmSetFallbackSetup(TCharmFallbackSetupFn f)
435 {
436         g_fallbackSetup=f;
437 }
438 CDECL void TCharmInDefaultSetup(void) {
439         g_numDefaultSetups++;
440 }
441
442 //Tiny simple main chare
443 class TCharmMain : public Chare {
444 public:
445   TCharmMain(CkArgMsg *msg) {
446     if (0!=(tcharm_nomig=CmiGetArgFlag(msg->argv,"+tcharm_nomig")))
447         CmiPrintf("TCHARM> Disabling migration support, for debugging\n");
448     tcharm_nothreads=CmiGetArgFlag(msg->argv,"+tcharm_nothread");
449     tcharm_nothreads|=CmiGetArgFlag(msg->argv,"+tcharm_nothreads");
450     if (0!=tcharm_nothreads)
451        CmiPrintf("TCHARM> Disabling thread support, for debugging\n");
452     char *traceLibName=NULL;
453     while (CmiGetArgString(msg->argv,"+tcharm_trace",&traceLibName))
454        tcharm_tracelibs.addTracing(traceLibName);
455
456     TCharmSetupCookie cookie(msg->argv);
457     TCharmSetupCookie::theCookie=&cookie;
458     g_numDefaultSetups=0;
459     
460     /*Call user-overridable C setup*/
461     TCharmUserSetup();
462     /*Call user-overridable Fortran setup*/
463     FTN_NAME(TCHARM_USER_SETUP,tcharm_user_setup)();
464     
465     if (g_numDefaultSetups==2) 
466     { //User didn't override either setup routine
467             if (g_fallbackSetup)
468                     (g_fallbackSetup)();
469             else
470                     CmiAbort("You need to override TCharmUserSetup to start your computation, or else link in a framework module\n");
471     }       
472     
473     delete msg;
474     
475     if (0==TCharmCoordinator::getTotal())
476             CkAbort("You didn't create any TCharm arrays in TCharmUserSetup!\n");
477
478     //Send out the readonly globals:
479     TCharmReadonlys r;
480     CProxy_TCharmReadonlyGroup::ckNew(r);
481   }
482 };
483
484 #ifndef CMK_OPTIMIZE
485 /*The setup cookie, used to store global initialization state*/
486 TCharmSetupCookie &TCharmSetupCookie::check(void)
487 {
488         if (magic!=correctMagic)
489                 CkAbort("TCharm setup cookie is damaged!\n");
490         return *this;
491 }
492 #endif
493
494 void TCharmSetupCookie::setThreads(const CkArrayID &aid,int nel)
495 {
496         coord=new TCharmCoordinator(aid,nel);
497         tc=aid; numElements=nel;
498 }
499
500 TCharmSetupCookie::TCharmSetupCookie(char **argv_)
501 {
502         magic=correctMagic;
503         argv=argv_;
504         coord=NULL;
505         stackSize=1*1024*1024; /*Default stack size is 1MB*/
506         CmiGetArgInt(argv,"+tcharm_stacksize",&stackSize);
507 }
508
509
510 /************** User API ***************/
511
512 #define cookie (*TCharmSetupCookie::get())
513
514 /**********************************
515 Callable from UserSetup: 
516 */
517
518 /*Set the size of the thread stack*/
519 CDECL void TCharmSetStackSize(int newStackSize)
520 {
521         TCHARMAPI("TCharmSetStackSize");
522         if (TCharm::getState()!=inInit)
523                 CkAbort("TCharm> Can only set stack size from in init!\n");
524         cookie.setStackSize(newStackSize);
525 }
526 FDECL void FTN_NAME(TCHARM_SET_STACK_SIZE,tcharm_set_stack_size)
527         (int *newSize)
528 { TCharmSetStackSize(*newSize); }
529
530
531 /*Create a new array of threads, which will be bound to by subsequent libraries*/
532 CDECL void TCharmCreate(int nThreads,
533                         TCharmThreadStartFn threadFn)
534 {
535         TCHARMAPI("TCharmCreate");
536         TCharmCreateData(nThreads,
537                          (TCharmThreadDataStartFn)threadFn,NULL,0);
538 }
539 FDECL void FTN_NAME(TCHARM_CREATE,tcharm_create)
540         (int *nThreads,TCharmThreadStartFn threadFn)
541 { TCharmCreate(*nThreads,threadFn); }
542
543
544 /*As above, but pass along (arbitrary) data to threads*/
545 CDECL void TCharmCreateData(int nThreads,
546                   TCharmThreadDataStartFn threadFn,
547                   void *threadData,int threadDataLen)
548 {
549         TCHARMAPI("TCharmCreateData");
550         if (TCharm::getState()!=inInit)
551                 CkAbort("TCharm> Can only create threads from in init!\n");
552         TCharmSetupCookie &cook=cookie;
553         TCharmInitMsg *msg=new (threadDataLen,0) TCharmInitMsg(
554                 (CthVoidFn)threadFn,cook.getStackSize());
555         msg->numElements=nThreads;
556         memcpy(msg->data,threadData,threadDataLen);
557         TCharmBuildThreads(msg,cook);
558 }
559
560 FDECL void FTN_NAME(TCHARM_CREATE_DATA,tcharm_create_data)
561         (int *nThreads,
562                   TCharmThreadDataStartFn threadFn,
563                   void *threadData,int *threadDataLen)
564 { TCharmCreateData(*nThreads,threadFn,threadData,*threadDataLen); }
565
566
567 /*Get the unconsumed command-line arguments*/
568 CDECL char **TCharmArgv(void)
569 {
570         TCHARMAPI("TCharmArgv");
571         if (TCharm::getState()!=inInit)
572                 CkAbort("TCharm> Can only get arguments from in init!\n");
573         return cookie.getArgv();
574 }
575 CDECL int TCharmArgc(void)
576 {
577         TCHARMAPI("TCharmArgc");
578         if (TCharm::getState()!=inInit)
579                 CkAbort("TCharm> Can only get arguments from in init!\n");
580         return CmiGetArgc(cookie.getArgv());
581 }
582
583 CDECL int TCharmGetNumChunks(void)
584 {
585         TCHARMAPI("TCharmGetNumChunks");
586         int nChunks=CkNumPes();
587         char **argv=TCharmArgv();
588         CmiGetArgInt(argv,"-vp",&nChunks);
589         CmiGetArgInt(argv,"+vp",&nChunks);
590         lastNumChunks=nChunks;
591         return nChunks;
592 }
593 FDECL int FTN_NAME(TCHARM_GET_NUM_CHUNKS,tcharm_get_num_chunks)(void)
594 {
595         return TCharmGetNumChunks();
596 }
597
598
599 /***********************************
600 Callable from worker thread
601 */
602 CDECL int TCharmElement(void)
603
604         TCHARMAPI("TCharmElement");
605         return TCharm::get()->getElement();
606 }
607 CDECL int TCharmNumElements(void)
608
609         TCHARMAPI("TCharmNumElements");
610         if (TCharm::getState()==inDriver)
611                 return TCharm::get()->getNumElements();
612         else
613                 return lastNumChunks;
614 }
615
616 FDECL int FTN_NAME(TCHARM_ELEMENT,tcharm_element)(void) 
617 { return TCharmElement();}
618 FDECL int FTN_NAME(TCHARM_NUM_ELEMENTS,tcharm_num_elements)(void) 
619 { return TCharmNumElements();}
620
621 //Make sure this address will migrate with us when we move:
622 static void checkAddress(void *data)
623 {
624         if (tcharm_nomig||tcharm_nothreads) return; //Stack is not isomalloc'd
625         if (!CmiIsomallocInRange(data))
626             CkAbort("The UserData you register must be allocated on the stack!\n");
627 }
628
629 CDECL int TCharmRegister(void *data,TCharmPupFn pfn)
630
631         TCHARMAPI("TCharmRegister");
632         checkAddress(data);
633         return TCharm::get()->add(TCharm::UserData(pfn,data));
634 }
635 FDECL int FTN_NAME(TCHARM_REGISTER,tcharm_register)
636         (void *data,TCpupUserDataF pfn)
637
638         TCHARMAPI("TCharm_Register");
639         checkAddress(data);
640         return TCharm::get()->add(TCharm::UserData(
641                 pfn,data,TCharm::UserData::isFortran()));
642 }
643
644 CDECL void *TCharmGetUserdata(int id)
645 {
646         TCHARMAPI("TCharmGetUserdata");
647         return TCharm::get()->lookupUserData(id);
648 }
649 FDECL void *FTN_NAME(TCHARM_GET_USERDATA,tcharm_get_userdata)(int *id)
650 { return TCharmGetUserdata(*id); }
651
652 CDECL void TCharmMigrate(void)
653 {
654         TCHARMAPI("TCharmMigrate");
655         TCharm::get()->migrate();
656 }
657 FDECL void FTN_NAME(TCHARM_MIGRATE,tcharm_migrate)(void)
658 {
659         TCHARMAPI("TCharmMigrate");
660         TCharm::get()->migrate();
661 }
662
663 CDECL void TCharmBarrier(void)
664 {
665         TCHARMAPI("TCharmBarrier");
666         TCharm::get()->barrier();
667 }
668 FDECL void FTN_NAME(TCHARM_BARRIER,tcharm_barrier)(void)
669 {
670         TCharmBarrier();
671 }
672
673 CDECL void TCharmDone(void)
674 {
675         TCHARMAPI("TCharmDone");
676         TCharm::get()->done();
677 }
678 FDECL void FTN_NAME(TCHARM_DONE,tcharm_done)(void)
679 {
680         TCharmDone();
681 }
682
683 CDECL double TCharmWallTimer(void) 
684 {
685   TCHARMAPI("TCharmWallTimer");
686   if(TCharm::getState()!=inDriver) return CkWallTimer();
687   else { //Have to apply current thread's time offset
688     return CkWallTimer()+TCharm::get()->getTimeOffset();
689   }
690 }
691
692
693 #include "tcharm.def.h"