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