Merge branch 'charm', remote-tracking branch 'origin' into harshitha/adaptive_lb
[charm.git] / src / arch / mpi / machine.c
1
2 /** @file
3  * MPI based machine layer
4  * @ingroup Machine
5  */
6 /*@{*/
7
8 #include <stdio.h>
9 #include <errno.h>
10 #include "converse.h"
11 #include <mpi.h>
12 #if CMK_TIMER_USE_XT3_DCLOCK
13 #include <catamount/dclock.h>
14 #endif
15
16
17 #ifdef AMPI
18 #  warning "We got the AMPI version of mpi.h, instead of the system version--"
19 #  warning "   Try doing an 'rm charm/include/mpi.h' and building again."
20 #  error "Can't build Charm++ using AMPI version of mpi.h header"
21 #endif
22
23 /*Support for ++debug: */
24 #if defined(_WIN32) && ! defined(__CYGWIN__)
25 #include <windows.h>
26 #include <wincon.h>
27 #include <sys/types.h>
28 #include <sys/timeb.h>
29 static void sleep(int secs) {
30     Sleep(1000*secs);
31 }
32 #else
33 #include <unistd.h> /*For getpid()*/
34 #endif
35 #include <stdlib.h> /*For sleep()*/
36
37 #include "machine.h"
38 #include "pcqueue.h"
39
40 /* =======Beginning of Definitions of Performance-Specific Macros =======*/
41 /* Whether to use multiple send queue in SMP mode */
42 #define MULTI_SENDQUEUE    0
43
44 /* ###Beginning of flow control related macros ### */
45 #define CMI_EXERT_SEND_CAP 0
46 #define CMI_EXERT_RECV_CAP 0
47
48 #define CMI_DYNAMIC_EXERT_CAP 0
49 /* This macro defines the max number of msgs in the sender msg buffer
50  * that is allowed for recving operation to continue
51  */
52 static int CMI_DYNAMIC_OUTGOING_THRESHOLD=4;
53 #define CMI_DYNAMIC_MAXCAPSIZE 1000
54 static int CMI_DYNAMIC_SEND_CAPSIZE=4;
55 static int CMI_DYNAMIC_RECV_CAPSIZE=3;
56 /* initial values, -1 indiates there's no cap */
57 static int dynamicSendCap = CMI_DYNAMIC_MAXCAPSIZE;
58 static int dynamicRecvCap = CMI_DYNAMIC_MAXCAPSIZE;
59
60 #if CMI_EXERT_SEND_CAP
61 #define SEND_CAP 3
62 #endif
63
64 #if CMI_EXERT_RECV_CAP
65 #define RECV_CAP 2
66 #endif
67 /* ###End of flow control related macros ### */
68
69 /* ###Beginning of machine-layer-tracing related macros ### */
70 #if CMK_TRACE_ENABLED && CMK_SMP_TRACE_COMMTHREAD
71 #define CMI_MPI_TRACE_MOREDETAILED 0
72 #undef CMI_MPI_TRACE_USEREVENTS
73 #define CMI_MPI_TRACE_USEREVENTS 1
74 #else
75 #undef CMK_SMP_TRACE_COMMTHREAD
76 #define CMK_SMP_TRACE_COMMTHREAD 0
77 #endif
78
79 #define CMK_TRACE_COMMOVERHEAD 0
80 #if CMK_TRACE_ENABLED && CMK_TRACE_COMMOVERHEAD
81 #undef CMI_MPI_TRACE_USEREVENTS
82 #define CMI_MPI_TRACE_USEREVENTS 1
83 #else
84 #undef CMK_TRACE_COMMOVERHEAD
85 #define CMK_TRACE_COMMOVERHEAD 0
86 #endif
87
88 #if CMI_MPI_TRACE_USEREVENTS && CMK_TRACE_ENABLED && !CMK_TRACE_IN_CHARM
89 CpvStaticDeclare(double, projTraceStart);
90 #define  START_EVENT()  CpvAccess(projTraceStart) = CmiWallTimer();
91 #define  END_EVENT(x)   traceUserBracketEvent(x, CpvAccess(projTraceStart), CmiWallTimer());
92 #else
93 #define  START_EVENT()
94 #define  END_EVENT(x)
95 #endif
96
97 #if CMK_SMP_TRACE_COMMTHREAD
98 #define START_TRACE_SENDCOMM(msg)  \
99                         int isTraceEligible = traceBeginCommOp(msg); \
100                         if(isTraceEligible) traceSendMsgComm(msg);
101 #define END_TRACE_SENDCOMM(msg) if(isTraceEligible) traceEndCommOp(msg);
102 #define START_TRACE_RECVCOMM(msg) CpvAccess(projTraceStart) = CmiWallTimer();
103 #define END_TRACE_RECVCOMM(msg) \
104                         if(traceBeginCommOp(msg)){ \
105                             traceChangeLastTimestamp(CpvAccess(projTraceStart)); \
106                             traceSendMsgComm(msg); \
107                             traceEndCommOp(msg); \
108                         }
109 #define CONDITIONAL_TRACE_USER_EVENT(x) \
110                         do{ \
111                             double etime = CmiWallTimer(); \
112                             if(etime - CpvAccess(projTraceStart) > 5*1e-6){ \
113                                 traceUserBracketEvent(x, CpvAccess(projTraceStart), etime); \
114                             }\
115                         }while(0);
116 #else
117 #define START_TRACE_SENDCOMM(msg)
118 #define END_TRACE_SENDCOMM(msg)
119 #define START_TRACE_RECVCOMM(msg)
120 #define END_TRACE_RECVCOMM(msg)
121 #define CONDITIONAL_TRACE_USER_EVENT(x)
122 #endif
123
124 /* ###End of machine-layer-tracing related macros ### */
125
126 /* ###Beginning of POST_RECV related macros ### */
127 /*
128  * If MPI_POST_RECV is defined, we provide default values for
129  * size and number of posted recieves. If MPI_POST_RECV_COUNT
130  * is set then a default value for MPI_POST_RECV_SIZE is used
131  * if not specified by the user.
132  */
133 #define MPI_POST_RECV 0
134
135 /* Making those parameters configurable for testing them easily */
136
137 #if MPI_POST_RECV
138 #define MPI_DYNAMIC_POST_RECV 0
139
140 /* Note the tag offset of a msg is determined by
141  * (its size - MPI_RECV_LOWERSIZE)/MPI_POST_RECV_INC.
142  * based on POST_RECV_TAG.
143  */
144 static int MPI_POST_RECV_COUNT=10;
145
146 /* The range of msgs to be tracked for histogramming */
147 static int MPI_POST_RECV_LOWERSIZE=8000;
148 static int MPI_POST_RECV_UPPERSIZE=64000;
149
150 /* The increment of msg size to be tracked, i.e. the histogram bucket size */
151 static int MPI_POST_RECV_INC = 1000;
152
153 /* The unit increment of msg cnt for increase #buf for a post recved msg */
154 static int MPI_POST_RECV_MSG_INC = 400;
155
156 /* If the #msg exceeds this value, post recv is created for such msg */
157 static int MPI_POST_RECV_MSG_CNT_THRESHOLD = 200;
158
159 /* The frequency of checking the existing posted recv buffers in the unit of #msgs */
160 static int MPI_POST_RECV_FREQ = 1000;
161
162 static int MPI_POST_RECV_SIZE;
163
164 typedef struct mpiPostRecvList {
165     /* POST_RECV_TAG + msgSizeIdx is the recv tag;
166      * Based on this value, this buf corresponds to msg size ranging
167      * [msgSizeIdx*MPI_POST_RECV_INC, (msgSizeIdx+1)*MPI_POST_RECV_INC)
168      */
169     int msgSizeIdx;
170     int bufCnt;
171     MPI_Request *postedRecvReqs;
172     char **postedRecvBufs;
173     struct mpiPostRecvList *next;
174 } MPIPostRecvList;
175 CpvDeclare(MPIPostRecvList *, postRecvListHdr);
176 CpvDeclare(MPIPostRecvList *, curPostRecvPtr);
177 CpvDeclare(int, msgRecvCnt);
178
179 CpvDeclare(unsigned long long, Cmi_posted_recv_total);
180 CpvDeclare(unsigned long long, Cmi_unposted_recv_total);
181 CpvDeclare(MPI_Request*, CmiPostedRecvRequests); /* An array of request handles for posted recvs */
182 CpvDeclare(char**,CmiPostedRecvBuffers);
183
184 /* Note: currently MPI doesn't provide a function whether a request is in progress.
185  * For example, a irecv has been filled partially. Then a call to MPI_Test still returns
186  * indicating it has not been finished. If only relying on this result, then calling
187  * MPI_Cancel will result in a loss of this msg. The dynamic post recv mechanism
188  * can only be safely used in a synchronized point such as load balancing.
189  */
190 #if MPI_DYNAMIC_POST_RECV
191 static int MSG_HISTOGRAM_BINSIZE;
192 static int MAX_HISTOGRAM_BUCKETS; /* only cares msg size less 2 MB */
193 CpvDeclare(int *, MSG_HISTOGRAM_ARRAY);
194 static void recordMsgHistogramInfo(int size);
195 static void reportMsgHistogramInfo();
196 #endif /* end of MPI_DYNAMIC_POST_RECV defined */
197
198 #endif /* end of MPI_POST_RECV defined */
199
200 /* Defining this macro will use MPI_Irecv instead of MPI_Recv for
201  * large messages. This could save synchronization overhead caused by
202  * the rzv protocol used by MPI
203  */
204 #define USE_ASYNC_RECV_FUNC 0
205
206 #ifdef USE_ASYNC_RECV_FUNC
207 static int IRECV_MSG_THRESHOLD = 8000;
208 typedef struct IRecvListEntry{
209     MPI_Request req;
210     char *msg;
211     int size;
212     struct IRecvListEntry *next;
213 }*IRecvList;
214
215 static IRecvList freedIrecvList = NULL; /* used to recycle the entries */
216 static IRecvList waitIrecvListHead = NULL; /* points to the guardian entry, i.e., the next of it points to the first entry */
217 static IRecvList waitIrecvListTail = NULL; /* points to the last entry */
218
219 static IRecvList irecvListEntryAllocate(){
220     IRecvList ret;
221     if(freedIrecvList == NULL) {
222         ret = (IRecvList)malloc(sizeof(struct IRecvListEntry));        
223         return ret;
224     } else {
225         ret = freedIrecvList;
226         freedIrecvList = freedIrecvList->next;
227         return ret;
228     }
229 }
230 static void irecvListEntryFree(IRecvList used){
231     used->next = freedIrecvList;
232     freedIrecvList = used;
233 }
234
235 #endif /* end of USE_ASYNC_RECV_FUNC */
236
237 /* Providing functions for external usage to set up the dynamic recv buffer
238  * when the user is aware that it's safe to call such function
239  */
240 void CmiSetupMachineRecvBuffers();
241
242 #define CAPTURE_MSG_HISTOGRAM 0
243 #if CAPTURE_MSG_HISTOGRAM && !MPI_DYNAMIC_POST_RECV
244 static int MSG_HISTOGRAM_BINSIZE=1000;
245 static int MAX_HISTOGRAM_BUCKETS=2000; /* only cares msg size less 2 MB */
246 CpvDeclare(int *, MSG_HISTOGRAM_ARRAY);
247 static void recordMsgHistogramInfo(int size);
248 static void reportMsgHistogramInfo();
249 #endif
250
251 /* to avoid MPI's in order delivery, changing MPI Tag all the time */
252 #define TAG     1375
253 #if MPI_POST_RECV
254 #define POST_RECV_TAG       (TAG+1)
255 #define BARRIER_ZERO_TAG  TAG
256 #else
257 #define BARRIER_ZERO_TAG   (TAG-1)
258 #endif
259 /* ###End of POST_RECV related related macros ### */
260
261 #if CMK_BLUEGENEL
262 #define MAX_QLEN 8
263 #define NETWORK_PROGRESS_PERIOD_DEFAULT 16
264 #else
265 #define NETWORK_PROGRESS_PERIOD_DEFAULT 0
266 #define MAX_QLEN 200
267 #endif
268 /* =======End of Definitions of Performance-Specific Macros =======*/
269
270
271 /* =====Beginning of Definitions of Message-Corruption Related Macros=====*/
272 #define CMI_MAGIC(msg)                   ((CmiMsgHeaderBasic *)msg)->magic
273 #define CHARM_MAGIC_NUMBER               126
274
275 #if CMK_ERROR_CHECKING
276 extern unsigned char computeCheckSum(unsigned char *data, int len);
277 static int checksum_flag = 0;
278 #define CMI_SET_CHECKSUM(msg, len)      \
279         if (checksum_flag)  {   \
280           ((CmiMsgHeaderBasic *)msg)->cksum = 0;        \
281           ((CmiMsgHeaderBasic *)msg)->cksum = computeCheckSum((unsigned char*)msg, len);        \
282         }
283 #define CMI_CHECK_CHECKSUM(msg, len)    \
284         if (checksum_flag)      \
285           if (computeCheckSum((unsigned char*)msg, len) != 0)   \
286             CmiAbort("Fatal error: checksum doesn't agree!\n");
287 #else
288 #define CMI_SET_CHECKSUM(msg, len)
289 #define CMI_CHECK_CHECKSUM(msg, len)
290 #endif
291 /* =====End of Definitions of Message-Corruption Related Macros=====*/
292
293 /* =====Beginning of Declarations of Machine Specific Variables===== */
294 #include <signal.h>
295 void (*signal_int)(int);
296
297 static int _thread_provided = -1; /* Indicating MPI thread level */
298 static int idleblock = 0;
299
300 /* A simple list for msgs that have been sent by MPI_Isend */
301 typedef struct msg_list {
302     char *msg;
303     struct msg_list *next;
304     int size, destpe, mode;
305     MPI_Request req;
306 } SMSG_LIST;
307
308 CpvStaticDeclare(SMSG_LIST *, sent_msgs);
309 CpvStaticDeclare(SMSG_LIST *, end_sent);
310
311 CpvStaticDeclare(int, MsgQueueLen);
312 static int request_max;
313 /*FLAG: consume outstanding Isends in scheduler loop*/
314 static int no_outstanding_sends=0;
315
316 #if NODE_0_IS_CONVHOST
317 int inside_comm = 0;
318 #endif
319
320 typedef struct ProcState {
321 #if MULTI_SENDQUEUE
322     PCQueue      sendMsgBuf;       /* per processor message sending queue */
323 #endif
324     CmiNodeLock  recvLock;                  /* for cs->recv */
325 } ProcState;
326 static ProcState  *procState;
327
328 #if CMK_SMP && !MULTI_SENDQUEUE
329 static PCQueue sendMsgBuf;
330 static CmiNodeLock  sendMsgBufLock = NULL;        /* for sendMsgBuf */
331 #endif
332 /* =====End of Declarations of Machine Specific Variables===== */
333
334 #if CMK_MEM_CHECKPOINT
335 #define FAIL_TAG   1200
336 int num_workpes, total_pes;
337 int *petorank = NULL;
338 int  nextrank;
339 void mpi_end_spare();
340 #endif
341
342 /* =====Beginning of Declarations of Machine Specific Functions===== */
343 /* Utility functions */
344 #if CMK_BLUEGENEL
345 extern void MPID_Progress_test();
346 #endif
347 static size_t CmiAllAsyncMsgsSent(void);
348 static void CmiReleaseSentMessages(void);
349 static int PumpMsgs(void);
350 static void PumpMsgsBlocking(void);
351
352 #if CMK_SMP
353 static int MsgQueueEmpty();
354 static int RecvQueueEmpty();
355 static int SendMsgBuf();
356 static  void EnqueueMsg(void *m, int size, int node, int mode);
357 #endif
358
359 /* The machine-specific send function */
360 static CmiCommHandle MachineSpecificSendForMPI(int destNode, int size, char *msg, int mode);
361 #define LrtsSendFunc MachineSpecificSendForMPI
362
363 /* ### Beginning of Machine-startup Related Functions ### */
364 static void MachineInitForMPI(int *argc, char ***argv, int *numNodes, int *myNodeID);
365 #define LrtsInit MachineInitForMPI
366
367 static void MachinePreCommonInitForMPI(int everReturn);
368 static void MachinePostCommonInitForMPI(int everReturn);
369 #define LrtsPreCommonInit MachinePreCommonInitForMPI
370 #define LrtsPostCommonInit MachinePostCommonInitForMPI
371 /* ### End of Machine-startup Related Functions ### */
372
373 /* ### Beginning of Machine-running Related Functions ### */
374 static void AdvanceCommunicationForMPI(int whenidle);
375 #define LrtsAdvanceCommunication AdvanceCommunicationForMPI
376
377 static void DrainResourcesForMPI(); /* used when exit */
378 #define LrtsDrainResources DrainResourcesForMPI
379
380 static void MachineExitForMPI();
381 #define LrtsExit MachineExitForMPI
382 /* ### End of Machine-running Related Functions ### */
383
384 /* ### Beginning of Idle-state Related Functions ### */
385 void CmiNotifyIdleForMPI(void);
386 /* ### End of Idle-state Related Functions ### */
387
388 static void MachinePostNonLocalForMPI();
389 #define LrtsPostNonLocal MachinePostNonLocalForMPI
390
391 /* =====End of Declarations of Machine Specific Functions===== */
392
393 /**
394  *  Macros that overwrites the common codes, such as
395  *  CMK_SMP_NO_COMMTHD, NETWORK_PROGRESS_PERIOD_DEFAULT,
396  *  USE_COMMON_SYNC_P2P, CMK_HAS_SIZE_IN_MSGHDR,
397  *  CMK_OFFLOAD_BCAST_PROCESS etc.
398  */
399 #define CMK_HAS_SIZE_IN_MSGHDR 0
400 #include "machine-lrts.h"
401 #include "machine-common-core.c"
402
403 /* The machine specific msg-sending function */
404
405 #if CMK_SMP
406 static void EnqueueMsg(void *m, int size, int node, int mode) {
407     /*SMSG_LIST *msg_tmp = (SMSG_LIST *) CmiAlloc(sizeof(SMSG_LIST));*/
408     SMSG_LIST *msg_tmp = (SMSG_LIST *) malloc(sizeof(SMSG_LIST));
409     MACHSTATE1(3,"EnqueueMsg to node %d {{ ", node);
410     msg_tmp->msg = m;
411     msg_tmp->size = size;
412     msg_tmp->destpe = node;
413     msg_tmp->next = 0;
414     msg_tmp->mode = mode;
415
416 #if MULTI_SENDQUEUE
417     PCQueuePush(procState[CmiMyRank()].sendMsgBuf,(char *)msg_tmp);
418 #else
419     /*CmiLock(sendMsgBufLock);*/
420     PCQueuePush(sendMsgBuf,(char *)msg_tmp);
421     /*CmiUnlock(sendMsgBufLock);*/
422 #endif
423
424     MACHSTATE3(3,"}} EnqueueMsg to %d finish with queue %p len: %d", node, sendMsgBuf, PCQueueLength(sendMsgBuf));
425 }
426 #endif
427
428 /* The function that calls MPI_Isend so that both non-SMP and SMP could use */
429 static CmiCommHandle MPISendOneMsg(SMSG_LIST *smsg) {
430     int node = smsg->destpe;
431     int size = smsg->size;
432     char *msg = smsg->msg;
433     int mode = smsg->mode;
434     int dstrank;
435
436     MACHSTATE2(3,"MPI_send to node %d rank: %d{", node, CMI_DEST_RANK(msg));
437 #if CMK_ERROR_CHECKING
438     CMI_MAGIC(msg) = CHARM_MAGIC_NUMBER;
439     CMI_SET_CHECKSUM(msg, size);
440 #endif
441
442 #if MPI_POST_RECV
443     if (size>=MPI_POST_RECV_LOWERSIZE && size < MPI_POST_RECV_UPPERSIZE) {
444 #if MPI_DYNAMIC_POST_RECV
445         int sendTagOffset = (size-MPI_POST_RECV_LOWERSIZE)/MPI_POST_RECV_INC+1;
446         START_TRACE_SENDCOMM(msg);
447         if (MPI_SUCCESS != MPI_Isend((void *)msg,size,MPI_BYTE,node,POST_RECV_TAG+sendTagOffset,MPI_COMM_WORLD,&(smsg->req)))
448             CmiAbort("MPISendOneMsg: MPI_Isend failed!\n");
449         END_TRACE_SENDCOMM(msg);
450 #else
451         START_TRACE_SENDCOMM(msg);
452         if (MPI_SUCCESS != MPI_Isend((void *)msg,size,MPI_BYTE,node,POST_RECV_TAG,MPI_COMM_WORLD,&(smsg->req)))
453             CmiAbort("MPISendOneMsg: MPI_Isend failed!\n");
454         END_TRACE_SENDCOMM(msg);
455 #endif
456     } else {
457         START_TRACE_SENDCOMM(msg);
458             if (MPI_SUCCESS != MPI_Isend((void *)msg,size,MPI_BYTE,node,TAG,MPI_COMM_WORLD,&(smsg->req)))
459             CmiAbort("MPISendOneMsg: MPI_Isend failed!\n");
460         END_TRACE_SENDCOMM(msg);
461     }
462 #else
463 /* branch not using MPI_POST_RECV */
464
465 #if CMK_MEM_CHECKPOINT
466         dstrank = petorank[node];
467 #else
468         dstrank=node;
469 #endif
470     START_TRACE_SENDCOMM(msg)
471     if (MPI_SUCCESS != MPI_Isend((void *)msg,size,MPI_BYTE,dstrank,TAG,MPI_COMM_WORLD,&(smsg->req)))
472         CmiAbort("MPISendOneMsg: MPI_Isend failed!\n");
473     END_TRACE_SENDCOMM(msg)
474 #endif /* end of #if MPI_POST_RECV */
475
476     MACHSTATE(3,"}MPI_Isend end");
477     CpvAccess(MsgQueueLen)++;
478     if (CpvAccess(sent_msgs)==0)
479         CpvAccess(sent_msgs) = smsg;
480     else
481         CpvAccess(end_sent)->next = smsg;
482     CpvAccess(end_sent) = smsg;
483
484 #if !CMI_DYNAMIC_EXERT_CAP && !CMI_EXERT_SEND_CAP
485     if (mode == P2P_SYNC || mode == P2P_ASYNC)
486     {
487     while (CpvAccess(MsgQueueLen) > request_max) {
488         CmiReleaseSentMessages();
489         PumpMsgs();
490     }
491     }
492 #endif
493
494     return (CmiCommHandle) &(smsg->req);
495 }
496
497 static CmiCommHandle MachineSpecificSendForMPI(int destNode, int size, char *msg, int mode) {
498     /* Ignoring the mode for MPI layer */
499
500     CmiState cs = CmiGetState();
501     SMSG_LIST *msg_tmp;
502     int  rank;
503
504     CmiAssert(destNode != CmiMyNode());
505 #if CMK_SMP
506     if (Cmi_smp_mode_setting == COMM_THREAD_SEND_RECV) {
507       EnqueueMsg(msg, size, destNode, mode);
508       return 0;
509     }
510 #endif
511     /* non smp */
512     /*msg_tmp = (SMSG_LIST *) CmiAlloc(sizeof(SMSG_LIST));*/
513     msg_tmp = (SMSG_LIST *) malloc(sizeof(SMSG_LIST));
514     msg_tmp->msg = msg;
515     msg_tmp->destpe = destNode;
516     msg_tmp->size = size;
517     msg_tmp->next = 0;
518     msg_tmp->mode = mode;
519     return MPISendOneMsg(msg_tmp);
520 }
521
522 static size_t CmiAllAsyncMsgsSent(void) {
523     SMSG_LIST *msg_tmp = CpvAccess(sent_msgs);
524     MPI_Status sts;
525     int done;
526
527     while (msg_tmp!=0) {
528         done = 0;
529         if (MPI_SUCCESS != MPI_Test(&(msg_tmp->req), &done, &sts))
530             CmiAbort("CmiAllAsyncMsgsSent: MPI_Test failed!\n");
531         if (!done)
532             return 0;
533         msg_tmp = msg_tmp->next;
534         /*    MsgQueueLen--; ????? */
535     }
536     return 1;
537 }
538
539 int CmiAsyncMsgSent(CmiCommHandle c) {
540
541     SMSG_LIST *msg_tmp = CpvAccess(sent_msgs);
542     int done;
543     MPI_Status sts;
544
545     while ((msg_tmp) && ((CmiCommHandle)&(msg_tmp->req) != c))
546         msg_tmp = msg_tmp->next;
547     if (msg_tmp) {
548         done = 0;
549         if (MPI_SUCCESS != MPI_Test(&(msg_tmp->req), &done, &sts))
550             CmiAbort("CmiAsyncMsgSent: MPI_Test failed!\n");
551         return ((done)?1:0);
552     } else {
553         return 1;
554     }
555 }
556
557 void CmiReleaseCommHandle(CmiCommHandle c) {
558     return;
559 }
560
561 /* ######Beginning of functions related with communication progress ###### */
562 static void CmiReleaseSentMessages(void) {
563     SMSG_LIST *msg_tmp=CpvAccess(sent_msgs);
564     SMSG_LIST *prev=0;
565     SMSG_LIST *temp;
566     int done;
567     MPI_Status sts;
568
569 #if CMK_BLUEGENEL
570     MPID_Progress_test();
571 #endif
572
573     MACHSTATE1(2,"CmiReleaseSentMessages begin on %d {", CmiMyPe());
574     while (msg_tmp!=0) {
575         done =0;
576 #if CMK_SMP_TRACE_COMMTHREAD || CMK_TRACE_COMMOVERHEAD
577         double startT = CmiWallTimer();
578 #endif
579         if (MPI_Test(&(msg_tmp->req), &done, &sts) != MPI_SUCCESS)
580             CmiAbort("CmiReleaseSentMessages: MPI_Test failed!\n");
581         if (done) {
582             MACHSTATE2(3,"CmiReleaseSentMessages release one %d to %d", CmiMyPe(), msg_tmp->destpe);
583             CpvAccess(MsgQueueLen)--;
584             /* Release the message */
585             temp = msg_tmp->next;
586             if (prev==0) /* first message */
587                 CpvAccess(sent_msgs) = temp;
588             else
589                 prev->next = temp;
590             CmiFree(msg_tmp->msg);
591             /* CmiFree(msg_tmp); */
592             free(msg_tmp);
593             msg_tmp = temp;
594         } else {
595             prev = msg_tmp;
596             msg_tmp = msg_tmp->next;
597         }
598 #if CMK_SMP_TRACE_COMMTHREAD || CMK_TRACE_COMMOVERHEAD
599         {
600             double endT = CmiWallTimer();
601             /* only record the event if it takes more than 1ms */
602             if (endT-startT>=0.001) traceUserSuppliedBracketedNote("MPI_Test: release a msg", 60, startT, endT);
603         }
604 #endif
605     }
606     CpvAccess(end_sent) = prev;
607     MACHSTATE(2,"} CmiReleaseSentMessages end");
608 }
609
610 static int PumpMsgs(void) {
611     int nbytes, flg, res;
612     char *msg;
613     MPI_Status sts;
614     int recd=0;
615
616 #if CMI_EXERT_RECV_CAP || CMI_DYNAMIC_EXERT_CAP
617     int recvCnt=0;
618 #endif
619
620 #if CMK_BLUEGENEL
621     MPID_Progress_test();
622 #endif
623
624     MACHSTATE(2,"PumpMsgs begin {");
625
626 #if CMI_DYNAMIC_EXERT_CAP
627     dynamicRecvCap = CMI_DYNAMIC_MAXCAPSIZE;
628 #endif
629
630     while (1) {
631         int doSyncRecv = 1;
632 #if CMI_EXERT_RECV_CAP
633         if (recvCnt==RECV_CAP) break;
634 #elif CMI_DYNAMIC_EXERT_CAP
635         if (recvCnt >= dynamicRecvCap) break;
636 #endif
637
638         START_TRACE_RECVCOMM(NULL);
639
640         /* First check posted recvs then do  probe unmatched outstanding messages */
641 #if MPI_POST_RECV
642         MPIPostRecvList *postedOne = NULL;
643         int completed_index = -1;
644         flg = 0;
645 #if MPI_DYNAMIC_POST_RECV
646         MPIPostRecvList *oldPostRecvPtr = CpvAccess(curPostRecvPtr);
647         if (oldPostRecvPtr) {
648             /* post recv buf inited */
649             do {
650                 /* round-robin iteration over the list */
651                 MPIPostRecvList *cur = CpvAccess(curPostRecvPtr);
652                 if (MPI_SUCCESS != MPI_Testany(cur->bufCnt, cur->postedRecvReqs, &completed_index, &flg, &sts))
653                     CmiAbort("PumpMsgs: MPI_Testany failed!\n");
654
655                 if (flg) {
656                     postedOne = cur;
657                     break;
658                 }
659                 CpvAccess(curPostRecvPtr) = CpvAccess(curPostRecvPtr)->next;
660             } while (CpvAccess(curPostRecvPtr) != oldPostRecvPtr);
661         }
662 #else
663         MPIPostRecvList *cur = CpvAccess(curPostRecvPtr);
664         if (MPI_SUCCESS != MPI_Testany(cur->bufCnt, cur->postedRecvReqs, &completed_index, &flg, &sts))
665             CmiAbort("PumpMsgs: MPI_Testany failed!\n");
666 #endif
667         CONDITIONAL_TRACE_USER_EVENT(60); /* MPI_Test related user event */
668         if (flg) {
669             if (MPI_SUCCESS != MPI_Get_count(&sts, MPI_BYTE, &nbytes))
670                 CmiAbort("PumpMsgs: MPI_Get_count failed!\n");
671
672             recd = 1;
673 #if !MPI_DYNAMIC_POST_RECV
674             postedOne = CpvAccess(curPostRecvPtr);
675 #endif
676             msg = (postedOne->postedRecvBufs)[completed_index];
677             (postedOne->postedRecvBufs)[completed_index] = NULL;
678
679             CpvAccess(Cmi_posted_recv_total)++;
680         } else {
681             START_EVENT();
682             res = MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &flg, &sts);
683             if (res != MPI_SUCCESS)
684                 CmiAbort("MPI_Iprobe failed\n");
685             if (!flg) break;
686             
687             CONDITIONAL_TRACE_USER_EVENT(70); /* MPI_Iprobe related user event */
688             recd = 1;
689             MPI_Get_count(&sts, MPI_BYTE, &nbytes);
690             msg = (char *) CmiAlloc(nbytes);
691
692 #if USE_ASYNC_RECV_FUNC
693             if(nbytes >= IRECV_MSG_THRESHOLD) doSyncRecv = 0;
694 #endif            
695             if(doSyncRecv){
696                 START_EVENT();
697                 if (MPI_SUCCESS != MPI_Recv(msg,nbytes,MPI_BYTE,sts.MPI_SOURCE,sts.MPI_TAG, MPI_COMM_WORLD,&sts))
698                     CmiAbort("PumpMsgs: MPI_Recv failed!\n");                
699             }
700 #if USE_ASYNC_RECV_FUNC        
701             else {
702                 START_EVENT();
703                 IRecvList one = irecvListEntryAllocate();
704                 if(MPI_SUCCESS != MPI_Irecv(msg, nbytes, MPI_BYTE, sts.MPI_SOURCE, sts.MPI_TAG, MPI_COMM_WORLD, &(one->req));
705                     CmiAbort("PumpMsgs: MPI_Irecv failed!\n");
706                 one->msg = msg;
707                 one->size = nbytes;
708                 one->next = NULL;
709                 waitIrecvListTail->next = one;
710                 CONDITIONAL_TRACE_USER_EVENT(50); /* MPI_Irecv related user events */
711             }
712 #endif
713             CpvAccess(Cmi_unposted_recv_total)++;
714         }
715 #else
716         /* Original version */
717         START_EVENT();
718         res = MPI_Iprobe(MPI_ANY_SOURCE, TAG, MPI_COMM_WORLD, &flg, &sts);
719         if (res != MPI_SUCCESS)
720             CmiAbort("MPI_Iprobe failed\n");
721
722         if (!flg) break;
723         CONDITIONAL_TRACE_USER_EVENT(70); /* MPI_Iprobe related user event */
724         
725         recd = 1;
726         MPI_Get_count(&sts, MPI_BYTE, &nbytes);
727         msg = (char *) CmiAlloc(nbytes);
728
729 #if USE_ASYNC_RECV_FUNC
730         if(nbytes >= IRECV_MSG_THRESHOLD) doSyncRecv = 0;
731 #endif        
732         if(doSyncRecv){
733             START_EVENT();
734             if (MPI_SUCCESS != MPI_Recv(msg,nbytes,MPI_BYTE,sts.MPI_SOURCE,sts.MPI_TAG, MPI_COMM_WORLD,&sts))
735                 CmiAbort("PumpMsgs: MPI_Recv failed!\n");            
736         }
737 #if USE_ASYNC_RECV_FUNC        
738         else {
739             START_EVENT();
740             IRecvList one = irecvListEntryAllocate();
741             if(MPI_SUCCESS != MPI_Irecv(msg, nbytes, MPI_BYTE, sts.MPI_SOURCE, sts.MPI_TAG, MPI_COMM_WORLD, &(one->req)))
742                 CmiAbort("PumpMsgs: MPI_Irecv failed!\n");
743             one->msg = msg;
744             one->size = nbytes;
745             one->next = NULL;
746             waitIrecvListTail->next = one;
747             waitIrecvListTail = one;
748             /*printf("PE[%d]: MPI_Irecv msg=%p, size=%d, entry=%p\n", CmiMyPe(), msg, nbytes, one);*/
749             CONDITIONAL_TRACE_USER_EVENT(50); /* MPI_Irecv related user events */
750         }
751 #endif
752
753 #endif /*end of not MPI_POST_RECV */
754
755         MACHSTATE2(3,"PumpMsgs recv one from node:%d to rank:%d", sts.MPI_SOURCE, CMI_DEST_RANK(msg));
756         CMI_CHECK_CHECKSUM(msg, nbytes);
757 #if CMK_ERROR_CHECKING
758         if (CMI_MAGIC(msg) != CHARM_MAGIC_NUMBER) { /* received a non-charm msg */
759             CmiPrintf("Charm++ Abort: Non Charm++ Message Received of size %d. \n", nbytes);
760             CmiFree(msg);
761             CmiAbort("Abort!\n");
762             continue;
763         }
764 #endif
765
766         if(doSyncRecv){
767             END_TRACE_RECVCOMM(msg);
768             handleOneRecvedMsg(nbytes, msg);
769         }
770         
771 #if CAPTURE_MSG_HISTOGRAM || MPI_DYNAMIC_POST_RECV
772         recordMsgHistogramInfo(nbytes);
773 #endif
774
775 #if  MPI_POST_RECV
776 #if MPI_DYNAMIC_POST_RECV
777         if (postedOne) {
778             //printf("[%d]: get one posted recv\n", CmiMyPe());
779             /* Get the upper size of this buffer */
780             int postRecvBufSize = postedOne->msgSizeIdx*MPI_POST_RECV_INC + MPI_POST_RECV_LOWERSIZE - 1;
781             int postRecvTag = POST_RECV_TAG + postedOne->msgSizeIdx;
782             /* Has to re-allocate the buffer for the message */
783             (postedOne->postedRecvBufs)[completed_index] = (char *)CmiAlloc(postRecvBufSize);
784
785             /* and repost the recv */
786             START_EVENT();
787
788             if (MPI_SUCCESS != MPI_Irecv((postedOne->postedRecvBufs)[completed_index] ,
789                                          postRecvBufSize,
790                                          MPI_BYTE,
791                                          MPI_ANY_SOURCE,
792                                          postRecvTag,
793                                          MPI_COMM_WORLD,
794                                          &((postedOne->postedRecvReqs)[completed_index])  ))
795                 CmiAbort("PumpMsgs: MPI_Irecv failed!\n");
796             CONDITIONAL_TRACE_USER_EVENT(50); /* MPI_Irecv related user events */
797         }
798 #else
799         if (postedOne) {
800             /* Has to re-allocate the buffer for the message */
801             (postedOne->postedRecvBufs)[completed_index] = (char *)CmiAlloc(MPI_POST_RECV_SIZE);
802
803             /* and repost the recv */
804             START_EVENT();
805             if (MPI_SUCCESS != MPI_Irecv((postedOne->postedRecvBufs)[completed_index] ,
806                                          MPI_POST_RECV_SIZE,
807                                          MPI_BYTE,
808                                          MPI_ANY_SOURCE,
809                                          POST_RECV_TAG,
810                                          MPI_COMM_WORLD,
811                                          &((postedOne->postedRecvReqs)[completed_index])  ))
812                 CmiAbort("PumpMsgs: MPI_Irecv failed!\n");
813             CONDITIONAL_TRACE_USER_EVENT(50); /* MPI_Irecv related user events */
814         }
815 #endif /* not MPI_DYNAMIC_POST_RECV */
816 #endif
817
818 #if CMI_EXERT_RECV_CAP
819         recvCnt++;
820 #elif CMI_DYNAMIC_EXERT_CAP
821         recvCnt++;
822 #if CMK_SMP
823         /* check sendMsgBuf to get the  number of messages that have not been sent
824              * which is only available in SMP mode
825          * MsgQueueLen indicates the number of messages that have not been released
826              * by MPI
827              */
828         if (PCQueueLength(sendMsgBuf) > CMI_DYNAMIC_OUTGOING_THRESHOLD
829                 || CpvAccess(MsgQueueLen) > CMI_DYNAMIC_OUTGOING_THRESHOLD) {
830             dynamicRecvCap = CMI_DYNAMIC_RECV_CAPSIZE;
831         }
832 #else
833         /* MsgQueueLen indicates the number of messages that have not been released
834              * by MPI
835              */
836         if (CpvAccess(MsgQueueLen) > CMI_DYNAMIC_OUTGOING_THRESHOLD) {
837             dynamicRecvCap = CMI_DYNAMIC_RECV_CAPSIZE;
838         }
839 #endif
840
841 #endif
842
843     }
844
845 #if USE_ASYNC_RECV_FUNC
846 /* Another loop to check the irecved msgs list */
847 {
848     IRecvList irecvEnt;
849     int irecvDone = 0;
850     MPI_Status sts;
851     while(waitIrecvListHead->next) {
852         IRecvList irecvEnt = waitIrecvListHead->next;
853
854         START_EVENT();
855                 
856         /*printf("PE[%d]: check irecv entry=%p\n", CmiMyPe(), irecvEnt);*/
857         if(MPI_SUCCESS != MPI_Test(&(irecvEnt->req), &irecvDone, &sts))
858             CmiAbort("PumpMsgs: MPI_Test failed!\n");
859         if(!irecvDone) break; /* in-order recv */
860
861         END_TRACE_RECVCOMM((irecvEnt->msg));
862         /*printf("PE[%d]: irecv entry=%p finished with size=%d, msg=%p\n", CmiMyPe(), irecvEnt, irecvEnt->size, irecvEnt->msg);*/
863         
864         handleOneRecvedMsg(irecvEnt->size, irecvEnt->msg);
865         waitIrecvListHead->next = irecvEnt->next;
866         irecvListEntryFree(irecvEnt);
867         recd = 1;        
868     }
869     if(waitIrecvListHead->next == NULL)
870         waitIrecvListTail = waitIrecvListHead;
871 }
872 #endif
873
874
875     MACHSTATE(2,"} PumpMsgs end ");
876     return recd;
877 }
878
879 /* blocking version */
880 static void PumpMsgsBlocking(void) {
881     static int maxbytes = 20000000;
882     static char *buf = NULL;
883     int nbytes, flg;
884     MPI_Status sts;
885     char *msg;
886     int recd=0;
887
888     if (!PCQueueEmpty(CmiGetState()->recv)) return;
889     if (!CdsFifo_Empty(CpvAccess(CmiLocalQueue))) return;
890     if (!CqsEmpty(CpvAccess(CsdSchedQueue))) return;
891     if (CpvAccess(sent_msgs))  return;
892
893 #if 0
894     CmiPrintf("[%d] PumpMsgsBlocking. \n", CmiMyPe());
895 #endif
896
897     if (buf == NULL) {
898         buf = (char *) CmiAlloc(maxbytes);
899         _MEMCHECK(buf);
900     }
901
902
903 #if MPI_POST_RECV
904 #warning "Using MPI posted receives and PumpMsgsBlocking() will break"
905     CmiAbort("Unsupported use of PumpMsgsBlocking. This call should be extended to check posted recvs, cancel them all, and then wait on any incoming message, and then re-post the recvs");
906 #endif
907
908     START_TRACE_RECVCOMM(NULL);
909     if (MPI_SUCCESS != MPI_Recv(buf,maxbytes,MPI_BYTE,MPI_ANY_SOURCE,TAG, MPI_COMM_WORLD,&sts))
910         CmiAbort("PumpMsgs: PMP_Recv failed!\n");    
911
912     MPI_Get_count(&sts, MPI_BYTE, &nbytes);
913     msg = (char *) CmiAlloc(nbytes);
914     memcpy(msg, buf, nbytes);
915     END_TRACE_RECVCOMM(msg);
916
917 #if CMK_SMP_TRACE_COMMTHREAD && CMI_MPI_TRACE_MOREDETAILED
918     char tmp[32];
919     sprintf(tmp, "To proc %d", CmiNodeFirst(CmiMyNode())+CMI_DEST_RANK(msg));
920     traceUserSuppliedBracketedNote(tmp, 30, CpvAccess(projTraceStart), CmiWallTimer());
921 #endif
922
923     handleOneRecvedMsg(nbytes, msg);
924 }
925
926
927 #if CMK_SMP
928
929 /* called by communication thread in SMP */
930 static int SendMsgBuf() {
931     SMSG_LIST *msg_tmp;
932     char *msg;
933     int node, rank, size;
934     int i;
935     int sent = 0;
936
937 #if CMI_EXERT_SEND_CAP || CMI_DYNAMIC_EXERT_CAP
938     int sentCnt = 0;
939 #endif
940
941 #if CMI_DYNAMIC_EXERT_CAP
942     dynamicSendCap = CMI_DYNAMIC_MAXCAPSIZE;
943 #endif
944
945     MACHSTATE(2,"SendMsgBuf begin {");
946 #if MULTI_SENDQUEUE
947     for (i=0; i<_Cmi_mynodesize+1; i++) { /* subtle: including comm thread */
948         if (!PCQueueEmpty(procState[i].sendMsgBuf)) {
949             msg_tmp = (SMSG_LIST *)PCQueuePop(procState[i].sendMsgBuf);
950 #else
951     /* single message sending queue */
952     /* CmiLock(sendMsgBufLock); */
953     msg_tmp = (SMSG_LIST *)PCQueuePop(sendMsgBuf);
954     /* CmiUnlock(sendMsgBufLock); */
955     while (NULL != msg_tmp) {
956 #endif
957             MPISendOneMsg(msg_tmp);
958             sent=1;
959
960 #if CMI_EXERT_SEND_CAP
961             if (++sentCnt == SEND_CAP) break;
962 #elif CMI_DYNAMIC_EXERT_CAP
963             if (++sentCnt >= dynamicSendCap) break;
964             if (CpvAccess(MsgQueueLen) > CMI_DYNAMIC_OUTGOING_THRESHOLD)
965                 dynamicSendCap = CMI_DYNAMIC_SEND_CAPSIZE;
966 #endif
967
968 #if ! MULTI_SENDQUEUE
969             /* CmiLock(sendMsgBufLock); */
970             msg_tmp = (SMSG_LIST *)PCQueuePop(sendMsgBuf);
971             /* CmiUnlock(sendMsgBufLock); */
972 #endif
973         }
974 #if MULTI_SENDQUEUE
975     }
976 #endif
977     MACHSTATE(2,"}SendMsgBuf end ");
978     return sent;
979 }
980
981 static int MsgQueueEmpty() {
982     int i;
983 #if MULTI_SENDQUEUE
984     for (i=0; i<_Cmi_mynodesize; i++)
985         if (!PCQueueEmpty(procState[i].sendMsgBuf)) return 0;
986 #else
987     return PCQueueEmpty(sendMsgBuf);
988 #endif
989     return 1;
990 }
991
992 /* test if all processors recv queues are empty */
993 static int RecvQueueEmpty() {
994     int i;
995     for (i=0; i<_Cmi_mynodesize; i++) {
996         CmiState cs=CmiGetStateN(i);
997         if (!PCQueueEmpty(cs->recv)) return 0;
998     }
999     return 1;
1000 }
1001
1002
1003 #define REPORT_COMM_METRICS 0
1004 #if REPORT_COMM_METRICS
1005 static double pumptime = 0.0;
1006 static double releasetime = 0.0;
1007 static double sendtime = 0.0;
1008 #endif
1009
1010 #endif //end of CMK_SMP
1011
1012 static void AdvanceCommunicationForMPI(int whenidle) {
1013 #if REPORT_COMM_METRICS
1014     double t1, t2, t3, t4;
1015     t1 = CmiWallTimer();
1016 #endif
1017
1018 #if CMK_SMP
1019     PumpMsgs();
1020
1021 #if REPORT_COMM_METRICS
1022     t2 = CmiWallTimer();
1023 #endif
1024
1025     CmiReleaseSentMessages();
1026 #if REPORT_COMM_METRICS
1027     t3 = CmiWallTimer();
1028 #endif
1029
1030     SendMsgBuf();
1031
1032 #if REPORT_COMM_METRICS
1033     t4 = CmiWallTimer();
1034     pumptime += (t2-t1);
1035     releasetime += (t3-t2);
1036     sendtime += (t4-t3);
1037 #endif
1038
1039 #else /* non-SMP case */
1040     CmiReleaseSentMessages();
1041
1042 #if REPORT_COMM_METRICS
1043     t2 = CmiWallTimer();
1044 #endif
1045     PumpMsgs();
1046
1047 #if REPORT_COMM_METRICS
1048     t3 = CmiWallTimer();
1049     pumptime += (t3-t2);
1050     releasetime += (t2-t1);
1051 #endif
1052
1053 #endif /* end of #if CMK_SMP */
1054 }
1055 /* ######End of functions related with communication progress ###### */
1056
1057 static void MachinePostNonLocalForMPI() {
1058 #if !CMK_SMP
1059     if (no_outstanding_sends) {
1060         while (CpvAccess(MsgQueueLen)>0) {
1061             AdvanceCommunicationForMPI(0);
1062         }
1063     }
1064
1065     /* FIXME: I don't think the following codes are needed because
1066      * it repeats the same job of the next call of CmiGetNonLocal
1067      */
1068 #if 0
1069     if (!msg) {
1070         CmiReleaseSentMessages();
1071         if (PumpMsgs())
1072             return  PCQueuePop(cs->recv);
1073         else
1074             return 0;
1075     }
1076 #endif
1077 #else
1078   if (Cmi_smp_mode_setting == COMM_THREAD_ONLY_RECV) {
1079         CmiReleaseSentMessages();       
1080         /* ??? SendMsgBuf is a not a thread-safe function. If it is put
1081          * here and this function will be called in CmiNotifyStillIdle,
1082          * then a data-race problem occurs */
1083         /*SendMsgBuf();*/
1084   }
1085 #endif
1086 }
1087
1088 /* Idle-state related functions: called in non-smp mode */
1089 void CmiNotifyIdleForMPI(void) {
1090     CmiReleaseSentMessages();
1091     if (!PumpMsgs() && idleblock) PumpMsgsBlocking();
1092 }
1093
1094 /* Network progress function is used to poll the network when for
1095    messages. This flushes receive buffers on some  implementations*/
1096 #if CMK_MACHINE_PROGRESS_DEFINED
1097 void CmiMachineProgressImpl() {
1098 #if !CMK_SMP
1099     PumpMsgs();
1100 #if CMK_IMMEDIATE_MSG
1101     CmiHandleImmediate();
1102 #endif
1103 #else
1104     /*Not implemented yet. Communication server does not seem to be
1105       thread safe, so only communication thread call it */
1106     if (CmiMyRank() == CmiMyNodeSize())
1107         CommunicationServerThread(0);
1108 #endif
1109 }
1110 #endif
1111
1112 /* ######Beginning of functions related with exiting programs###### */
1113 void DrainResourcesForMPI() {
1114 #if !CMK_SMP
1115     while (!CmiAllAsyncMsgsSent()) {
1116         PumpMsgs();
1117         CmiReleaseSentMessages();
1118     }
1119 #else
1120     if(Cmi_smp_mode_setting == COMM_THREAD_SEND_RECV){
1121         while (!MsgQueueEmpty() || !CmiAllAsyncMsgsSent()) {
1122             CmiReleaseSentMessages();
1123             SendMsgBuf();
1124             PumpMsgs();
1125         }
1126     }else if(Cmi_smp_mode_setting == COMM_THREAD_ONLY_RECV) {
1127         while(!CmiAllAsyncMsgsSent()) {
1128             CmiReleaseSentMessages();
1129         }
1130     }
1131 #endif
1132 #if CMK_MEM_CHECKPOINT
1133     if (CmiMyPe() == 0) mpi_end_spare();
1134 #endif
1135     MACHSTATE(2, "Machine exit barrier begin {");
1136     START_EVENT();
1137     if (MPI_SUCCESS != MPI_Barrier(MPI_COMM_WORLD))
1138         CmiAbort("DrainResourcesForMPI: MPI_Barrier failed!\n");
1139     END_EVENT(10);
1140     MACHSTATE(2, "} Machine exit barrier end");
1141 }
1142
1143 void MachineExitForMPI() {
1144     int i;
1145 #if (CMK_DEBUG_MODE || CMK_WEB_MODE || NODE_0_IS_CONVHOST)
1146     int doPrint = 0;
1147     if (CmiMyNode()==0) doPrint = 1;
1148
1149     if (doPrint /*|| CmiMyNode()%11==0 */) {
1150 #if MPI_POST_RECV
1151         CmiPrintf("node[%d]: %llu posted receives,  %llu unposted receives\n", CmiMyNode(), CpvAccess(Cmi_posted_recv_total), CpvAccess(Cmi_unposted_recv_total));
1152 #endif
1153     }
1154 #endif
1155
1156 #if MPI_POST_RECV
1157     {
1158         MPIPostRecvList *ptr = CpvAccess(postRecvListHdr);
1159         if (ptr) {
1160             do {
1161                 for (i=0; i<ptr->bufCnt; i++) MPI_Cancel(ptr->postedRecvReqs+i);
1162                 ptr = ptr->next;
1163             } while (ptr!=CpvAccess(postRecvListHdr));
1164         }
1165     }
1166 #endif
1167
1168 #if REPORT_COMM_METRICS
1169 #if CMK_SMP
1170     CmiPrintf("Report comm metrics for node %d[%d-%d]: pumptime: %f, releasetime: %f, senttime: %f\n",
1171               CmiMyNode(), CmiNodeFirst(CmiMyNode()), CmiNodeFirst(CmiMyNode())+CmiMyNodeSize()-1,
1172               pumptime, releasetime, sendtime);
1173 #else
1174     CmiPrintf("Report comm metrics for proc %d: pumptime: %f, releasetime: %f, senttime: %f\n",
1175               CmiMyPe(), pumptime, releasetime, sendtime);
1176 #endif
1177 #endif
1178
1179 #if ! CMK_AUTOBUILD
1180     signal(SIGINT, signal_int);
1181     MPI_Finalize();
1182 #endif
1183     exit(0);
1184 }
1185
1186 static int machine_exit_idx;
1187 static void machine_exit(char *m) {
1188     EmergencyExit();
1189     /*printf("--> %d: machine_exit\n",CmiMyPe());*/
1190     fflush(stdout);
1191     CmiNodeBarrier();
1192     if (CmiMyRank() == 0) {
1193         MPI_Barrier(MPI_COMM_WORLD);
1194         /*printf("==> %d: passed barrier\n",CmiMyPe());*/
1195         MPI_Abort(MPI_COMM_WORLD, 1);
1196     } else {
1197         while (1) CmiYield();
1198     }
1199 }
1200
1201 static void KillOnAllSigs(int sigNo) {
1202     static int already_in_signal_handler = 0;
1203     char *m;
1204     if (already_in_signal_handler) return;   /* MPI_Abort(MPI_COMM_WORLD,1); */
1205     already_in_signal_handler = 1;
1206 #if CMK_CCS_AVAILABLE
1207     if (CpvAccess(cmiArgDebugFlag)) {
1208         CpdNotify(CPD_SIGNAL, sigNo);
1209         CpdFreeze();
1210     }
1211 #endif
1212     CmiError("------------- Processor %d Exiting: Caught Signal ------------\n"
1213              "Signal: %d\n",CmiMyPe(),sigNo);
1214     CmiPrintStackTrace(1);
1215
1216     m = CmiAlloc(CmiMsgHeaderSizeBytes);
1217     CmiSetHandler(m, machine_exit_idx);
1218     CmiSyncBroadcastAndFree(CmiMsgHeaderSizeBytes, m);
1219     machine_exit(m);
1220 }
1221 /* ######End of functions related with exiting programs###### */
1222
1223
1224 /* ######Beginning of functions related with starting programs###### */
1225 static void registerMPITraceEvents() {
1226 #if CMI_MPI_TRACE_USEREVENTS && CMK_TRACE_ENABLED && !CMK_TRACE_IN_CHARM
1227     traceRegisterUserEvent("MPI_Barrier", 10);
1228     traceRegisterUserEvent("MPI_Send", 20);
1229     traceRegisterUserEvent("MPI_Recv", 30);
1230     traceRegisterUserEvent("MPI_Isend", 40);
1231     traceRegisterUserEvent("MPI_Irecv", 50);
1232     traceRegisterUserEvent("MPI_Test[any]", 60);
1233     traceRegisterUserEvent("MPI_Iprobe", 70);
1234 #endif
1235 }
1236
1237 #if MACHINE_DEBUG_LOG
1238 FILE *debugLog = NULL;
1239 #endif
1240
1241 static char *thread_level_tostring(int thread_level) {
1242 #if CMK_MPI_INIT_THREAD
1243     switch (thread_level) {
1244     case MPI_THREAD_SINGLE:
1245         return "MPI_THREAD_SINGLE";
1246     case MPI_THREAD_FUNNELED:
1247         return "MPI_THREAD_FUNNELED";
1248     case MPI_THREAD_SERIALIZED:
1249         return "MPI_THREAD_SERIALIZED";
1250     case MPI_THREAD_MULTIPLE :
1251         return "MPI_THREAD_MULTIPLE";
1252     default: {
1253         char *str = (char*)malloc(5);
1254         sprintf(str,"%d", thread_level);
1255         return str;
1256     }
1257     }
1258     return  "unknown";
1259 #else
1260     char *str = (char*)malloc(5);
1261     sprintf(str,"%d", thread_level);
1262     return str;
1263 #endif
1264 }
1265
1266 /**
1267  *  Obtain the number of nodes, my node id, and consuming machine layer
1268  *  specific arguments
1269  */
1270 static void MachineInitForMPI(int *argc, char ***argv, int *numNodes, int *myNodeID) {
1271     int n,i;
1272     int ver, subver;
1273     int provided;
1274     int thread_level;
1275     int myNID;
1276     int largc=*argc;
1277     char** largv=*argv;
1278
1279 #if MACHINE_DEBUG
1280     debugLog=NULL;
1281 #endif
1282 #if CMK_USE_HP_MAIN_FIX
1283 #if FOR_CPLUS
1284     _main(largc,largv);
1285 #endif
1286 #endif
1287
1288     if (CmiGetArgFlag(largv, "+comm_thread_only_recv")) {
1289 #if CMK_SMP
1290       Cmi_smp_mode_setting = COMM_THREAD_ONLY_RECV;
1291 #else
1292       CmiAbort("+comm_thread_only_recv option can only be used with SMP version of Charm++");
1293 #endif
1294     }
1295
1296 #if CMK_MPI_INIT_THREAD
1297 #if CMK_SMP
1298     if (Cmi_smp_mode_setting == COMM_THREAD_SEND_RECV)
1299       thread_level = MPI_THREAD_FUNNELED;
1300     else
1301       thread_level = MPI_THREAD_MULTIPLE;
1302 #else
1303     thread_level = MPI_THREAD_SINGLE;
1304 #endif
1305     MPI_Init_thread(argc, argv, thread_level, &provided);
1306     _thread_provided = provided;
1307 #else
1308     MPI_Init(argc, argv);
1309     thread_level = 0;
1310     _thread_provided = -1;
1311 #endif
1312     largc = *argc;
1313     largv = *argv;
1314     MPI_Comm_size(MPI_COMM_WORLD, numNodes);
1315     MPI_Comm_rank(MPI_COMM_WORLD, myNodeID);
1316
1317     myNID = *myNodeID;
1318
1319     MPI_Get_version(&ver, &subver);
1320     if (myNID == 0) {
1321         printf("Charm++> Running on MPI version: %d.%d\n", ver, subver);
1322         printf("Charm++> level of thread support used: %s (desired: %s)\n", thread_level_tostring(_thread_provided), thread_level_tostring(thread_level));
1323     }
1324
1325 #if CMK_SMP
1326     if (Cmi_smp_mode_setting == COMM_THREAD_ONLY_RECV && _thread_provided != MPI_THREAD_MULTIPLE) {
1327         Cmi_smp_mode_setting = COMM_THREAD_SEND_RECV; 
1328         if (myNID == 0) {
1329           printf("Charm++> +comm_thread_only_recv disabled\n");
1330         }
1331     }
1332 #endif
1333
1334     {
1335         int debug = CmiGetArgFlag(largv,"++debug");
1336         int debug_no_pause = CmiGetArgFlag(largv,"++debug-no-pause");
1337         if (debug || debug_no_pause) {  /*Pause so user has a chance to start and attach debugger*/
1338 #if CMK_HAS_GETPID
1339             printf("CHARMDEBUG> Processor %d has PID %d\n",myNID,getpid());
1340             fflush(stdout);
1341             if (!debug_no_pause)
1342                 sleep(15);
1343 #else
1344             printf("++debug ignored.\n");
1345 #endif
1346         }
1347     }
1348
1349
1350 #if CMK_MEM_CHECKPOINT
1351     if (CmiGetArgInt(largv,"+wp",&num_workpes)) {
1352        CmiAssert(num_workpes <= *numNodes);
1353        total_pes = *numNodes;
1354        *numNodes = num_workpes;
1355     }
1356     else
1357        total_pes = num_workpes = *numNodes;
1358     if (*myNodeID == 0)
1359        CmiPrintf("Charm++> FT using %d processors and %d spare processors.\n", num_workpes, total_pes-num_workpes);
1360     petorank = (int *)malloc(sizeof(int) * num_workpes);
1361     for (i=0; i<num_workpes; i++)  petorank[i] = i;
1362     nextrank = num_workpes;
1363
1364     if (*myNodeID >= num_workpes) {    /* is spare processor */
1365       MPI_Status sts;
1366       int vals[2];
1367       MPI_Recv(vals,2,MPI_INT,MPI_ANY_SOURCE,FAIL_TAG, MPI_COMM_WORLD,&sts);
1368       int newpe = vals[0];
1369       CpvAccess(_curRestartPhase) = vals[1];
1370
1371       if (newpe == -1) {
1372           MPI_Barrier(MPI_COMM_WORLD);
1373           MPI_Finalize();
1374           exit(0);
1375       }
1376
1377       CmiPrintf("Charm++> Spare MPI rank %d is activated for PE %d.\n", *myNodeID, newpe);
1378         /* update petorank */
1379       MPI_Recv(petorank, num_workpes, MPI_INT,MPI_ANY_SOURCE,FAIL_TAG,MPI_COMM_WORLD, &sts);
1380       nextrank = *myNodeID + 1;
1381       *myNodeID = newpe;
1382       myNID = newpe;
1383
1384        /* add +restartaftercrash to argv */
1385       char *phase_str;
1386       char **restart_argv;
1387       int i=0;
1388       while(largv[i]!= NULL) i++;
1389       restart_argv = (char **)malloc(sizeof(char *)*(i+3));
1390       i=0;
1391       while(largv[i]!= NULL){
1392                 restart_argv[i] = largv[i];
1393                 i++;
1394       }
1395       restart_argv[i] = "+restartaftercrash";
1396       phase_str = (char*)malloc(10);
1397       sprintf(phase_str,"%d", CpvAccess(_curRestartPhase));
1398       restart_argv[i+1]=phase_str;
1399       restart_argv[i+2]=NULL;
1400       *argv = restart_argv;
1401       *argc = i+2;
1402       largc = *argc;
1403       largv = *argv;
1404     }
1405 #endif
1406
1407     idleblock = CmiGetArgFlag(largv, "+idleblocking");
1408     if (idleblock && _Cmi_mynode == 0) {
1409         printf("Charm++: Running in idle blocking mode.\n");
1410     }
1411
1412 #if CMK_CHARMDEBUG
1413     /* setup signal handlers */
1414     signal(SIGSEGV, KillOnAllSigs);
1415     signal(SIGFPE, KillOnAllSigs);
1416     signal(SIGILL, KillOnAllSigs);
1417     signal_int = signal(SIGINT, KillOnAllSigs);
1418     signal(SIGTERM, KillOnAllSigs);
1419     signal(SIGABRT, KillOnAllSigs);
1420 #   if !defined(_WIN32) || defined(__CYGWIN__) /*UNIX-only signals*/
1421     signal(SIGQUIT, KillOnAllSigs);
1422     signal(SIGBUS, KillOnAllSigs);
1423 #   endif /*UNIX*/
1424 #endif
1425
1426 #if CMK_NO_OUTSTANDING_SENDS
1427     no_outstanding_sends=1;
1428 #endif
1429     if (CmiGetArgFlag(largv,"+no_outstanding_sends")) {
1430         no_outstanding_sends = 1;
1431         if (myNID == 0)
1432             printf("Charm++: Will%s consume outstanding sends in scheduler loop\n",
1433                    no_outstanding_sends?"":" not");
1434     }
1435
1436     request_max=MAX_QLEN;
1437     CmiGetArgInt(largv,"+requestmax",&request_max);
1438     /*printf("request max=%d\n", request_max);*/
1439
1440 #if MPI_POST_RECV
1441     CmiGetArgInt(largv, "+postRecvCnt", &MPI_POST_RECV_COUNT);
1442     CmiGetArgInt(largv, "+postRecvLowerSize", &MPI_POST_RECV_LOWERSIZE);
1443     CmiGetArgInt(largv, "+postRecvUpperSize", &MPI_POST_RECV_UPPERSIZE);
1444     CmiGetArgInt(largv, "+postRecvThreshold", &MPI_POST_RECV_MSG_CNT_THRESHOLD);
1445     CmiGetArgInt(largv, "+postRecvBucketSize", &MPI_POST_RECV_INC);
1446     CmiGetArgInt(largv, "+postRecvMsgInc", &MPI_POST_RECV_MSG_INC);
1447     CmiGetArgInt(largv, "+postRecvCheckFreq", &MPI_POST_RECV_FREQ);
1448     if (MPI_POST_RECV_COUNT<=0) MPI_POST_RECV_COUNT=1;
1449     if (MPI_POST_RECV_LOWERSIZE>MPI_POST_RECV_UPPERSIZE) MPI_POST_RECV_UPPERSIZE = MPI_POST_RECV_LOWERSIZE;
1450     MPI_POST_RECV_SIZE = MPI_POST_RECV_UPPERSIZE;
1451     if (myNID==0) {
1452         printf("Charm++: using post-recv scheme with %d pre-posted recvs ranging from %d to %d (bytes) with msg count threshold %d and msg histogram bucket size %d, #buf increment every %d msgs. The buffers are checked every %d msgs\n",
1453                MPI_POST_RECV_COUNT, MPI_POST_RECV_LOWERSIZE, MPI_POST_RECV_UPPERSIZE,
1454                MPI_POST_RECV_MSG_CNT_THRESHOLD, MPI_POST_RECV_INC, MPI_POST_RECV_MSG_INC, MPI_POST_RECV_FREQ);
1455     }
1456 #endif
1457
1458 #if CMI_DYNAMIC_EXERT_CAP
1459     CmiGetArgInt(largv, "+dynCapThreshold", &CMI_DYNAMIC_OUTGOING_THRESHOLD);
1460     CmiGetArgInt(largv, "+dynCapSend", &CMI_DYNAMIC_SEND_CAPSIZE);
1461     CmiGetArgInt(largv, "+dynCapRecv", &CMI_DYNAMIC_RECV_CAPSIZE);
1462     if (myNID==0) {
1463         printf("Charm++: using dynamic flow control with outgoing threshold %d, send cap %d, recv cap %d\n",
1464                CMI_DYNAMIC_OUTGOING_THRESHOLD, CMI_DYNAMIC_SEND_CAPSIZE, CMI_DYNAMIC_RECV_CAPSIZE);
1465     }
1466 #endif
1467
1468 #if USE_ASYNC_RECV_FUNC
1469     CmiGetArgInt(largv, "+irecvMsgThreshold", &IRECV_MSG_THRESHOLD);
1470     if(myNID==0) {
1471         printf("Charm++: for msg size larger than %d, MPI_Irecv is going to be used.\n", IRECV_MSG_THRESHOLD);
1472     }
1473 #endif
1474
1475     /* checksum flag */
1476     if (CmiGetArgFlag(largv,"+checksum")) {
1477 #if CMK_ERROR_CHECKING
1478         checksum_flag = 1;
1479         if (myNID == 0) CmiPrintf("Charm++: CheckSum checking enabled! \n");
1480 #else
1481         if (myNID == 0) CmiPrintf("Charm++: +checksum ignored in optimized version! \n");
1482 #endif
1483     }
1484
1485     procState = (ProcState *)malloc((_Cmi_mynodesize+1) * sizeof(ProcState));
1486     for (i=0; i<_Cmi_mynodesize+1; i++) {
1487 #if MULTI_SENDQUEUE
1488         procState[i].sendMsgBuf = PCQueueCreate();
1489 #endif
1490         procState[i].recvLock = CmiCreateLock();
1491     }
1492 #if CMK_SMP
1493 #if !MULTI_SENDQUEUE
1494     sendMsgBuf = PCQueueCreate();
1495     sendMsgBufLock = CmiCreateLock();
1496 #endif
1497 #endif
1498 }
1499
1500 static void MachinePreCommonInitForMPI(int everReturn) {
1501
1502 #if MPI_POST_RECV
1503     int doInit = 1;
1504     int i;
1505
1506 #if CMK_SMP
1507     if (CmiMyRank() != CmiMyNodeSize()) doInit = 0;
1508 #endif
1509
1510     /* Currently, in mpi smp, the main thread will be the comm thread, so
1511      *  only the comm thread should post recvs. Cpvs, however, need to be
1512      * created on rank 0 (the ptrs to the actual cpv memory), while
1513      * other ranks are busy waiting for this to finish. So cpv initialize
1514      * routines have to be called on every ranks, although they are only
1515      * useful on comm thread (whose rank is not zero) -Chao Mei
1516      */
1517     CpvInitialize(unsigned long long, Cmi_posted_recv_total);
1518     CpvInitialize(unsigned long long, Cmi_unposted_recv_total);
1519     CpvInitialize(MPI_Request*, CmiPostedRecvRequests);
1520     CpvInitialize(char **, CmiPostedRecvBuffers);
1521
1522     CpvAccess(CmiPostedRecvRequests) = NULL;
1523     CpvAccess(CmiPostedRecvBuffers) = NULL;
1524
1525     CpvInitialize(MPIPostRecvList *, postRecvListHdr);
1526     CpvInitialize(MPIPostRecvList *, curPostRecvPtr);
1527     CpvInitialize(int, msgRecvCnt);
1528
1529     CpvAccess(postRecvListHdr) = NULL;
1530     CpvAccess(curPostRecvPtr) = NULL;
1531     CpvAccess(msgRecvCnt) = 0;
1532
1533 #if MPI_DYNAMIC_POST_RECV
1534     CpvInitialize(int *, MSG_HISTOGRAM_ARRAY);
1535 #endif
1536
1537     if (doInit) {
1538 #if MPI_DYNAMIC_POST_RECV
1539         MSG_HISTOGRAM_BINSIZE = MPI_POST_RECV_INC;
1540         /* including two more buckets that are out of the range [LOWERSIZE, UPPERSIZE] */
1541         MAX_HISTOGRAM_BUCKETS = (MPI_POST_RECV_UPPERSIZE - MPI_POST_RECV_LOWERSIZE)/MSG_HISTOGRAM_BINSIZE+2;
1542         CpvAccess(MSG_HISTOGRAM_ARRAY) = (int *)malloc(sizeof(int)*MAX_HISTOGRAM_BUCKETS);
1543         memset(CpvAccess(MSG_HISTOGRAM_ARRAY), 0, sizeof(int)*MAX_HISTOGRAM_BUCKETS);
1544 #else
1545         /* Post some extra recvs to help out with incoming messages */
1546         /* On some MPIs the messages are unexpected and thus slow */
1547
1548         CpvAccess(postRecvListHdr) = (MPIPostRecvList *)malloc(sizeof(MPIPostRecvList));
1549
1550         /* An array of request handles for posted recvs */
1551         CpvAccess(postRecvListHdr)->msgSizeIdx = -1;
1552         CpvAccess(postRecvListHdr)->bufCnt = MPI_POST_RECV_COUNT;
1553         CpvAccess(postRecvListHdr)->postedRecvReqs = (MPI_Request*)malloc(sizeof(MPI_Request)*MPI_POST_RECV_COUNT);
1554         /* An array of buffers for posted recvs */
1555         CpvAccess(postRecvListHdr)->postedRecvBufs = (char**)malloc(MPI_POST_RECV_COUNT*sizeof(char *));
1556         CpvAccess(postRecvListHdr)->next = CpvAccess(postRecvListHdr);
1557         CpvAccess(curPostRecvPtr) = CpvAccess(postRecvListHdr);
1558
1559         /* Post Recvs */
1560         for (i=0; i<MPI_POST_RECV_COUNT; i++) {
1561             char *tmpbuf = (char *)CmiAlloc(MPI_POST_RECV_SIZE); /* Note: could be aligned allocation?? */
1562             CpvAccess(postRecvListHdr)->postedRecvBufs[i] = tmpbuf;
1563             if (MPI_SUCCESS != MPI_Irecv(tmpbuf,
1564                                          MPI_POST_RECV_SIZE,
1565                                          MPI_BYTE,
1566                                          MPI_ANY_SOURCE,
1567                                          POST_RECV_TAG,
1568                                          MPI_COMM_WORLD,
1569                                          CpvAccess(postRecvListHdr)->postedRecvReqs+i  ))
1570                 CmiAbort("MPI_Irecv failed\n");
1571         }
1572 #endif
1573     }
1574 #endif /* end of MPI_POST_RECV */
1575
1576 #if CAPTURE_MSG_HISTOGRAM && !MPI_DYNAMIC_POST_RECV
1577     CpvInitialize(int *, MSG_HISTOGRAM_ARRAY);
1578     CpvAccess(MSG_HISTOGRAM_ARRAY) = (int *)malloc(sizeof(int)*MAX_HISTOGRAM_BUCKETS);
1579     memset(CpvAccess(MSG_HISTOGRAM_ARRAY), 0, sizeof(int)*MAX_HISTOGRAM_BUCKETS);
1580 #endif
1581
1582 #if USE_ASYNC_RECV_FUNC
1583 #if CMK_SMP
1584     /* allocate the guardian entry only on comm thread considering NUMA */
1585     if(CmiMyRank() == CmiMyNodeSize()) {
1586         waitIrecvListHead = waitIrecvListTail = irecvListEntryAllocate();
1587         waitIrecvListHead->next = NULL;
1588     }
1589 #else    
1590     waitIrecvListHead = waitIrecvListTail = irecvListEntryAllocate();
1591     waitIrecvListHead->next = NULL;
1592 #endif
1593 #endif
1594 }
1595
1596 static void MachinePostCommonInitForMPI(int everReturn) {
1597
1598     CmiIdleState *s=CmiNotifyGetState();
1599
1600     CpvInitialize(SMSG_LIST *, sent_msgs);
1601     CpvInitialize(SMSG_LIST *, end_sent);
1602     CpvInitialize(int, MsgQueueLen);
1603     CpvAccess(sent_msgs) = NULL;
1604     CpvAccess(end_sent) = NULL;
1605     CpvAccess(MsgQueueLen) = 0;
1606
1607     machine_exit_idx = CmiRegisterHandler((CmiHandler)machine_exit);
1608
1609 #if CMI_MPI_TRACE_USEREVENTS && CMK_TRACE_ENABLED && !CMK_TRACE_IN_CHARM
1610     CpvInitialize(double, projTraceStart);
1611     /* only PE 0 needs to care about registration (to generate sts file). */
1612     if (CmiMyPe() == 0) {
1613         registerMachineUserEventsFunction(&registerMPITraceEvents);
1614     }
1615 #endif
1616
1617 #if CMK_SMP
1618     CcdCallOnConditionKeep(CcdPROCESSOR_BEGIN_IDLE,(CcdVoidFn)CmiNotifyBeginIdle,(void *)s);
1619     CcdCallOnConditionKeep(CcdPROCESSOR_STILL_IDLE,(CcdVoidFn)CmiNotifyStillIdle,(void *)s);
1620     if (Cmi_smp_mode_setting == COMM_THREAD_ONLY_RECV)
1621       CcdCallOnConditionKeep(CcdPERIODIC,(CcdVoidFn)LrtsPostNonLocal,NULL);
1622 #else
1623     CcdCallOnConditionKeep(CcdPROCESSOR_STILL_IDLE,(CcdVoidFn)CmiNotifyIdleForMPI,NULL);
1624 #endif
1625
1626 #if MACHINE_DEBUG_LOG
1627     if (CmiMyRank() == 0) {
1628         char ln[200];
1629         sprintf(ln,"debugLog.%d",CmiMyNode());
1630         debugLog=fopen(ln,"w");
1631     }
1632 #endif
1633 }
1634 /* ######End of functions related with starting programs###### */
1635
1636 /***********************************************************************
1637  *
1638  * Abort function:
1639  *
1640  ************************************************************************/
1641
1642 void LrtsAbort(const char *message) {
1643     char *m;
1644     /* if CharmDebug is attached simply try to send a message to it */
1645 #if CMK_CCS_AVAILABLE
1646     if (CpvAccess(cmiArgDebugFlag)) {
1647         CpdNotify(CPD_ABORT, message);
1648         CpdFreeze();
1649     }
1650 #endif
1651     CmiError("------------- Processor %d Exiting: Called CmiAbort ------------\n"
1652              "Reason: %s\n",CmiMyPe(),message);
1653     /*  CmiError(message); */
1654     CmiPrintStackTrace(0);
1655     m = CmiAlloc(CmiMsgHeaderSizeBytes);
1656     CmiSetHandler(m, machine_exit_idx);
1657     CmiSyncBroadcastAndFree(CmiMsgHeaderSizeBytes, m);
1658     machine_exit(m);
1659     /* Program never reaches here */
1660     MPI_Abort(MPI_COMM_WORLD, 1);
1661 }
1662
1663 /**************************  TIMER FUNCTIONS **************************/
1664 #if CMK_TIMER_USE_SPECIAL || CMK_TIMER_USE_XT3_DCLOCK
1665
1666 /* MPI calls are not threadsafe, even the timer on some machines */
1667 static CmiNodeLock  timerLock = 0;
1668                                 static int _absoluteTime = 0;
1669                                                            static double starttimer = 0;
1670                                                                                       static int _is_global = 0;
1671
1672 int CmiTimerIsSynchronized() {
1673     int  flag;
1674     void *v;
1675
1676     /*  check if it using synchronized timer */
1677     if (MPI_SUCCESS != MPI_Attr_get(MPI_COMM_WORLD, MPI_WTIME_IS_GLOBAL, &v, &flag))
1678         printf("MPI_WTIME_IS_GLOBAL not valid!\n");
1679     if (flag) {
1680         _is_global = *(int*)v;
1681         if (_is_global && CmiMyPe() == 0)
1682             printf("Charm++> MPI timer is synchronized\n");
1683     }
1684     return _is_global;
1685 }
1686
1687 int CmiTimerAbsolute() {
1688     return _absoluteTime;
1689 }
1690
1691 double CmiStartTimer() {
1692     return 0.0;
1693 }
1694
1695 double CmiInitTime() {
1696     return starttimer;
1697 }
1698
1699 void CmiTimerInit(char **argv) {
1700     _absoluteTime = CmiGetArgFlagDesc(argv,"+useAbsoluteTime", "Use system's absolute time as wallclock time.");
1701     if (_absoluteTime && CmiMyPe() == 0)
1702         printf("Charm++> absolute MPI timer is used\n");
1703
1704 #if ! CMK_MEM_CHECKPOINT
1705     _is_global = CmiTimerIsSynchronized();
1706 #else
1707     _is_global = 0;
1708 #endif
1709
1710     if (_is_global) {
1711         if (CmiMyRank() == 0) {
1712             double minTimer;
1713 #if CMK_TIMER_USE_XT3_DCLOCK
1714             starttimer = dclock();
1715 #else
1716             starttimer = MPI_Wtime();
1717 #endif
1718
1719             MPI_Allreduce(&starttimer, &minTimer, 1, MPI_DOUBLE, MPI_MIN,
1720                           MPI_COMM_WORLD );
1721             starttimer = minTimer;
1722         }
1723     } else { /* we don't have a synchronous timer, set our own start time */
1724 #if ! CMK_MEM_CHECKPOINT
1725         CmiBarrier();
1726         CmiBarrier();
1727         CmiBarrier();
1728 #endif
1729 #if CMK_TIMER_USE_XT3_DCLOCK
1730         starttimer = dclock();
1731 #else
1732         starttimer = MPI_Wtime();
1733 #endif
1734     }
1735
1736 #if 0 && CMK_SMP && CMK_MPI_INIT_THREAD
1737     if (CmiMyRank()==0 && _thread_provided == MPI_THREAD_SINGLE)
1738         timerLock = CmiCreateLock();
1739 #endif
1740     CmiNodeAllBarrier();          /* for smp */
1741 }
1742
1743 /**
1744  * Since the timerLock is never created, and is
1745  * always NULL, then all the if-condition inside
1746  * the timer functions could be disabled right
1747  * now in the case of SMP. --Chao Mei
1748  */
1749 double CmiTimer(void) {
1750     double t;
1751 #if 0 && CMK_SMP
1752     if (timerLock) CmiLock(timerLock);
1753 #endif
1754
1755 #if CMK_TIMER_USE_XT3_DCLOCK
1756     t = dclock();
1757 #else
1758     t = MPI_Wtime();
1759 #endif
1760
1761 #if 0 && CMK_SMP
1762     if (timerLock) CmiUnlock(timerLock);
1763 #endif
1764
1765     return _absoluteTime?t: (t-starttimer);
1766 }
1767
1768 double CmiWallTimer(void) {
1769     double t;
1770 #if 0 && CMK_SMP
1771     if (timerLock) CmiLock(timerLock);
1772 #endif
1773
1774 #if CMK_TIMER_USE_XT3_DCLOCK
1775     t = dclock();
1776 #else
1777     t = MPI_Wtime();
1778 #endif
1779
1780 #if 0 && CMK_SMP
1781     if (timerLock) CmiUnlock(timerLock);
1782 #endif
1783
1784     return _absoluteTime? t: (t-starttimer);
1785 }
1786
1787 double CmiCpuTimer(void) {
1788     double t;
1789 #if 0 && CMK_SMP
1790     if (timerLock) CmiLock(timerLock);
1791 #endif
1792 #if CMK_TIMER_USE_XT3_DCLOCK
1793     t = dclock() - starttimer;
1794 #else
1795     t = MPI_Wtime() - starttimer;
1796 #endif
1797 #if 0 && CMK_SMP
1798     if (timerLock) CmiUnlock(timerLock);
1799 #endif
1800     return t;
1801 }
1802
1803 #endif     /* CMK_TIMER_USE_SPECIAL */
1804
1805 /************Barrier Related Functions****************/
1806 /* must be called on all ranks including comm thread in SMP */
1807 int CmiBarrier() {
1808 #if CMK_SMP
1809     /* make sure all ranks reach here, otherwise comm threads may reach barrier ignoring other ranks  */
1810     CmiNodeAllBarrier();
1811     if (CmiMyRank() == CmiMyNodeSize())
1812 #else
1813     if (CmiMyRank() == 0)
1814 #endif
1815     {
1816         /**
1817          *  The call of CmiBarrier is usually before the initialization
1818          *  of trace module of Charm++, therefore, the START_EVENT
1819          *  and END_EVENT are disabled here. -Chao Mei
1820          */
1821         /*START_EVENT();*/
1822
1823         if (MPI_SUCCESS != MPI_Barrier(MPI_COMM_WORLD))
1824             CmiAbort("Timernit: MPI_Barrier failed!\n");
1825
1826         /*END_EVENT(10);*/
1827     }
1828     CmiNodeAllBarrier();
1829     return 0;
1830 }
1831
1832 /* CmiBarrierZero make sure node 0 is the last one exiting the barrier */
1833 int CmiBarrierZero() {
1834     int i;
1835 #if CMK_SMP
1836     if (CmiMyRank() == CmiMyNodeSize())
1837 #else
1838     if (CmiMyRank() == 0)
1839 #endif
1840     {
1841         char msg[1];
1842         MPI_Status sts;
1843         if (CmiMyNode() == 0)  {
1844             for (i=0; i<CmiNumNodes()-1; i++) {
1845                 START_EVENT();
1846
1847                 if (MPI_SUCCESS != MPI_Recv(msg,1,MPI_BYTE,MPI_ANY_SOURCE,BARRIER_ZERO_TAG, MPI_COMM_WORLD,&sts))
1848                     CmiPrintf("MPI_Recv failed!\n");
1849
1850                 END_EVENT(30);
1851             }
1852         } else {
1853             START_EVENT();
1854
1855             if (MPI_SUCCESS != MPI_Send((void *)msg,1,MPI_BYTE,0,BARRIER_ZERO_TAG,MPI_COMM_WORLD))
1856                 printf("MPI_Send failed!\n");
1857
1858             END_EVENT(20);
1859         }
1860     }
1861     CmiNodeAllBarrier();
1862     return 0;
1863 }
1864
1865
1866 #if CMK_MEM_CHECKPOINT
1867
1868 void mpi_restart_crashed(int pe, int rank)
1869 {
1870     int vals[2];
1871     vals[0] = pe;
1872     vals[1] = CpvAccess(_curRestartPhase)+1;
1873     MPI_Send((void *)vals,2,MPI_INT,rank,FAIL_TAG,MPI_COMM_WORLD);
1874     MPI_Send(petorank, num_workpes, MPI_INT,rank,FAIL_TAG,MPI_COMM_WORLD);
1875 }
1876
1877 /* notify spare processors to exit */
1878 void mpi_end_spare()
1879 {
1880     int i;
1881     for (i=nextrank; i<total_pes; i++) {
1882         int vals[2] = {-1,-1};
1883         MPI_Send((void *)vals,2,MPI_INT,i,FAIL_TAG,MPI_COMM_WORLD);
1884     }
1885 }
1886
1887 int find_spare_mpirank(int pe)
1888 {
1889     if (nextrank == total_pes) {
1890       CmiAbort("Charm++> No spare processor available.");
1891     }
1892     petorank[pe] = nextrank;
1893     nextrank++;
1894     return nextrank-1;
1895 }
1896
1897 void CkDieNow()
1898 {
1899     CmiPrintf("[%d] die now.\n", CmiMyPe());
1900
1901       /* release old messages */
1902     while (!CmiAllAsyncMsgsSent()) {
1903         PumpMsgs();
1904         CmiReleaseSentMessages();
1905     }
1906     MPI_Barrier(MPI_COMM_WORLD);
1907     MPI_Finalize();
1908     exit(0);
1909 }
1910
1911 #endif
1912
1913 /*======Beginning of Msg Histogram or Dynamic Post-Recv Related Funcs=====*/
1914 #if CAPTURE_MSG_HISTOGRAM || MPI_DYNAMIC_POST_RECV
1915 /* Functions related with capturing msg histogram */
1916
1917 #if MPI_DYNAMIC_POST_RECV
1918 /* Consume all messages in the request buffers */
1919 static void consumeAllMsgs()
1920 {
1921     MPIPostRecvList *ptr = CpvAccess(curPostRecvPtr);
1922     if (ptr) {
1923         do {
1924             int i;
1925             for (i=0; i<ptr->bufCnt; i++) {
1926                 int done = 0;
1927                 MPI_Status sts;
1928
1929                 /* Indicating this entry has been tested before */
1930                 if (ptr->postedRecvBufs[i] == NULL) continue;
1931
1932                 START_TRACE_RECVCOMM(NULL);
1933                 if (MPI_SUCCESS != MPI_Test(ptr->postedRecvReqs+i, &done, &sts))
1934                     CmiAbort("consumeAllMsgs failed in MPI_Test!\n");
1935                 if (done) {
1936                     int nbytes;
1937                     char *msg;                    
1938                     
1939                     if (MPI_SUCCESS != MPI_Get_count(&sts, MPI_BYTE, &nbytes))
1940                         CmiAbort("consumeAllMsgs failed in MPI_Get_count!\n");
1941                     /* ready to handle this msg */
1942                     msg = (ptr->postedRecvBufs)[i];
1943                     (ptr->postedRecvBufs)[i] = NULL;
1944                     
1945                     END_TRACE_RECVCOMM(msg);
1946                     handleOneRecvedMsg(nbytes, msg);
1947                 } else {
1948                     if (MPI_SUCCESS != MPI_Cancel(ptr->postedRecvReqs+i))
1949                         CmiAbort("consumeAllMsgs failed in MPI_Cancel!\n");
1950                 }
1951             }
1952             ptr = ptr->next;
1953         } while (ptr != CpvAccess(curPostRecvPtr));
1954     }
1955 }
1956
1957 static void recordMsgHistogramInfo(int size)
1958 {
1959     int idx = 0;
1960     size -= MPI_POST_RECV_LOWERSIZE;
1961     if (size > 0)
1962         idx = (size/MSG_HISTOGRAM_BINSIZE + 1);
1963
1964     if (idx >= MAX_HISTOGRAM_BUCKETS) idx = MAX_HISTOGRAM_BUCKETS-1;
1965     CpvAccess(MSG_HISTOGRAM_ARRAY)[idx]++;
1966 }
1967
1968 #define POST_RECV_USE_STATIC_PARAM 0
1969 #define POST_RECV_REPORT_STS 0
1970
1971 #if POST_RECV_REPORT_STS
1972 static int buildDynCallCnt = 0;
1973 #endif
1974
1975 static void buildDynamicRecvBuffers()
1976 {
1977     int i;
1978
1979     int local_MSG_CNT_THRESHOLD;
1980     int local_MSG_INC;
1981
1982 #if POST_RECV_REPORT_STS
1983     buildDynCallCnt++;
1984 #endif
1985
1986     /* For debugging usage */
1987     reportMsgHistogramInfo();
1988
1989     CpvAccess(msgRecvCnt) = 0;
1990     /* consume all outstanding msgs */
1991     consumeAllMsgs();
1992
1993 #if POST_RECV_USE_STATIC_PARAM
1994     local_MSG_CNT_THRESHOLD = MPI_POST_RECV_MSG_CNT_THRESHOLD;
1995     local_MSG_INC = MPI_POST_RECV_MSG_INC;
1996 #else
1997     {
1998         int total = 0;
1999         int count = 0;
2000         for (i=1; i<MAX_HISTOGRAM_BUCKETS-1; i++) {
2001             int tmp = CpvAccess(MSG_HISTOGRAM_ARRAY)[i];
2002             /* avg is temporarily used for counting how many buckets are non-zero */
2003             if (tmp > 0)  {
2004                 total += tmp;
2005                 count++;
2006             }
2007         }
2008         if (count == 1) local_MSG_CNT_THRESHOLD = 1; /* Just filter out those zero-count msgs */
2009         else local_MSG_CNT_THRESHOLD = total / count /3; /* Catch >50% msgs NEED-BETTER-SCHEME HERE!!*/
2010         local_MSG_INC = total/count; /* Not having a good heuristic right now */
2011 #if POST_RECV_REPORT_STS
2012         printf("sel_histo[%d]: critia_threshold=%d, critia_msginc=%d\n", CmiMyPe(), local_MSG_CNT_THRESHOLD, local_MSG_INC);
2013 #endif
2014     }
2015 #endif
2016
2017     /* First continue to find the first msg range that requires post recv */
2018     /* Ignore the fist and the last one because they are not tracked */
2019     MPIPostRecvList *newHdr = NULL;
2020     MPIPostRecvList *newListPtr = newHdr;
2021     MPIPostRecvList *ptr = CpvAccess(postRecvListHdr);
2022     for (i=1; i<MAX_HISTOGRAM_BUCKETS-1; i++) {
2023         int count = CpvAccess(MSG_HISTOGRAM_ARRAY)[i];
2024         if (count >= local_MSG_CNT_THRESHOLD) {
2025
2026 #if POST_RECV_REPORT_STS
2027             /* Report histogram results */
2028             int low = (i-1)*MSG_HISTOGRAM_BINSIZE + MPI_POST_RECV_LOWERSIZE;
2029             int high = low + MSG_HISTOGRAM_BINSIZE;
2030             int reportCnt;
2031             if (count == local_MSG_CNT_THRESHOLD) reportCnt = 1;
2032             else reportCnt = (count - local_MSG_CNT_THRESHOLD)/local_MSG_INC + 1;
2033             printf("sel_histo[%d]-%d: msg size [%.2f, %.2f) with count=%d (%d)\n", CmiMyPe(), buildDynCallCnt, low/1000.0, high/1000.0, count, reportCnt);
2034 #endif
2035             /* find if this msg idx exists, the "i" is the msgSizeIdx, in the current list */
2036             int notFound = 1;
2037             MPIPostRecvList *newEntry = NULL;
2038             while (ptr) {
2039                 if (ptr->msgSizeIdx < i) {
2040                     /* free the buffer for this range of msg size */
2041                     MPIPostRecvList *nextptr = ptr->next;
2042
2043                     free(ptr->postedRecvReqs);
2044                     int j;
2045                     for (j=0; j<ptr->bufCnt; j++) {
2046                         if ((ptr->postedRecvBufs)[j]) CmiFree((ptr->postedRecvBufs)[j]);
2047                     }
2048                     free(ptr->postedRecvBufs);
2049                     ptr = nextptr;
2050                 } else if (ptr->msgSizeIdx == i) {
2051                     int newBufCnt, j;
2052                     int bufSize = i*MPI_POST_RECV_INC + MPI_POST_RECV_LOWERSIZE - 1;
2053                     newEntry = ptr;
2054                     /* Do some adjustment according to the current statistics */
2055                     if (count == local_MSG_CNT_THRESHOLD) newBufCnt = 1;
2056                     else newBufCnt = (count - local_MSG_CNT_THRESHOLD)/local_MSG_INC + 1;
2057                     if (newBufCnt != ptr->bufCnt) {
2058                         /* free old buffers, and allocate new buffers */
2059                         free(ptr->postedRecvReqs);
2060                         ptr->postedRecvReqs = (MPI_Request *)malloc(newBufCnt * sizeof(MPI_Request));
2061                         for (j=0; j<ptr->bufCnt; j++) {
2062                             if ((ptr->postedRecvBufs)[j]) CmiFree((ptr->postedRecvBufs)[j]);
2063                         }
2064                         free(ptr->postedRecvBufs);
2065                         ptr->postedRecvBufs = (char **)malloc(newBufCnt * sizeof(char *));
2066                     }
2067
2068                     /* re-post those buffers */
2069                     ptr->bufCnt = newBufCnt;
2070                     for (j=0; j<ptr->bufCnt; j++) {
2071                         ptr->postedRecvBufs[j] = (char *)CmiAlloc(bufSize);
2072                         if (MPI_SUCCESS != MPI_Irecv(ptr->postedRecvBufs[j], bufSize, MPI_BYTE,
2073                                                      MPI_ANY_SOURCE, POST_RECV_TAG+ptr->msgSizeIdx,
2074                                                      MPI_COMM_WORLD, ptr->postedRecvReqs+j))
2075                             CmiAbort("MPI_Irecv failed in buildDynamicRecvBuffers!\n");
2076                     }
2077
2078                     /* We already posted bufs for this range of msg size */
2079                     ptr = ptr->next;
2080                     /* Need to set ptr to NULL as the buf list comes to an end and the while loop exits */
2081                     if (ptr == CpvAccess(postRecvListHdr)) ptr = NULL;
2082                     notFound = 0;
2083                     break;
2084                 } else {
2085                     /* The msgSizeIdx is larger than i */
2086                     break;
2087                 }
2088                 if (ptr == CpvAccess(postRecvListHdr)) {
2089                     ptr = NULL;
2090                     break;
2091                 }
2092             } /* end while(ptr): iterating the posted recv buffer list */
2093
2094             if (notFound) {
2095                 /* the current range of msg size is not found in the list */
2096                 int j;
2097                 int bufSize = i*MPI_POST_RECV_INC + MPI_POST_RECV_LOWERSIZE - 1;
2098                 newEntry = malloc(sizeof(MPIPostRecvList));
2099                 MPIPostRecvList *one = newEntry;
2100                 one->msgSizeIdx = i;
2101                 if (count == local_MSG_CNT_THRESHOLD) one->bufCnt = 1;
2102                 else one->bufCnt = (count - local_MSG_CNT_THRESHOLD)/local_MSG_INC + 1;
2103                 one->postedRecvReqs = (MPI_Request *)malloc(sizeof(MPI_Request)*one->bufCnt);
2104                 one->postedRecvBufs = (char **)malloc(one->bufCnt * sizeof(char *));
2105                 for (j=0; j<one->bufCnt; j++) {
2106                     one->postedRecvBufs[j] = (char *)CmiAlloc(bufSize);
2107                     if (MPI_SUCCESS != MPI_Irecv(one->postedRecvBufs[j], bufSize, MPI_BYTE,
2108                                                  MPI_ANY_SOURCE, POST_RECV_TAG+one->msgSizeIdx,
2109                                                  MPI_COMM_WORLD, one->postedRecvReqs+j))
2110                         CmiAbort("MPI_Irecv failed in buildDynamicRecvBuffers!\n");
2111                 }
2112             } /* end if notFound */
2113
2114             /* Update the new list with the newEntry */
2115             CmiAssert(newEntry != NULL);
2116             if (newHdr == NULL) {
2117                 newHdr = newEntry;
2118                 newListPtr = newEntry;
2119                 newHdr->next = newHdr;
2120             } else {
2121                 newListPtr->next = newEntry;
2122                 newListPtr = newEntry;
2123                 newListPtr->next = newHdr;
2124             }
2125         } /* end if the count of this msg size range exceeds the threshold */
2126     } /* end for loop over the histogram buckets */
2127
2128     /* Free remaining entries in the list */
2129     while (ptr) {
2130         /* free the buffer for this range of msg size */
2131         MPIPostRecvList *nextptr = ptr->next;
2132
2133         free(ptr->postedRecvReqs);
2134         int j;
2135         for (j=0; j<ptr->bufCnt; j++) {
2136             if ((ptr->postedRecvBufs)[j]) CmiFree((ptr->postedRecvBufs)[j]);
2137         }
2138         free(ptr->postedRecvBufs);
2139         ptr = nextptr;
2140         if (ptr == CpvAccess(postRecvListHdr)) break;
2141     }
2142
2143     CpvAccess(curPostRecvPtr) = CpvAccess(postRecvListHdr) = newHdr;
2144     memset(CpvAccess(MSG_HISTOGRAM_ARRAY), 0, sizeof(int)*MAX_HISTOGRAM_BUCKETS);
2145 } /* end of function buildDynamicRecvBuffers */
2146
2147 static void examineMsgHistogramInfo(int size)
2148 {
2149     int total = CpvAccess(msgRecvCnt)++;
2150     if (total < MPI_POST_RECV_FREQ) {
2151         recordMsgHistogramInfo(size);
2152     } else {
2153         buildDynamicRecvBuffers();
2154     }
2155 }
2156 #else
2157 /* case when CAPTURE_MSG_HISTOGRAM is defined */
2158 static void recordMsgHistogramInfo(int size)
2159 {
2160     int idx = size/MSG_HISTOGRAM_BINSIZE;
2161     if (idx >= MAX_HISTOGRAM_BUCKETS) idx = MAX_HISTOGRAM_BUCKETS-1;
2162     CpvAccess(MSG_HISTOGRAM_ARRAY)[idx]++;
2163 }
2164 #endif /* end of MPI_DYNAMIC_POST_RECV */
2165
2166 static void reportMsgHistogramInfo()
2167 {
2168 #if MPI_DYNAMIC_POST_RECV
2169     int i, count;
2170     count = CpvAccess(MSG_HISTOGRAM_ARRAY)[0];
2171     if (count > 0) {
2172         printf("msg_histo[%d]: %d for msg [0, %.2fK)\n", CmiMyNode(), count, MPI_POST_RECV_LOWERSIZE/1000.0);
2173     }
2174     for (i=1; i<MAX_HISTOGRAM_BUCKETS-1; i++) {
2175         int count = CpvAccess(MSG_HISTOGRAM_ARRAY)[i];
2176         if (count > 0) {
2177             int low = (i-1)*MSG_HISTOGRAM_BINSIZE + MPI_POST_RECV_LOWERSIZE;
2178             int high = low + MSG_HISTOGRAM_BINSIZE;
2179             printf("msg_histo[%d]: %d for msg [%.2fK, %.2fK)\n", CmiMyNode(), count, low/1000.0, high/1000.0);
2180         }
2181     }
2182     count = CpvAccess(MSG_HISTOGRAM_ARRAY)[MAX_HISTOGRAM_BUCKETS-1];
2183     if (count > 0) {
2184         printf("msg_histo[%d]: %d for msg [%.2fK, +inf)\n", CmiMyNode(), count, MPI_POST_RECV_UPPERSIZE/1000.0);
2185     }
2186 #else
2187     int i;
2188     for (i=0; i<MAX_HISTOGRAM_BUCKETS; i++) {
2189         int count = CpvAccess(MSG_HISTOGRAM_ARRAY)[i];
2190         if (count > 0) {
2191             int low = i*MSG_HISTOGRAM_BINSIZE;
2192             int high = low + MSG_HISTOGRAM_BINSIZE;
2193             printf("msg_histo[%d]: %d for msg [%dK, %dK)\n", CmiMyNode(), count, low/1000, high/1000);
2194         }
2195     }
2196 #endif
2197 }
2198 #endif /* end of CAPTURE_MSG_HISTOGRAM || MPI_DYNAMIC_POST_RECV */
2199
2200 void CmiSetupMachineRecvBuffersUser()
2201 {
2202 #if MPI_DYNAMIC_POST_RECV
2203     buildDynamicRecvBuffers();
2204 #endif
2205 }
2206 /*=======End of Msg Histogram or Dynamic Post-Recv Related Funcs======*/
2207
2208
2209 /*@}*/
2210