Fixed a bug in passing argv for machine-specific initialization procedures.
[charm.git] / src / arch / util / machine-common.c
1 #if CMK_C_INLINE
2 #define INLINE_KEYWORD inline
3 #else
4 #define INLINE_KEYWORD
5 #endif
6
7 /* ###Beginning of Broadcast related definitions ### */
8 #ifndef CMK_BROADCAST_SPANNING_TREE
9 #define CMK_BROADCAST_SPANNING_TREE    1
10 #endif
11
12 #ifndef CMK_BROADCAST_HYPERCUBE
13 #define CMK_BROADCAST_HYPERCUBE        0
14 #endif
15
16 #define BROADCAST_SPANNING_FACTOR      4
17 /* The number of children used when a msg is broadcast inside a node */
18 #define BROADCAST_SPANNING_INTRA_FACTOR  8
19
20 /* Root of broadcast:
21  * non-bcast msg: root = 0;
22  * proc-level bcast msg: root >=1; (CmiMyPe()+1)
23  * node-level bcast msg: root <=-1; (-CmiMyNode()-1)
24  */
25 #define CMI_BROADCAST_ROOT(msg)          ((CmiMsgHeaderBasic *)msg)->root
26 #define CMI_DEST_RANK(msg)               ((CmiMsgHeaderBasic *)msg)->rank
27 #define CMI_SET_BROADCAST_ROOT(msg, root)  CMI_BROADCAST_ROOT(msg) = (root);
28
29 #if USE_COMMON_SYNC_BCAST || USE_COMMON_ASYNC_BCAST
30 #if !CMK_BROADCAST_SPANNING_TREE && !CMK_BROADCAST_HYPERCUBE
31 #warning "Broadcast function is based on the plain P2P O(P)-message scheme!!!"
32 #endif
33 #endif
34
35 /**
36  * For some machine layers such as on Active Message framework,
37  * the receiver callback is usally executed on an internal
38  * thread (i.e. not the flow managed by ours). Therefore, for
39  * forwarding broadcast messages, we could have a choice whether
40  * to offload such function to the flow we manage such as the
41  * communication thread. -Chao Mei
42  */
43
44 #ifndef CMK_OFFLOAD_BCAST_PROCESS
45 #define CMK_OFFLOAD_BCAST_PROCESS 0
46 #endif
47 #if CMK_OFFLOAD_BCAST_PROCESS
48 CsvDeclare(PCQueue, procBcastQ);
49 #if CMK_NODE_QUEUE_AVAILABLE
50 CsvDeclare(PCQueue, nodeBcastQ);
51 #endif
52 #endif
53
54 #if CMK_BROADCAST_HYPERCUBE
55 /* ceil(log2(CmiNumNodes)) except when _Cmi_numnodes is 1, used for hypercube */
56 static int CmiNodesDim;
57 #endif
58 /* ###End of Broadcast related definitions ### */
59
60
61 #ifndef CMK_HAS_SIZE_IN_MSGHDR
62 #define CMK_HAS_SIZE_IN_MSGHDR 1
63 #endif
64 #if CMK_HAS_SIZE_IN_MSGHDR
65 #define CMI_MSG_SIZE(msg)  ((CmiMsgHeaderBasic *)msg)->size
66 #else
67 #define CMI_MSG_SIZE(msg)  (CmiAbort("Has no msg size in header"))
68 #endif
69
70 #if CMK_NODE_QUEUE_AVAILABLE
71 /* This value should be larger than the number of cores used
72  * per charm smp node. So it's currently set to such a large
73  * value.
74  */
75 #define DGRAM_NODEMESSAGE   (0x1FFB)
76 #endif
77
78 /* Node state structure */
79 int               _Cmi_mynode;    /* Which address space am I */
80 int               _Cmi_mynodesize;/* Number of processors in my address space */
81 int               _Cmi_numnodes;  /* Total number of address spaces */
82 int               _Cmi_numpes;    /* Total number of processors */
83
84 CpvDeclare(void*, CmiLocalQueue);
85
86 /* different modes for sending a message */
87 #define P2P_SYNC 0x1
88 #define P2P_ASYNC 0x2
89 #define BCAST_SYNC 0x3
90 #define BCAST_ASYNC 0x4
91
92 #if CMK_SMP
93 static volatile int commThdExit = 0;
94 static CmiNodeLock  commThdExitLock = 0;
95
96 /**
97  *  The macro defines whether to have a comm thd to offload some
98  *  work such as forwarding bcast messages etc. This macro
99  *  should be defined before including "machine-smp.c". Note
100  *  that whether a machine layer in SMP mode could run w/o comm
101  *  thread depends on the support of the underlying
102  *  communication library.
103  *
104  *  --Chao Mei
105  */
106 #ifndef CMK_SMP_NO_COMMTHD
107 #define CMK_SMP_NO_COMMTHD 0
108 #endif
109
110 #if CMK_SMP_NO_COMMTHD
111 int Cmi_commthread = 0;
112 #else
113 int Cmi_commthread = 1;
114 #endif
115
116 #endif
117
118 /*SHOULD BE MOVED TO MACHINE-SMP.C ??*/
119 static int Cmi_nodestart;
120
121 /*
122  * Network progress utility variables. Period controls the rate at
123  * which the network poll is called
124  */
125 #ifndef NETWORK_PROGRESS_PERIOD_DEFAULT
126 #define NETWORK_PROGRESS_PERIOD_DEFAULT 1000
127 #endif
128
129 CpvDeclare(unsigned , networkProgressCount);
130 int networkProgressPeriod;
131
132
133 /* ===== Beginning of Common Function Declarations ===== */
134 void CmiAbort(const char *message);
135 static void PerrorExit(const char *msg);
136
137 /* This function handles the msg received as which queue to push into */
138 static void handleOneRecvedMsg(int size, char *msg);
139
140 static void handleOneBcastMsg(int size, char *msg);
141 static void processBcastQs();
142
143 /* Utility functions for forwarding broadcast messages,
144  * should not be used in machine-specific implementations
145  * except in some special occasions.
146  */
147 static void processProcBcastMsg(int size, char *msg);
148 static void processNodeBcastMsg(int size, char *msg);
149 static void SendSpanningChildrenProc(int size, char *msg);
150 static void SendHyperCubeProc(int size, char *msg);
151 #if CMK_NODE_QUEUE_AVAILABLE
152 static void SendSpanningChildrenNode(int size, char *msg);
153 static void SendHyperCubeNode(int size, char *msg);
154 #endif
155
156 static void SendSpanningChildren(int size, char *msg, int rankToAssign, int startNode);
157 static void SendHyperCube(int size,  char *msg, int rankToAssign, int startNode);
158 /* send to other ranks except me on the same node*/
159 static void SendToPeers(int size, char *msg);
160
161
162 void CmiPushPE(int rank, void *msg);
163
164 #if CMK_NODE_QUEUE_AVAILABLE
165 void CmiPushNode(void *msg);
166 #endif
167
168 /* Functions regarding send ops declared in converse.h */
169
170 /* In default, using the common codes for msg sending */
171 #ifndef USE_COMMON_SYNC_P2P
172 #define USE_COMMON_SYNC_P2P 1
173 #endif
174 #ifndef USE_COMMON_ASYNC_P2P
175 #define USE_COMMON_ASYNC_P2P 1
176 #endif
177 #ifndef USE_COMMON_SYNC_BCAST
178 #define USE_COMMON_SYNC_BCAST 1
179 #endif
180 #ifndef USE_COMMON_ASYNC_BCAST
181 #define USE_COMMON_ASYNC_BCAST 1
182 #endif
183
184 static void CmiSendSelf(char *msg);
185
186 void CmiSyncSendFn(int destPE, int size, char *msg);
187 CmiCommHandle CmiAsyncSendFn(int destPE, int size, char *msg);
188 void CmiFreeSendFn(int destPE, int size, char *msg);
189
190 void CmiSyncBroadcastFn(int size, char *msg);
191 CmiCommHandle CmiAsyncBroadcastFn(int size, char *msg);
192 void CmiFreeBroadcastFn(int size, char *msg);
193
194 void CmiSyncBroadcastAllFn(int size, char *msg);
195 CmiCommHandle CmiAsyncBroadcastAllFn(int size, char *msg);
196 void CmiFreeBroadcastAllFn(int size, char *msg);
197
198 #if CMK_NODE_QUEUE_AVAILABLE
199 static void CmiSendNodeSelf(char *msg);
200
201 void CmiSyncNodeSendFn(int destNode, int size, char *msg);
202 CmiCommHandle CmiAsyncNodeSendFn(int destNode, int size, char *msg);
203 void CmiFreeNodeSendFn(int destNode, int size, char *msg);
204
205 void CmiSyncNodeBroadcastFn(int size, char *msg);
206 CmiCommHandle CmiAsyncNodeBroadcastFn(int size, char *msg);
207 void CmiFreeNodeBroadcastFn(int size, char *msg);
208
209 void CmiSyncNodeBroadcastAllFn(int size, char *msg);
210 CmiCommHandle CmiAsyncNodeBroadcastAllFn(int size, char *msg);
211 void CmiFreeNodeBroadcastAllFn(int size, char *msg);
212 #endif
213
214 /* Functions and variables regarding machine startup */
215 static char     **Cmi_argv;
216 static char     **Cmi_argvcopy;
217 static CmiStartFn Cmi_startfn;   /* The start function */
218 static int        Cmi_usrsched;  /* Continue after start function finishes? */
219 void ConverseInit(int argc, char **argv, CmiStartFn fn, int usched, int initret);
220 static void ConverseRunPE(int everReturn);
221
222 /*argc and argv could be changed by machine-specific initialization procedure*/
223 static void MachineSpecificInit(int *argc, char ***argv, int *numNodes, int *myNodeID);
224 /* Used in ConverseRunPE: one is called before ConverseCommonInit;
225   * The other is called after ConverseCommonInit as some data structures
226   * have been initialized, such as the tracing-relatd stuff --Chao Mei
227   */
228 static void MachineSpecificPreCommonInit(int everReturn);
229 static void MachineSpecificPostCommonInit(int everReturn);
230
231 /* Functions regarding machine running on every proc */
232 static void AdvanceCommunication();
233 static void CommunicationServer(int sleepTime);
234 static void CommunicationServerThread(int sleepTime);
235 void ConverseExit(void);
236
237 static void MachineSpecificAdvanceCommunication();
238 static void MachineSpecificDrainResources(); /* used when exit */
239 static void MachineSpecificExit();
240
241 /* Functions providing incoming network messages */
242 void *CmiGetNonLocal(void);
243 #if CMK_NODE_QUEUE_AVAILABLE
244 void *CmiGetNonLocalNodeQ(void);
245 #endif
246 void MachineSpecificPostNonLocal(void);
247
248 /* Utiltiy functions */
249 static char *CopyMsg(char *msg, int len);
250
251 /* ===== End of Common Function Declarations ===== */
252
253 #include "machine-smp.c"
254
255 /* ===== Beginning of Idle-state Related Declarations =====  */
256 typedef struct {
257     int sleepMs; /*Milliseconds to sleep while idle*/
258     int nIdles; /*Number of times we've been idle in a row*/
259     CmiState cs; /*Machine state*/
260 } CmiIdleState;
261
262 static CmiIdleState *CmiNotifyGetState(void);
263
264 /**
265  *  Generally,
266  *
267  *  CmiNotifyIdle is used in non-SMP mode when the proc is idle.
268  *  When the proc is idle, AdvanceCommunication needs to be
269  *  called.
270  *
271  *  CmiNotifyStillIdle and CmiNotifyBeginIdle are used in SMP mode.
272  *
273  *  Different layers have choices of registering different callbacks for
274  *  idle state.
275  */
276 static void CmiNotifyBeginIdle(CmiIdleState *s);
277 static void CmiNotifyStillIdle(CmiIdleState *s);
278 void CmiNotifyIdle(void);
279 /* ===== End of Idle-state Related Declarations =====  */
280
281 /* ===== Beginning of Processor/Node State-related Stuff =====*/
282 #if !CMK_SMP
283 /************ non SMP **************/
284 static struct CmiStateStruct Cmi_state;
285 int _Cmi_mype;
286 int _Cmi_myrank;
287
288 void CmiMemLock() {}
289 void CmiMemUnlock() {}
290
291 #define CmiGetState() (&Cmi_state)
292 #define CmiGetStateN(n) (&Cmi_state)
293
294 void CmiYield(void) {
295     sleep(0);
296 }
297
298 static void CmiStartThreads(char **argv) {
299     CmiStateInit(Cmi_nodestart, 0, &Cmi_state);
300     _Cmi_mype = Cmi_nodestart;
301     _Cmi_myrank = 0;
302 }
303 #else
304 /************** SMP *******************/
305 INLINE_KEYWORD int CmiMyPe(void) {
306     return CmiGetState()->pe;
307 }
308 INLINE_KEYWORD int CmiMyRank(void) {
309     return CmiGetState()->rank;
310 }
311 INLINE_KEYWORD int CmiNodeFirst(int node) {
312     return node*_Cmi_mynodesize;
313 }
314 INLINE_KEYWORD int CmiNodeSize(int node) {
315     return _Cmi_mynodesize;
316 }
317 INLINE_KEYWORD int CmiNodeOf(int pe) {
318     return (pe/_Cmi_mynodesize);
319 }
320 INLINE_KEYWORD int CmiRankOf(int pe) {
321     return pe%_Cmi_mynodesize;
322 }
323 #endif
324 CsvDeclare(CmiNodeState, NodeState);
325 /* ===== End of Processor/Node State-related Stuff =====*/
326
327 #include "immediate.c"
328
329 /* ===== Beginning of Common Function Definitions ===== */
330 static void PerrorExit(const char *msg) {
331     perror(msg);
332     exit(1);
333 }
334
335 /* ##### Beginning of Functions Related with Message Sending OPs ##### */
336 /*Add a message to this processor's receive queue, pe is a rank */
337 void CmiPushPE(int rank,void *msg) {
338     CmiState cs = CmiGetStateN(rank);
339     MACHSTATE2(3,"Pushing message into rank %d's queue %p{",rank, cs->recv);
340 #if CMK_IMMEDIATE_MSG
341     if (CmiIsImmediate(msg)) {
342         MACHSTATE1(3, "[%p] Push Immediate Message begin{",CmiGetState());
343         CMI_DEST_RANK(msg) = rank;
344         CmiPushImmediateMsg(msg);
345         MACHSTATE1(3, "[%p] Push Immediate Message end}",CmiGetState());
346         return;
347     }
348 #endif
349
350     PCQueuePush(cs->recv,msg);
351     CmiIdleLock_addMessage(&cs->idle);
352     MACHSTATE1(3,"} Pushing message into rank %d's queue done",rank);
353 }
354
355 #if CMK_NODE_QUEUE_AVAILABLE
356 /*Add a message to this processor's receive queue */
357 void CmiPushNode(void *msg) {
358     MACHSTATE(3,"Pushing message into NodeRecv queue");
359 #if CMK_IMMEDIATE_MSG
360     if (CmiIsImmediate(msg)) {
361         CMI_DEST_RANK(msg) = 0;
362         CmiPushImmediateMsg(msg);
363         return;
364     }
365 #endif
366     CmiLock(CsvAccess(NodeState).CmiNodeRecvLock);
367     PCQueuePush(CsvAccess(NodeState).NodeRecv,msg);
368     CmiUnlock(CsvAccess(NodeState).CmiNodeRecvLock);
369     {
370         CmiState cs=CmiGetStateN(0);
371         CmiIdleLock_addMessage(&cs->idle);
372     }
373 }
374 #endif
375
376 /* This function handles the msg received as which queue to push into */
377 static INLINE_KEYWORD void handleOneRecvedMsg(int size, char *msg) {
378     int isBcastMsg = 0;
379 #if CMK_BROADCAST_SPANNING_TREE || CMK_BROADCAST_HYPERCUBE
380     isBcastMsg = (CMI_BROADCAST_ROOT(msg)!=0);
381 #endif
382
383     if (isBcastMsg) {
384         handleOneBcastMsg(size, msg);
385         return;
386     }
387
388 #if CMK_NODE_QUEUE_AVAILABLE
389     if (CMI_DEST_RANK(msg)==DGRAM_NODEMESSAGE)
390         CmiPushNode(msg);
391     else
392 #endif
393         CmiPushPE(CMI_DEST_RANK(msg), msg);
394
395 }
396
397
398 /* ##### Beginning of Broadcast-related functions' defitions ##### */
399 static void handleOneBcastMsg(int size, char *msg) {
400     CmiAssert(CMI_BROADCAST_ROOT(msg)!=0);
401 #if CMK_OFFLOAD_BCAST_PROCESS
402     if (CMI_BROADCAST_ROOT(msg)>0) {
403         PCQueuePush(CsvAccess(procBcastQ), msg);
404     } else {
405 #if CMK_NODE_QUEUE_AVAILABLE
406         PCQueuePush(CsvAccess(nodeBcastQ), msg);
407 #endif
408     }
409 #else
410     if (CMI_BROADCAST_ROOT(msg)>0) {
411         processProcBcastMsg(size, msg);
412     } else {
413 #if CMK_NODE_QUEUE_AVAILABLE
414         processNodeBcastMsg(size, msg);
415 #endif
416     }
417 #endif
418 }
419
420 static void processBcastQs() {
421 #if CMK_OFFLOAD_BCAST_PROCESS
422     char *msg;
423     do {
424         msg = PCQueuePop(CsvAccess(procBcastQ));
425         if (!msg) break;
426         MACHSTATE2(4, "[%d]: process a proc-level bcast msg %p begin{", CmiMyNode(), msg);
427         processProcBcastMsg(CMI_MSG_SIZE(msg), msg);
428         MACHSTATE2(4, "[%d]: process a proc-level bcast msg %p end}", CmiMyNode(), msg);
429     } while (1);
430 #if CMK_NODE_QUEUE_AVAILABLE
431     do {
432         msg = PCQueuePop(CsvAccess(nodeBcastQ));
433         if (!msg) break;
434         MACHSTATE2(4, "[%d]: process a node-level bcast msg %p begin{", CmiMyNode(), msg);
435         processNodeBcastMsg(CMI_MSG_SIZE(msg), msg);
436         MACHSTATE2(4, "[%d]: process a node-level bcast msg %p end}", CmiMyNode(), msg);
437     } while (1);
438 #endif
439 #endif
440 }
441
442 static INLINE_KEYWORD void processProcBcastMsg(int size, char *msg) {
443 #if CMK_BROADCAST_SPANNING_TREE
444     SendSpanningChildrenProc(size, msg);
445 #elif CMK_BROADCAST_HYPERCUBE
446     SendHyperCubeProc(size, msg);
447 #endif
448
449     /* Since this function is only called on intermediate nodes,
450      * the rank of this msg should be 0.
451      */
452     CmiAssert(CMI_DEST_RANK(msg)==0);
453     /*CmiPushPE(CMI_DEST_RANK(msg), msg);*/
454     CmiPushPE(0, msg);
455 }
456
457 static INLINE_KEYWORD void processNodeBcastMsg(int size, char *msg) {
458 #if CMK_BROADCAST_SPANNING_TREE
459     SendSpanningChildrenNode(size, msg);
460 #elif CMK_BROADCAST_HYPERCUBE
461     SendHyperCubeNode(size, msg);
462 #endif
463
464     /* In SMP mode, this push operation needs to be executed
465      * after forwarding broadcast messages. If it is executed
466      * earlier, then during the bcast msg forwarding period,
467      * the msg could be already freed on the worker thread.
468      * As a result, the forwarded message could be wrong!
469      * --Chao Mei
470      */
471     CmiPushNode(msg);
472 }
473
474 static void SendSpanningChildren(int size, char *msg, int rankToAssign, int startNode) {
475 #if CMK_BROADCAST_SPANNING_TREE
476     int i, oldRank;
477
478     oldRank = CMI_DEST_RANK(msg);
479     /* doing this is to avoid the multiple assignment in the following for loop */
480     CMI_DEST_RANK(msg) = rankToAssign;
481     /* first send msgs to other nodes */
482     CmiAssert(startNode >=0 &&  startNode<CmiNumNodes());
483     for (i=1; i<=BROADCAST_SPANNING_FACTOR; i++) {
484         int nd = CmiMyNode()-startNode;
485         if (nd<0) nd+=CmiNumNodes();
486         nd = BROADCAST_SPANNING_FACTOR*nd + i;
487         if (nd > CmiNumNodes() - 1) break;
488         nd += startNode;
489         nd = nd%CmiNumNodes();
490         CmiAssert(nd>=0 && nd!=CmiMyNode());
491 #if CMK_BROADCAST_USE_CMIREFERENCE
492         CmiReference(msg);
493         CmiMachineSpecificSendFunc(nd, size, msg, P2P_SYNC);
494 #else
495         char *newmsg = CopyMsg(msg, size);
496         CmiMachineSpecificSendFunc(nd, size, newmsg, P2P_SYNC);
497 #endif
498     }
499     CMI_DEST_RANK(msg) = oldRank;
500 #endif
501 }
502 static void SendHyperCube(int size,  char *msg, int rankToAssign, int startNode) {
503 #if CMK_BROADCAST_HYPERCUBE
504     int i, cnt, tmp, relDist, oldRank;
505     const int dims=CmiNodesDim;
506
507     oldRank = CMI_DEST_RANK(msg);
508     /* doing this is to avoid the multiple assignment in the following for loop */
509     CMI_DEST_RANK(msg) = rankToAssign;
510
511     /* first send msgs to other nodes */
512     relDist = CmiMyNode()-startNode;
513     if (relDist < 0) relDist += CmiNumNodes();
514     cnt=0;
515     tmp = relDist;
516     /* count how many zeros (in binary format) relDist has */
517     for (i=0; i<dims; i++, cnt++) {
518         if (tmp & 1 == 1) break;
519         tmp = tmp >> 1;
520     }
521
522     /*CmiPrintf("ND[%d]: SendHypercube with snd=%d, relDist=%d, cnt=%d\n", CmiMyNode(), startnode, relDist, cnt);*/
523     for (i = cnt-1; i >= 0; i--) {
524         int nd = relDist + (1 << i);
525         if (nd >= CmiNumNodes()) continue;
526         nd = (nd+startNode)%CmiNumNodes();
527         /*CmiPrintf("ND[%d]: send to node %d\n", CmiMyNode(), nd);*/
528         CmiAssert(nd>=0 && nd!=CmiMyNode());
529 #if CMK_BROADCAST_USE_CMIREFERENCE
530         CmiReference(msg);
531         CmiMachineSpecificSendFunc(nd, size, msg, P2P_SYNC);
532 #else
533         char *newmsg = CopyMsg(msg, size);
534         CmiMachineSpecificSendFunc(nd, size, newmsg, P2P_SYNC);
535 #endif
536     }
537     CMI_DEST_RANK(msg) = oldRank;
538 #endif
539 }
540
541 static void SendSpanningChildrenProc(int size, char *msg) {
542     int startpe = CMI_BROADCAST_ROOT(msg)-1;
543     int startnode = CmiNodeOf(startpe);
544 #if CMK_SMP
545     if (startpe > CmiNumPes()) startnode = startpe - CmiNumPes();
546 #endif
547     SendSpanningChildren(size, msg, 0, startnode);
548 #if CMK_SMP
549     /* second send msgs to my peers on this node */
550     SendToPeers(size, msg);
551 #endif
552 }
553
554 /* send msg along the hypercube in broadcast. (Sameer) */
555 static void SendHyperCubeProc(int size, char *msg) {
556     int startpe = CMI_BROADCAST_ROOT(msg)-1;
557     int startnode = CmiNodeOf(startpe);
558 #if CMK_SMP
559     if (startpe > CmiNumPes()) startnode = startpe - CmiNumPes();
560 #endif
561     SendHyperCube(size, msg, 0, startnode);
562 #if CMK_SMP
563     /* second send msgs to my peers on this node */
564     SendToPeers(size, msg);
565 #endif
566 }
567
568 static void SendToPeers(int size, char *msg) {
569     /* FIXME: now it's just a flat p2p send!! When node size is large,
570     * it should also be sent in a tree
571     */
572     int exceptRank = CMI_DEST_RANK(msg);
573     int i;
574     for (i=0; i<exceptRank; i++) {
575         CmiPushPE(i, CopyMsg(msg, size));
576     }
577     for (i=exceptRank+1; i<CmiMyNodeSize(); i++) {
578         CmiPushPE(i, CopyMsg(msg, size));
579     }
580 }
581
582 #if CMK_NODE_QUEUE_AVAILABLE
583 static void SendSpanningChildrenNode(int size, char *msg) {
584     int startnode = -CMI_BROADCAST_ROOT(msg)-1;
585     SendSpanningChildren(size, msg, DGRAM_NODEMESSAGE, startnode);
586 }
587 static void SendHyperCubeNode(int size, char *msg) {
588     int startnode = -CMI_BROADCAST_ROOT(msg)-1;
589     SendHyperCube(size, msg, DGRAM_NODEMESSAGE, startnode);
590 }
591 #endif
592 /*##### End of Broadcast-related functions' defitions #####*/
593
594 /* Functions regarding sending operations */
595 static void CmiSendSelf(char *msg) {
596 #if CMK_IMMEDIATE_MSG
597     if (CmiIsImmediate(msg)) {
598         /* CmiBecomeNonImmediate(msg); */
599         CmiPushImmediateMsg(msg);
600         CmiHandleImmediate();
601         return;
602     }
603 #endif
604     CdsFifo_Enqueue(CpvAccess(CmiLocalQueue),msg);
605 }
606
607 /* Functions regarding P2P send op */
608 #if USE_COMMON_SYNC_P2P
609 void CmiSyncSendFn(int destPE, int size, char *msg) {
610     char *dupmsg = CopyMsg(msg, size);
611     CmiFreeSendFn(destPE, size, dupmsg);
612 }
613
614 void CmiFreeSendFn(int destPE, int size, char *msg) {
615     CMI_SET_BROADCAST_ROOT(msg, 0);
616     CQdCreate(CpvAccess(cQdState), 1);
617     if (CmiMyPe()==destPE) {
618         CmiSendSelf(msg);
619     } else {
620         int destNode = CmiNodeOf(destPE);
621 #if CMK_SMP
622         if (CmiMyNode()==destNode) {
623             CmiPushPE(CmiRankOf(destPE), msg);
624             return;
625         }
626 #endif
627         CMI_DEST_RANK(msg) = CmiRankOf(destPE);
628         CmiMachineSpecificSendFunc(destNode, size, msg, P2P_SYNC);
629     }
630 }
631 #endif
632
633 #if USE_COMMON_ASYNC_P2P
634 CmiCommHandle CmiAsyncSendFn(int destPE, int size, char *msg) {
635     int destNode = CmiNodeOf(destPE);
636     if (destNode == CmiMyNode()) {
637         CmiSyncSendFn(destPE,size,msg);
638         return 0;
639     } else {
640         return CmiMachineSpecificSendFunc(destPE, size, msg, P2P_ASYNC);
641     }
642 }
643 #endif
644
645 #if USE_COMMON_SYNC_BCAST
646 /* Functions regarding broadcat op that sends to every one else except me */
647 void CmiSyncBroadcastFn(int size, char *msg) {
648     int mype = CmiMyPe();
649     int i;
650
651     CQdCreate(CpvAccess(cQdState), CmiNumPes()-1);
652 #if CMK_SMP
653     /*record the rank to avoid re-sending the msg in  spanning tree or hypercube*/
654     CMI_DEST_RANK(msg) = CmiMyRank();
655 #endif
656
657 #if CMK_BROADCAST_SPANNING_TREE
658     CMI_SET_BROADCAST_ROOT(msg, mype+1);
659     SendSpanningChildrenProc(size, msg);
660 #elif CMK_BROADCAST_HYPERCUBE
661     CMI_SET_BROADCAST_ROOT(msg, mype+1);
662     SendHyperCubeProc(size, msg);
663 #else
664     for ( i=mype+1; i<_Cmi_numpes; i++ )
665         CmiSyncSendFn(i, size, msg) ;
666     for ( i=0; i<mype; i++ )
667         CmiSyncSendFn(i, size, msg) ;
668 #endif
669
670     /*CmiPrintf("In  SyncBroadcast broadcast\n");*/
671 }
672
673 void CmiFreeBroadcastFn(int size, char *msg) {
674     CmiSyncBroadcastFn(size,msg);
675     CmiFree(msg);
676 }
677 #endif
678
679 #if USE_COMMON_ASYNC_BCAST
680 /* FIXME: should use spanning or hypercube, but luckily async is never used */
681 CmiCommHandle CmiAsyncBroadcastFn(int size, char *msg) {
682     /*CmiPrintf("In  AsyncBroadcast broadcast\n");*/
683     CmiAbort("CmiAsyncBroadcastFn should never be called");
684     return 0;
685 }
686 #endif
687
688 /* Functions regarding broadcat op that sends to every one */
689 void CmiSyncBroadcastAllFn(int size, char *msg) {
690     CmiSyncSendFn(CmiMyPe(), size, msg) ;
691     CmiSyncBroadcastFn(size, msg);
692 }
693
694 void CmiFreeBroadcastAllFn(int size, char *msg) {
695     CmiSendSelf(msg);
696     CmiSyncBroadcastFn(size, msg);
697 }
698
699 CmiCommHandle CmiAsyncBroadcastAllFn(int size, char *msg) {
700     CmiSendSelf(CopyMsg(msg, size));
701     return CmiAsyncBroadcastFn(size, msg);
702 }
703
704 #if CMK_NODE_QUEUE_AVAILABLE
705 static void CmiSendNodeSelf(char *msg) {
706 #if CMK_IMMEDIATE_MSG
707     if (CmiIsImmediate(msg)) {
708         CmiPushImmediateMsg(msg);
709         if (!_immRunning) CmiHandleImmediate();
710         return;
711     }
712 #endif
713     CmiLock(CsvAccess(NodeState).CmiNodeRecvLock);
714     PCQueuePush(CsvAccess(NodeState).NodeRecv, msg);
715     CmiUnlock(CsvAccess(NodeState).CmiNodeRecvLock);
716 }
717
718 #if USE_COMMON_ASYNC_P2P
719 void CmiSyncNodeSendFn(int destNode, int size, char *msg) {
720     char *dupmsg = CopyMsg(msg, size);
721     CmiFreeNodeSendFn(destNode, size, dupmsg);
722 }
723
724 void CmiFreeNodeSendFn(int destNode, int size, char *msg) {
725     CMI_DEST_RANK(msg) = DGRAM_NODEMESSAGE;
726     CQdCreate(CpvAccess(cQdState), 1);
727     CMI_SET_BROADCAST_ROOT(msg, 0);
728     if (destNode == CmiMyNode()) {
729         CmiSendNodeSelf(msg);
730     } else {
731         CmiMachineSpecificSendFunc(destNode, size, msg, P2P_SYNC);
732     }
733 }
734 #endif
735
736 #if USE_COMMON_ASYNC_P2P
737 CmiCommHandle CmiAsyncNodeSendFn(int destNode, int size, char *msg) {
738     if (destNode == CmiMyNode()) {
739         CmiSyncNodeSendFn(destNode, size, msg);
740         return 0;
741     } else {
742         return CmiMachineSpecificSendFunc(destNode, size, msg, P2P_ASYNC);
743     }
744 }
745 #endif
746
747 #if USE_COMMON_SYNC_BCAST
748 void CmiSyncNodeBroadcastFn(int size, char *msg) {
749     int mynode = CmiMyNode();
750     int i;
751     CQdCreate(CpvAccess(cQdState), CmiNumNodes()-1);
752 #if CMK_BROADCAST_SPANNING_TREE
753     CMI_SET_BROADCAST_ROOT(msg, -CmiMyNode()-1);
754     SendSpanningChildrenNode(size, msg);
755 #elif CMK_BROADCAST_HYPERCUBE
756     CMI_SET_BROADCAST_ROOT(msg, -CmiMyNode()-1);
757     SendHyperCubeNode(size, msg);
758 #else
759     for (i=mynode+1; i<CmiNumNodes(); i++)
760         CmiSyncNodeSendFn(i, size, msg);
761     for (i=0; i<mynode; i++)
762         CmiSyncNodeSendFn(i, size, msg);
763 #endif
764 }
765
766 void CmiFreeNodeBroadcastFn(int size, char *msg) {
767     CmiSyncNodeBroadcastFn(size, msg);
768     CmiFree(msg);
769 }
770 #endif
771
772 #if USE_COMMON_ASYNC_BCAST
773 CmiCommHandle CmiAsyncNodeBroadcastFn(int size, char *msg) {
774     CmiSyncNodeBroadcastFn(size, msg);
775     return 0;
776 }
777 #endif
778
779 void CmiSyncNodeBroadcastAllFn(int size, char *msg) {
780     CmiSyncNodeSendFn(CmiMyNode(), size, msg);
781     CmiSyncNodeBroadcastFn(size, msg);
782 }
783
784 CmiCommHandle CmiAsyncNodeBroadcastAllFn(int size, char *msg) {
785     CmiSendNodeSelf(CopyMsg(msg, size));
786     return CmiAsyncNodeBroadcastFn(size, msg);
787 }
788
789 void CmiFreeNodeBroadcastAllFn(int size, char *msg) {
790     CmiSyncNodeBroadcastFn(size, msg);
791     /* Since it's a node-level msg, the msg could be executed on any other
792      * procs on the same node. This means, the push of this msg to the
793      * node-level queue could be immediately followed a pop of this msg on
794      * other cores on the same node even when this msg has not been sent to
795      * other nodes. This is the reason CmiSendNodeSelf must be called after
796      * CmiSyncNodeBroadcastFn -Chao Mei
797      */
798     CmiSendNodeSelf(msg);
799 }
800 #endif
801 /* ##### End of Functions Related with Message Sending OPs ##### */
802
803 /* ##### Beginning of Functions Related with Machine Startup ##### */
804 void ConverseInit(int argc, char **argv, CmiStartFn fn, int usched, int initret) {
805     int tmp;
806     /* processor per node */
807     _Cmi_mynodesize = 1;
808     if (!CmiGetArgInt(argv,"+ppn", &_Cmi_mynodesize))
809         CmiGetArgInt(argv,"++ppn", &_Cmi_mynodesize);
810 #if ! CMK_SMP
811     if (_Cmi_mynodesize > 1 && _Cmi_mynode == 0)
812         CmiAbort("+ppn cannot be used in non SMP version!\n");
813 #endif
814
815     /* Network progress function is used to poll the network when for
816     messages. This flushes receive buffers on some  implementations*/
817     networkProgressPeriod = NETWORK_PROGRESS_PERIOD_DEFAULT;
818     CmiGetArgInt(argv, "+networkProgressPeriod", &networkProgressPeriod);
819
820     /* _Cmi_mynodesize has to be obtained before MachineSpecificInit
821      * because it may be used inside MachineSpecificInit
822      */
823     /* argv could be changed inside MachineSpecificInit */
824     /* Inside this function, the number of nodes and my node id are obtained */
825     MachineSpecificInit(&argc, &argv, &_Cmi_numnodes, &_Cmi_mynode);
826
827     _Cmi_numpes = _Cmi_numnodes * _Cmi_mynodesize;
828     Cmi_nodestart = _Cmi_mynode * _Cmi_mynodesize;
829     Cmi_argvcopy = CmiCopyArgs(argv);
830     Cmi_argv = argv;
831     Cmi_startfn = fn;
832     Cmi_usrsched = usched;
833
834     /* CmiTimerInit(); */
835 #if CMK_BROADCAST_HYPERCUBE
836     /* CmiNodesDim = ceil(log2(CmiNumNodes)) except when #nodes is 1*/
837     tmp = CmiNumNodes()-1;
838     CmiNodesDim = 0;
839     while (tmp>0) {
840         CmiNodesDim++;
841         tmp = tmp >> 1;
842     }
843     if (CmiNumNodes()==1) CmiNodesDim=1;
844 #endif
845
846     CsvInitialize(CmiNodeState, NodeState);
847     CmiNodeStateInit(&CsvAccess(NodeState));
848 #if CMK_SMP
849     commThdExitLock = CmiCreateLock();
850 #endif
851
852 #if CMK_OFFLOAD_BCAST_PROCESS
853     /* the actual queues should be created on comm thread considering NUMA in SMP */
854     CsvInitialize(PCQueue, procBcastQ);
855 #if CMK_NODE_QUEUE_AVAILABLE
856     CsvInitialize(PCQueue, nodeBcastQ);
857 #endif
858 #endif
859
860     CmiStartThreads(argv);
861     ConverseRunPE(initret);
862 }
863
864 static void ConverseRunPE(int everReturn) {
865     CmiState cs;
866     char** CmiMyArgv;
867
868     MachineSpecificPreCommonInit(everReturn);
869
870 #if CMK_OFFLOAD_BCAST_PROCESS
871     int createQueue = 1;
872 #if CMK_SMP
873 #if CMK_SMP_NO_COMMTHD
874     /* If there's no comm thread, then the queue is created on rank 0 */
875     if (CmiMyRank()) createQueue = 0;
876 #else
877     if (CmiMyRank()<CmiMyNodeSize()) createQueue = 0;
878 #endif
879 #endif
880
881     if (createQueue) {
882         CsvAccess(procBcastQ) = PCQueueCreate();
883 #if CMK_NODE_QUEUE_AVAILABLE
884         CsvAccess(nodeBcastQ) = PCQueueCreate();
885 #endif
886     }
887 #endif
888
889     CmiNodeAllBarrier();
890
891     cs = CmiGetState();
892     CpvInitialize(void *,CmiLocalQueue);
893     CpvAccess(CmiLocalQueue) = cs->localqueue;
894
895     if (CmiMyRank())
896         CmiMyArgv=CmiCopyArgs(Cmi_argvcopy);
897     else
898         CmiMyArgv=Cmi_argv;
899
900     CthInit(CmiMyArgv);
901
902     /* initialize the network progress counter*/
903     /* Network progress function is used to poll the network when for
904        messages. This flushes receive buffers on some  implementations*/
905     CpvInitialize(unsigned , networkProgressCount);
906     CpvAccess(networkProgressCount) = 0;
907
908     ConverseCommonInit(CmiMyArgv);
909
910     MachineSpecificPostCommonInit(everReturn);
911
912     /* Converse initialization finishes, immediate messages can be processed.
913        node barrier previously should take care of the node synchronization */
914     _immediateReady = 1;
915
916     /* communication thread */
917     if (CmiMyRank() == CmiMyNodeSize()) {
918         Cmi_startfn(CmiGetArgc(CmiMyArgv), CmiMyArgv);
919         while (1) CommunicationServerThread(5);
920     } else { /* worker thread */
921         if (!everReturn) {
922             Cmi_startfn(CmiGetArgc(CmiMyArgv), CmiMyArgv);
923             if (Cmi_usrsched==0) CsdScheduler(-1);
924             ConverseExit();
925         }
926     }
927 }
928 /* ##### End of Functions Related with Machine Startup ##### */
929
930 /* ##### Beginning of Functions Related with Machine Running ##### */
931 static INLINE_KEYWORD void AdvanceCommunication() {
932     int doProcessBcast = 1;
933
934     MachineSpecificAdvanceCommunication();
935
936 #if CMK_OFFLOAD_BCAST_PROCESS
937 #if CMK_SMP_NO_COMMTHD
938     /*FIXME: only asks rank 0 to process bcast msgs, so perf may suffer*/
939     if (CmiMyRank()) doProcessBcast = 0;
940 #endif
941     if (doProcessBcast) processBcastQs();
942 #endif
943
944 #if CMK_IMMEDIATE_MSG
945 #if !CMK_SMP
946     CmiHandleImmediate();
947 #endif
948 #if CMK_SMP && CMK_SMP_NO_COMMTHD
949     if (CmiMyRank()==0) CmiHandleImmediate();
950 #endif
951 #endif
952
953 }
954
955 static void CommunicationServer(int sleepTime) {
956 #if CMK_SMP
957     AdvanceCommunication();
958
959     if (commThdExit == CmiMyNodeSize()) {
960         MACHSTATE(2, "CommunicationServer exiting {");
961         MachineSpecificDrainResources();
962         MACHSTATE(2, "} CommunicationServer EXIT");
963
964         ConverseCommonExit();
965
966         MachineSpecificExit();
967     }
968 #endif
969 }
970
971 static void CommunicationServerThread(int sleepTime) {
972     CommunicationServer(sleepTime);
973 #if CMK_IMMEDIATE_MSG
974     CmiHandleImmediate();
975 #endif
976 }
977
978 void ConverseExit(void) {
979 #if !CMK_SMP
980     MachineSpecificDrainResources();
981 #endif
982
983     ConverseCommonExit();
984
985 #if (CMK_DEBUG_MODE || CMK_WEB_MODE || NODE_0_IS_CONVHOST)
986     if (CmiMyPe() == 0) CmiPrintf("End of program\n");
987 #endif
988
989 #if !CMK_SMP
990     MachineSpecificExit();
991 #else
992     /* In SMP, the communication thread will exit */
993     /* atomic increment */
994     CmiLock(commThdExitLock);
995     commThdExit++;
996     CmiUnlock(commThdExitLock);
997     while (1) CmiYield();
998 #endif
999 }
1000 /* ##### End of Functions Related with Machine Running ##### */
1001
1002
1003 /* ##### Beginning of Functions Providing Incoming Network Messages ##### */
1004 void *CmiGetNonLocal(void) {
1005     CmiState cs = CmiGetState();
1006     void *msg = NULL;
1007
1008 #if !CMK_SMP || CMK_SMP_NO_COMMTHD
1009     /**
1010       * In SMP mode with comm thread, it's possible a normal
1011       * msg is sent from an immediate msg which is executed
1012       * on comm thread. In this case, the msg is sent to
1013       * the network queue of the work thread. Therefore,
1014       * even there's only one worker thread, the polling of
1015       * network queue is still required. -Chao Mei
1016       */
1017     if (CmiNumPes() == 1) return NULL;
1018 #endif
1019
1020     MACHSTATE2(3, "[%p] CmiGetNonLocal begin %d{", cs, CmiMyPe());
1021     CmiIdleLock_checkMessage(&cs->idle);
1022     /* ?????although it seems that lock is not needed, I found it crashes very often
1023        on mpi-smp without lock */
1024 #if !CMK_SMP
1025     AdvanceCommunication();
1026 #endif
1027
1028     msg = PCQueuePop(cs->recv);
1029
1030 #if !CMK_SMP
1031     MachineSpecificPostNonLocal();
1032 #endif
1033
1034     MACHSTATE3(3,"[%p] CmiGetNonLocal from queue %p with msg %p end }",CmiGetState(),(cs->recv), msg);
1035
1036     return msg;
1037 }
1038 #if CMK_NODE_QUEUE_AVAILABLE
1039 void *CmiGetNonLocalNodeQ(void) {
1040     CmiState cs = CmiGetState();
1041     char *result = 0;
1042     CmiIdleLock_checkMessage(&cs->idle);
1043     if (!PCQueueEmpty(CsvAccess(NodeState).NodeRecv)) {
1044         MACHSTATE1(3,"CmiGetNonLocalNodeQ begin %d {", CmiMyPe());
1045         CmiLock(CsvAccess(NodeState).CmiNodeRecvLock);
1046         result = (char *) PCQueuePop(CsvAccess(NodeState).NodeRecv);
1047         CmiUnlock(CsvAccess(NodeState).CmiNodeRecvLock);
1048         MACHSTATE1(3,"} CmiGetNonLocalNodeQ end %d ", CmiMyPe());
1049     }
1050
1051     return result;
1052 }
1053 #endif
1054 /* ##### End of Functions Providing Incoming Network Messages ##### */
1055
1056 /* ##### Beginning of Functions Related with Idle-state ##### */
1057 static CmiIdleState *CmiNotifyGetState(void) {
1058     CmiIdleState *s=(CmiIdleState *)malloc(sizeof(CmiIdleState));
1059     s->sleepMs=0;
1060     s->nIdles=0;
1061     s->cs=CmiGetState();
1062     return s;
1063 }
1064
1065 static void CmiNotifyBeginIdle(CmiIdleState *s) {
1066     s->sleepMs=0;
1067     s->nIdles=0;
1068 }
1069
1070 /*Number of times to spin before sleeping*/
1071 #define SPINS_BEFORE_SLEEP 20
1072 static void CmiNotifyStillIdle(CmiIdleState *s) {
1073     MACHSTATE1(2,"still idle (%d) begin {",CmiMyPe())
1074     s->nIdles++;
1075     if (s->nIdles>SPINS_BEFORE_SLEEP) { /*Start giving some time back to the OS*/
1076         s->sleepMs+=2;
1077         if (s->sleepMs>10) s->sleepMs=10;
1078     }
1079
1080     if (s->sleepMs>0) {
1081         MACHSTATE1(2,"idle lock(%d) {",CmiMyPe())
1082         CmiIdleLock_sleep(&s->cs->idle,s->sleepMs);
1083         MACHSTATE1(2,"} idle lock(%d)",CmiMyPe())
1084     }
1085
1086 #if !CMK_SMP
1087     AdvanceCommunication();
1088 #endif
1089
1090     MACHSTATE1(2,"still idle (%d) end {",CmiMyPe())
1091 }
1092
1093 /* usually called in non-smp mode */
1094 void CmiNotifyIdle(void) {
1095     AdvanceCommunication();
1096     CmiYield();
1097 }
1098 /* ##### End of Functions Related with Idle-state ##### */
1099
1100 /* Utiltiy functions */
1101 static char *CopyMsg(char *msg, int len) {
1102     char *copy = (char *)CmiAlloc(len);
1103 #if CMK_ERROR_CHECKING
1104     if (!copy) {
1105         CmiAbort("Error: out of memory in machine layer\n");
1106     }
1107 #endif
1108     memcpy(copy, msg, len);
1109     return copy;
1110 }
1111 /* ===== End of Common Function Definitions ===== */