*** empty log message ***
[charm.git] / src / arch / uth / machine.c
1 /***************************************************************************
2  * RCS INFORMATION:
3  *
4  *      $RCSfile$
5  *      $Author$        $Locker$                $State$
6  *      $Revision$      $Date$
7  *
8  ***************************************************************************
9  * DESCRIPTION:
10  *
11  ***************************************************************************
12  * REVISION HISTORY:
13  *
14  * $Log$
15  * Revision 1.28  1997-08-04 09:50:42  jyelon
16  * *** empty log message ***
17  *
18  * Revision 1.27  1997/07/30 19:58:09  jyelon
19  * *** empty log message ***
20  *
21  * Revision 1.26  1997/07/30 17:31:13  jyelon
22  * *** empty log message ***
23  *
24  * Revision 1.25  1997/07/29 16:09:47  milind
25  * Added CmiNodeLock macros and functions to the machine layer for all except
26  * solaris SMP.
27  *
28  * Revision 1.24  1997/04/25 20:48:12  jyelon
29  * Corrected CmiNotifyIdle
30  *
31  * Revision 1.23  1997/04/24 22:37:02  jyelon
32  * Added CmiNotifyIdle
33  *
34  * Revision 1.22  1997/03/21 19:23:57  milind
35  * removed the alignment bug in Common.uth/machine.c
36  *
37  * Revision 1.21  1997/03/19 04:31:36  jyelon
38  * Redesigned ConverseInit
39  *
40  * Revision 1.20  1997/02/13 09:31:39  jyelon
41  * Updated for new main/ConverseInit structure.
42  *
43  * Revision 1.19  1997/01/17 15:49:57  jyelon
44  * Minor adjustments to deal with recent changes to Common code.
45  *
46  * Revision 1.18  1996/11/20 06:46:54  jyelon
47  * Repaired rob's HP/C++ mods.
48  *
49  * Revision 1.17  1996/11/08 22:22:53  brunner
50  * Put _main in for HP-UX CC compilation.  It is ignored according to the
51  * CMK_USE_HP_MAIN_FIX flag.
52  *
53  * Revision 1.16  1996/07/19 17:07:37  jyelon
54  * *** empty log message ***
55  *
56  * Revision 1.15  1996/07/15 20:59:22  jyelon
57  * Moved much timer, signal, etc code into common.
58  *
59  * Revision 1.14  1996/07/02 21:25:22  jyelon
60  * *** empty log message ***
61  *
62  * Revision 1.13  1996/02/10 18:57:29  sanjeev
63  * fixed bugs in CmiGetNodeNeighbours, CmiNeighboursIndex
64  *
65  * Revision 1.12  1996/02/10 18:54:24  sanjeev
66  * fixed bug in CmiNumNeighbours
67  *
68  * Revision 1.11  1995/11/07 23:20:32  jyelon
69  * removed neighbour_init residue.
70  *
71  * Revision 1.10  1995/11/07  18:24:53  jyelon
72  * Corrected a bug in GetNodeNeighbours
73  *
74  * Revision 1.9  1995/11/07  18:16:45  jyelon
75  * Corrected 'neighbour' functions (they now make a hypercube).
76  *
77  * Revision 1.8  1995/10/27  21:45:35  jyelon
78  * Changed CmiNumPe --> CmiNumPes
79  *
80  * Revision 1.7  1995/10/18  22:22:53  jyelon
81  * I forget.
82  *
83  * Revision 1.6  1995/10/13  22:36:29  jyelon
84  * changed exit() --> exit(1)
85  *
86  * Revision 1.5  1995/10/13  22:34:42  jyelon
87  * added CmiNext to CmiCallMain.
88  *
89  * Revision 1.4  1995/10/13  20:05:13  jyelon
90  * *** empty log message ***
91  *
92  * Revision 1.3  1995/10/10  06:10:58  jyelon
93  * removed program_name
94  *
95  * Revision 1.2  1995/09/30  15:44:59  jyelon
96  * fixed a bug.
97  *
98  * Revision 1.1  1995/09/30  15:00:00  jyelon
99  * Initial revision
100  *
101  * Revision 2.5  1995/09/29  09:50:07  jyelon
102  * CmiGet-->CmiDeliver, added protos, etc.
103  *
104  * Revision 2.4  1995/09/20  15:56:29  gursoy
105  * made the arg of CmiFree and CmiSize void*
106  *
107  * Revision 2.3  1995/09/07  22:33:07  gursoy
108  * now the processor specific variables in machine files also accessed thru macros (because macros modifies the var names)
109  *
110  * Revision 2.2  1995/07/26  19:04:11  gursoy
111  * fixed some timer-system-include-file related problems
112  *
113  * Revision 2.1  1995/07/11  16:53:57  gursoy
114  * added CsdStopCount
115  *
116  * Revision 2.0  1995/07/05  23:37:59  gursoy
117  * *** empty log message ***
118  *
119  *
120  *
121  ***************************************************************************/
122 static char ident[] = "@(#)$Header$";
123
124 #include <stdio.h>
125 #include <math.h>
126 #include "converse.h"
127
128 static char *DeleteArg(argv)
129   char **argv;
130 {
131   char *res = argv[0];
132   if (res==0) { CmiError("Bad arglist."); exit(1); }
133   while (*argv) { argv[0]=argv[1]; argv++; }
134   return res;
135 }
136
137 /*****************************************************************************
138  *
139  * Memory management.
140  * 
141  ****************************************************************************/
142
143 void *CmiAlloc(size)
144 int size;
145 {
146 char *res;
147 res =(char *)malloc(size+8);
148 if (res==0) printf("Memory allocation failed.");
149 ((int *)res)[0]=size;
150 return (void *)(res+8);
151 }
152
153 int CmiSize(blk)
154 void *blk;
155 {
156 return ((int *)(((char *)blk)-8))[0];
157 }
158
159 void CmiFree(blk)
160 void *blk;
161 {
162 free( ((char *)blk)-8);
163 }
164
165 /*****************************************************************************
166  *
167  * Module variables
168  * 
169  ****************************************************************************/
170
171 typedef void *Fifo;
172
173 int        Cmi_mype;
174 int        Cmi_myrank;
175 int        Cmi_numpes;
176 int        Cmi_nodesize;
177 int        Cmi_stacksize = 64000;
178 char     **CmiArgv;
179 CmiStartFn CmiStart;
180 int        CmiUsched;
181 CthThread *CmiThreads;
182 Fifo      *CmiQueues;
183 int       *CmiBarred;
184 int        CmiNumBarred=0;
185
186 Fifo FIFO_Create();
187 CpvDeclare(Fifo, CmiLocalQueue);
188
189 /******************************************************************************
190  *
191  * Load-Balancer needs
192  *
193  * These neighbour functions impose a (possibly incomplete)
194  * hypercube on the machine.
195  *
196  *****************************************************************************/
197
198
199 long CmiNumNeighbours(node)
200 int node;
201 {
202   int bit, count=0;
203   bit = 1;
204   while (1) {
205     int neighbour = node ^ bit;
206     if (neighbour < CmiNumPes()) count++;
207     bit = bit<<1; 
208     if (bit > CmiNumPes()) break;
209   }
210   return count;
211 }
212
213 int CmiGetNodeNeighbours(node, neighbours)
214 int node, *neighbours;
215 {
216   int bit, count=0;
217   bit = 1;
218   while (1) {
219     int neighbour = node ^ bit;
220     if (neighbour < CmiNumPes()) neighbours[count++] = neighbour;
221     bit = bit<<1; 
222     if (bit > CmiNumPes()) break;
223   }
224   return count;
225 }
226  
227 int CmiNeighboursIndex(node, nbr)
228 int node, nbr;
229 {
230   int bit, count=0;
231   bit = 1;
232   while (1) {
233     int neighbour = node ^ bit;
234     if (neighbour < CmiNumPes()) { if (nbr==neighbour) return count; count++; }
235     bit = bit<<=1; 
236     if (bit > CmiNumPes()) break;
237   }
238   return(-1);
239 }
240
241
242 /*****************************************************************************
243  *
244  * Comm handles are nonexistent in uth version
245  *
246  *****************************************************************************/
247
248 int CmiAsyncMsgSent(c)
249 CmiCommHandle c ;
250 {
251   return 1;
252 }
253
254 void CmiReleaseCommHandle(c)
255 CmiCommHandle c ;
256 {
257 }
258
259 /********************* CONTEXT-SWITCHING FUNCTIONS ******************/
260
261 static void CmiNext()
262 {
263   CthThread t; int index; int orig;
264   index = (CmiMyPe()+1) % CmiNumPes();
265   orig = index;
266   while (1) {
267     t = CmiThreads[index];
268     if ((t)&&(!CmiBarred[index])) break;
269     index = (index+1) % CmiNumPes();
270     if (index == orig) exit(0);
271   }
272   Cmi_mype = index;
273   CthResume(t);
274 }
275
276 void CmiExit()
277 {
278   CmiThreads[CmiMyPe()] = 0;
279   CmiFree(CthSelf());
280   CmiNext();
281 }
282
283 void CmiYield()
284 {
285   CmiThreads[CmiMyPe()] = CthSelf();
286   CmiNext();
287 }
288
289 void CmiNodeBarrier()
290 {
291   int i;
292   CmiNumBarred++;
293   CmiBarred[CmiMyPe()] = 1;
294   if (CmiNumBarred == CmiNumPes()) {
295     for (i=0; i<CmiNumPes(); i++) CmiBarred[i]=0;
296     CmiNumBarred=0;
297   }
298   CmiYield();
299 }
300
301 CmiNodeLock CmiCreateLock()
302 {
303   return (CmiNodeLock)malloc(sizeof(int));
304 }
305
306 void CmiLock(CmiNodeLock lk)
307 {
308   while (*lk) CmiYield();
309   *lk = 1;
310 }
311
312 void CmiUnlock(CmiNodeLock lk)
313 {
314   if (*lk==0) {
315     CmiError("CmiNodeLock not locked, can't unlock.");
316     exit(1);
317   }
318   *lk = 0;
319 }
320
321 int CmiTryLock(CmiNodeLock lk)
322 {
323   if (*lk==0) { *lk=1; return 0; }
324   return -1;
325 }
326
327 void CmiDestroyLock(CmiNodeLock lk)
328 {
329   free(lk);
330 }
331
332
333 /*****************************************************************************
334  *
335  * The following are the CmiDeliverXXX functions.
336  *
337  * void CmiDeliversInit()
338  *
339  *      - CmiInit promises to call this before calling CmiDeliverMsgs
340  *        or any of the other functions in this section.
341  *
342  * int CmiDeliverMsgs(int maxmsgs)
343  *
344  *      - CmiDeliverMsgs will retrieve up to maxmsgs that were transmitted
345  *        with the Cmi, and will invoke their handlers.  It does not wait
346  *        if no message is unavailable.  Instead, it returns the quantity
347  *        (maxmsgs-delivered), where delivered is the number of messages it
348  *        delivered.
349  *
350  * void CmiDeliverSpecificMsg(int handlerno)
351  *
352  *      - Waits for a message with the specified handler to show up, then
353  *        invokes the message's handler.  Note that unlike CmiDeliverMsgs,
354  *        This function _does_ wait.
355  *
356  * void CmiGrabBuffer(void **bufptrptr)
357  *
358  *      - When CmiDeliverMsgs or CmiDeliverSpecificMsgs calls a handler,
359  *        the handler receives a pointer to a buffer containing the message.
360  *        The buffer does not belong to the handler, eg, the handler may not
361  *        free the buffer.  Instead, the buffer will be automatically reused
362  *        or freed as soon as the handler returns.  If the handler wishes to
363  *        keep a copy of the data after the handler returns, it may do so by
364  *        calling CmiGrabBuffer and passing it a pointer to a variable which
365  *        in turn contains a pointer to the system buffer.  The variable will
366  *        be updated to contain a pointer to a handler-owned buffer containing
367  *        the same data as before.  The handler then has the responsibility of
368  *        making sure the buffer eventually gets freed.  Example:
369  *
370  * void myhandler(void *msg)
371  * {
372  *    CmiGrabBuffer(&msg);      // Claim ownership of the message buffer
373  *    ... rest of handler ...
374  *    CmiFree(msg);             // I have the right to free it or
375  *                              // keep it, as I wish.
376  * }
377  *
378  *
379  * For this common implementation to work, the machine layer must provide the
380  * following:
381  *
382  * void *CmiGetNonLocal()
383  *
384  *      - returns a message just retrieved from some other PE, not from
385  *        local.  If no such message exists, returns 0.
386  *
387  * CpvExtern(FIFO_Queue, CmiLocalQueue);
388  *
389  *      - a FIFO queue containing all messages from the local processor.
390  *
391  *****************************************************************************/
392
393 CpvStaticDeclare(int, CmiBufferGrabbed);
394 CpvExtern(void*, CmiLocalQueue);
395
396 void CmiDeliversInit()
397 {
398   CpvInitialize(int, CmiBufferGrabbed);
399   CpvAccess(CmiBufferGrabbed) = 0;
400 }
401
402 void CmiGrabBuffer()
403 {
404   CpvAccess(CmiBufferGrabbed) = 1;
405 }
406
407 int CmiDeliverMsgs(maxmsgs)
408 int maxmsgs;
409 {
410   void *msg;
411   
412   while (1) {
413     CmiYield();
414     FIFO_DeQueue(CpvAccess(CmiLocalQueue), &msg);
415     if (msg==0) break;
416     CpvAccess(CmiBufferGrabbed)=0;
417     (CmiGetHandlerFunction(msg))(msg);
418     if (!CpvAccess(CmiBufferGrabbed)) CmiFree(msg);
419     maxmsgs--; if (maxmsgs==0) break;
420   }
421   return maxmsgs;
422 }
423
424 /*
425  * CmiDeliverSpecificMsg(lang)
426  *
427  * - waits till a message with the specified handler is received,
428  *   then delivers it.
429  *
430  */
431
432 void CmiDeliverSpecificMsg(handler)
433 int handler;
434 {
435   void *tmpqueue = FIFO_Create(); int *msg, *msg1;
436   
437   while (1) {
438     FIFO_DeQueue(CpvAccess(CmiLocalQueue), &msg);
439     if (msg == 0) { CmiYield(); continue; }
440     if (CmiGetHandler(msg)==handler) break;
441     FIFO_EnQueue(tmpqueue, msg);
442   }
443   while (1) {
444     FIFO_DeQueue(tmpqueue, &msg1);
445     if (msg1==0) break;
446     FIFO_EnQueue(CpvAccess(CmiLocalQueue), msg1);
447   }
448   FIFO_Destroy(tmpqueue);
449   CpvAccess(CmiBufferGrabbed)=0;
450   (CmiGetHandlerFunction(msg))(msg);
451   if (!CpvAccess(CmiBufferGrabbed)) CmiFree(msg);
452 }
453
454 void CmiNotifyIdle()
455 {
456 #if CMK_WHEN_PROCESSOR_IDLE_USLEEP
457   struct timeval tv;
458   tv.tv_sec=0; tv.tv_usec=5000;
459   select(0,0,0,0,&tv);
460 #endif
461 }
462  
463 /********************* MESSAGE SEND FUNCTIONS ******************/
464
465 void CmiSyncSendFn(destPE, size, msg)
466 int destPE;
467 int size;
468 char * msg;
469 {
470   char *buf = (char *)CmiAlloc(size);
471   memcpy(buf,msg,size);
472   FIFO_EnQueue(CmiQueues[destPE],buf);
473 }
474
475 CmiCommHandle CmiAsyncSendFn(destPE, size, msg) 
476 int destPE;
477 int size;
478 char * msg;
479 {
480   char *buf = (char *)CmiAlloc(size);
481   memcpy(buf,msg,size);
482   FIFO_EnQueue(CmiQueues[destPE],buf);
483 }
484
485 void CmiFreeSendFn(destPE, size, msg)
486 int destPE;
487 int size;
488 char * msg;
489 {
490   FIFO_EnQueue(CmiQueues[destPE], msg);
491 }
492
493 void CmiSyncBroadcastFn(size, msg)
494 int size;
495 char * msg;
496 {
497   int i;
498   for(i=0; i<CmiNumPes(); i++)
499     if (i != CmiMyPe()) CmiSyncSendFn(i,size,msg);
500 }
501
502 CmiCommHandle CmiAsyncBroadcastFn(size, msg)
503 int size;
504 char * msg;
505 {
506   CmiSyncBroadcastFn(size, msg);
507   return 0;
508 }
509
510 void CmiFreeBroadcastFn(size, msg)
511 int size;
512 char * msg;
513 {
514   CmiSyncBroadcastFn(size, msg);
515   CmiFree(msg);
516 }
517
518 void CmiSyncBroadcastAllFn(size, msg)
519 int size;
520 char * msg;
521 {
522   int i;
523   for(i=0; i<CmiNumPes(); i++)
524     CmiSyncSendFn(i,size,msg);
525 }
526
527 CmiCommHandle CmiAsyncBroadcastAllFn(size, msg)
528 int size;
529 char * msg;
530 {
531   CmiSyncBroadcastAllFn(size,msg);
532   return 0 ;
533 }
534
535 void CmiFreeBroadcastAllFn(size, msg)
536 int size;
537 char * msg;
538 {
539   int i;
540   for(i=0; i<CmiNumPes(); i++)
541     if (i!=CmiMyPe()) CmiSyncSendFn(i,size,msg);
542   FIFO_EnQueue(CpvAccess(CmiLocalQueue),msg);
543 }
544
545
546
547 /************************** SETUP ***********************************/
548
549 static void CmiParseArgs(argv)
550 char **argv;
551 {
552   char **argp;
553   
554   for (argp=argv; *argp; ) {
555     if ((strcmp(*argp,"++stacksize")==0)&&(argp[1])) {
556       DeleteArg(argp);
557       Cmi_stacksize = atoi(*argp);
558       DeleteArg(argp);
559     } else if ((strcmp(*argp,"+p")==0)&&(argp[1])) {
560       Cmi_numpes = atoi(argp[1]);
561       argp+=2;
562     } else if (sscanf(*argp, "+p%d", &CmiNumPes()) == 1) {
563       argp+=1;
564     } else argp++;
565   }
566   
567   if (CmiNumPes()<1) {
568     printf("Error: must specify number of processors to simulate with +pXXX\n",CmiNumPes());
569     exit(1);
570   }
571 }
572
573 void CmiInitPE()
574 {
575   CpvAccess(CmiLocalQueue) = CmiQueues[CmiMyPe()];
576   CmiSpanTreeInit();
577   CmiTimerInit();
578   ConverseCommonInit(CmiArgv);
579 }
580
581 void CmiCallMain()
582 {
583   int argc; char **argv;
584   for (argc=0; CmiArgv[argc]; argc++);
585   argv = (char **)CmiAlloc((argc+1)*sizeof(char *));
586   memcpy(argv, CmiArgv, (argc+1)*sizeof(char *));
587   CmiInitPE();
588   CmiStart(argc, argv);
589   if (CmiUsched==0) CsdScheduler(-1);
590   CmiThreads[CmiMyPe()] = 0;
591   CmiNext();
592 }
593
594 void ConverseExit()
595 {
596   CmiThreads[CmiMyPe()] = 0;
597   CmiNext();
598 }
599
600 void ConverseInit(argc,argv,fn,usched,initret)
601 int argc;
602 char *argv[];
603 CmiStartFn fn;
604 int usched, initret;
605 {
606   CthThread t; int stacksize, i;
607   
608 #if CMK_USE_HP_MAIN_FIX
609 #if FOR_CPLUS
610   _main(argc,argv);
611 #endif
612 #endif
613   
614   CmiArgv = argv;
615   CmiStart = fn;
616   CmiUsched = usched;
617   CmiParseArgs(argv);
618   CthInit(argv);
619   CpvInitialize(void*, CmiLocalQueue);
620   CmiThreads = (CthThread *)CmiAlloc(CmiNumPes()*sizeof(CthThread));
621   CmiBarred  = (int       *)CmiAlloc(CmiNumPes()*sizeof(int));
622   CmiQueues  = (Fifo      *)CmiAlloc(CmiNumPes()*sizeof(Fifo));
623   
624   /* Create threads for all PE except PE 0 */
625   for(i=0; i<CmiNumPes(); i++) {
626     t = (i==0) ? CthSelf() : CthCreate(CmiCallMain, 0, Cmi_stacksize);
627     CmiThreads[i] = t;
628     CmiBarred[i] = 0;
629     CmiQueues[i] = FIFO_Create();
630   }
631   Cmi_mype = 0;
632   CmiInitPE();
633   if (initret==0) {
634     fn(argc, argv);
635     if (usched==0) CsdScheduler(-1);
636     ConverseExit();
637   }
638 }
639