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