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