Added function declarations to .h file
[charm.git] / src / conv-ccs / conv-ccs.c
1 /*****************************************************************************
2  * $Source$
3  * $Author$
4  * $Date$
5  * $Revision$
6  *****************************************************************************/
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <errno.h>
11 #include <string.h>
12
13 #include "converse.h"
14 #include "conv-ccs.h"
15 #include "ccs-server.h"
16 #include "sockRoutines.h"
17 #include "queueing.h"
18
19 #if CMK_CCS_AVAILABLE
20
21 /*****************************************************************************
22  *
23  * Converse Client-Server Functions
24  *
25  *****************************************************************************/
26  
27 #include "ckhashtable.h"
28
29 /* Includes all information stored about a single CCS handler. */
30 typedef struct CcsHandlerRec {
31         const char *name; /*Name passed over socket*/
32         CmiHandler fnOld; /*Old converse-style handler, or NULL if new-style*/
33         CcsHandlerFn fn; /*New-style handler function, or NULL if old-style*/
34         void *userPtr;
35         CmiReduceMergeFn mergeFn; /*Merge function used for bcast requests*/
36         int nCalls; /* Number of times handler has been executed*/
37         CmiUInt2 redID; /*Reduction ID to be used with CmiListReduce*/
38 } CcsHandlerRec;
39
40 static void initHandlerRec(CcsHandlerRec *c,const char *name) {
41   if (strlen(name)>=CCS_MAXHANDLER) 
42         CmiAbort("CCS handler names cannot exceed 32 characters");
43   c->name=strdup(name);
44   c->fn=NULL;
45   c->fnOld=NULL;
46   c->userPtr=NULL;
47   c->mergeFn=NULL;
48   c->nCalls=0;
49 }
50
51 static void callHandlerRec(CcsHandlerRec *c,int reqLen,const void *reqData) {
52         c->nCalls++;
53         if (c->fnOld) 
54         { /* Backward compatability version:
55             Pack user data into a converse message (cripes! why bother?);
56             user will delete the message. 
57           */
58                 char *cmsg = (char *) CmiAlloc(CmiMsgHeaderSizeBytes+reqLen);
59                 memcpy(cmsg+CmiMsgHeaderSizeBytes, reqData, reqLen);
60                 (c->fnOld)(cmsg);
61         }
62         else { /* Pass read-only copy of data straight to user */
63                 (c->fn)(c->userPtr, reqLen, reqData);
64         }
65 }
66
67 /*Table maps handler name to CcsHandler object*/
68 typedef CkHashtable_c CcsHandlerTable;
69 CpvStaticDeclare(CcsHandlerTable, ccsTab);
70
71 CpvStaticDeclare(CcsImplHeader*,ccsReq);/*Identifies CCS requestor (client)*/
72
73 void CcsRegisterHandler(const char *name, CmiHandler fn) {
74   CcsHandlerRec cp;
75   initHandlerRec(&cp,name);
76   cp.fnOld=fn;
77   *(CcsHandlerRec *)CkHashtablePut(CpvAccess(ccsTab),(void *)&cp.name)=cp;
78 }
79 void CcsRegisterHandlerFn(const char *name, CcsHandlerFn fn, void *ptr) {
80   CcsHandlerRec cp;
81   initHandlerRec(&cp,name);
82   cp.fn=fn;
83   cp.userPtr=ptr;
84   *(CcsHandlerRec *)CkHashtablePut(CpvAccess(ccsTab),(void *)&cp.name)=cp;
85 }
86 void CcsSetMergeFn(const char *name, CmiReduceMergeFn newMerge) {
87   CcsHandlerRec *rec=(CcsHandlerRec *)CkHashtableGet(CpvAccess(ccsTab),(void *)&name);
88   if (rec==NULL) {
89     CmiAbort("CCS: Unknown CCS handler name.\n");
90   }
91   rec->mergeFn=newMerge;
92   rec->redID=CmiGetGlobalReduction();
93 }
94
95 void * CcsMerge_concat(int *size,void *local,void **remote,int n) {
96   CcsImplHeader *hdr;
97   int total = *size;
98   void *reply;
99   char *ptr;
100   int i;
101   for (i=0; i<n; ++i) {
102     hdr = (CcsImplHeader*)(((char*)remote[i])+CmiMsgHeaderSizeBytes);
103     total += ChMessageInt(hdr->len);
104   }
105   reply = CmiAlloc(total);
106   memcpy(reply, local, *size);
107   ((CcsImplHeader*)(((char*)reply)+CmiMsgHeaderSizeBytes))->len = ChMessageInt_new(total-CmiMsgHeaderSizeBytes-sizeof(CcsImplHeader));
108   CmiFree(local);
109   ptr = ((char*)reply)+*size;
110   for (i=0; i<n; ++i) {
111     int len = ChMessageInt(((CcsImplHeader*)(((char*)remote[i])+CmiMsgHeaderSizeBytes))->len);
112     memcpy(ptr, ((char*)remote[i])+CmiMsgHeaderSizeBytes+sizeof(CcsImplHeader), len);
113     ptr += len;
114   }
115   *size = total;
116   return reply;
117 }
118
119 #define SIMPLE_REDUCTION(name, dataType, loop) \
120 void * CcsMerge_##name(int *size,void *local,void **remote,int n) { \
121   int i, m; \
122   CcsImplHeader *hdrLocal = (CcsImplHeader*)(((char*)local)+CmiMsgHeaderSizeBytes); \
123   int lenLocal = ChMessageInt(hdrLocal->len); \
124   int nElem = lenLocal / sizeof(dataType); \
125   dataType *ret = (dataType *) (hdrLocal+1); \
126   CcsImplHeader *hdr; \
127   for (m=0; m<n; ++m) { \
128     int len; \
129     dataType *value; \
130     hdr = (CcsImplHeader*)(((char*)remote[m])+CmiMsgHeaderSizeBytes); \
131     len = ChMessageInt(hdr->len); \
132     value = (dataType *)(hdr+1); \
133     CmiAssert(lenLocal == len); \
134     for (i=0; i<nElem; ++i) loop; \
135   } \
136   return local; \
137 }
138
139 SIMPLE_REDUCTION(logical_and, int, ret[i]=(ret[i]&&value[i])?1:0)
140 SIMPLE_REDUCTION(logical_or, int, ret[i]=(ret[i]||value[i])?1:0)
141 SIMPLE_REDUCTION(bitvec_and, int, ret[i]&=value[i])
142 SIMPLE_REDUCTION(bitvec_or, int, ret[i]|=value[i])
143
144 /*Use this macro for reductions that have the same type for all inputs */
145 #define SIMPLE_POLYMORPH_REDUCTION(nameBase,loop) \
146   SIMPLE_REDUCTION(nameBase##_int, int, loop) \
147   SIMPLE_REDUCTION(nameBase##_float, float, loop) \
148   SIMPLE_REDUCTION(nameBase##_double, double, loop)
149
150 SIMPLE_POLYMORPH_REDUCTION(sum, ret[i]+=value[i])
151 SIMPLE_POLYMORPH_REDUCTION(product, ret[i]*=value[i])
152 SIMPLE_POLYMORPH_REDUCTION(max, if (ret[i]<value[i]) ret[i]=value[i])
153 SIMPLE_POLYMORPH_REDUCTION(min, if (ret[i]>value[i]) ret[i]=value[i])
154
155 #undef SIMPLE_REDUCTION
156 #undef SIMPLE_POLYMORPH_REDUCTION
157
158 int CcsEnabled(void)
159 {
160   return 1;
161 }
162
163 int CcsIsRemoteRequest(void)
164 {
165   return CpvAccess(ccsReq)!=NULL;
166 }
167
168 void CcsCallerId(skt_ip_t *pip, unsigned int *pport)
169 {
170   *pip = CpvAccess(ccsReq)->attr.ip;
171   *pport = ChMessageInt(CpvAccess(ccsReq)->attr.port);
172 }
173
174 static int rep_fw_handler_idx;
175
176 /**
177  * Decide if the reply is ready to be forwarded to the waiting client,
178  * or if combination is required (for broadcast/multicast CCS requests.
179  */
180 int CcsReply(CcsImplHeader *rep,int repLen,const void *repData) {
181   int repPE = (int)ChMessageInt(rep->pe);
182   if (repPE <= -1) {
183     /* Reduce the message to get the final reply */
184     CcsHandlerRec *fn;
185     int len=CmiMsgHeaderSizeBytes+sizeof(CcsImplHeader)+repLen;
186     char *msg=CmiAlloc(len);
187     char *r=msg+CmiMsgHeaderSizeBytes;
188     char *handlerStr;
189     rep->len = ChMessageInt_new(repLen);
190     *(CcsImplHeader *)r=*rep; r+=sizeof(CcsImplHeader);
191     memcpy(r,repData,repLen);
192     CmiSetHandler(msg,rep_fw_handler_idx);
193     handlerStr=rep->handler;
194     fn=(CcsHandlerRec *)CkHashtableGet(CpvAccess(ccsTab),(void *)&handlerStr);
195     if (fn->mergeFn == NULL) CmiAbort("Called CCS broadcast with NULL merge function!\n");
196     if (repPE == -1) {
197       /* CCS Broadcast */
198       CmiReduce(msg, len, fn->mergeFn);
199     } else {
200       /* CCS Multicast */
201       CmiListReduce(-repPE, (int*)(rep+1), msg, len, fn->mergeFn, fn->redID);
202     }
203   } else {
204     CcsImpl_reply(rep, repLen, repData);
205   }
206 }
207
208 CcsDelayedReply CcsDelayReply(void)
209 {
210   CcsDelayedReply ret;
211   int len = sizeof(CcsImplHeader);
212   if (ChMessageInt(CpvAccess(ccsReq)->pe) < -1)
213     len += ChMessageInt(CpvAccess(ccsReq)->pe) * sizeof(int);
214   ret.hdr = (CcsImplHeader*)malloc(len);
215   memcpy(ret.hdr, CpvAccess(ccsReq), len);
216   CpvAccess(ccsReq)=NULL;
217   return ret;
218 }
219
220 void CcsSendReply(int replyLen, const void *replyData)
221 {
222   if (CpvAccess(ccsReq)==NULL)
223     CmiAbort("CcsSendReply: reply already sent!\n");
224   CpvAccess(ccsReq)->len = ChMessageInt_new(1);
225   CcsReply(CpvAccess(ccsReq),replyLen,replyData);
226   CpvAccess(ccsReq) = NULL;
227 }
228
229 void CcsSendDelayedReply(CcsDelayedReply d,int replyLen, const void *replyData)
230 {
231   CcsImplHeader *h = d.hdr;
232   h->len=ChMessageInt_new(1);
233   CcsReply(h,replyLen,replyData);
234   free(h);
235 }
236
237 void CcsNoReply()
238 {
239   if (CpvAccess(ccsReq)==NULL) return;
240   CpvAccess(ccsReq)->len = ChMessageInt_new(0);
241   CcsReply(CpvAccess(ccsReq),0,NULL);
242   CpvAccess(ccsReq) = NULL;
243 }
244
245 void CcsNoDelayedReply(CcsDelayedReply d)
246 {
247   CcsImplHeader *h = d.hdr;
248   h->len = ChMessageInt_new(0);
249   CcsReply(h,0,NULL);
250   free(h);
251 }
252
253
254 /**********************************
255 _CCS Implementation Routines:
256   These do the request forwarding and
257 delivery.
258 ***********************************/
259
260 /*CCS Bottleneck:
261   Deliver the given message data to the given
262 CCS handler.
263 */
264 static void CcsHandleRequest(CcsImplHeader *hdr,const char *reqData)
265 {
266   char *cmsg;
267   int reqLen=ChMessageInt(hdr->len);
268 /*Look up handler's converse ID*/
269   char *handlerStr=hdr->handler;
270   CcsHandlerRec *fn=(CcsHandlerRec *)CkHashtableGet(CpvAccess(ccsTab),(void *)&handlerStr);
271   if (fn==NULL) {
272     CmiPrintf("CCS: Unknown CCS handler name '%s' requested. Ignoring...\n",
273               hdr->handler);
274     CpvAccess(ccsReq)=hdr;
275     CcsSendReply(0,NULL); /*Send an empty reply to the possibly waiting client*/
276     return;
277  /*   CmiAbort("CCS: Unknown CCS handler name.\n");*/
278   }
279
280 /* Call the handler */
281   CpvAccess(ccsReq)=hdr;
282   callHandlerRec(fn,reqLen,reqData);
283   
284 /*Check if a reply was sent*/
285   if (CpvAccess(ccsReq)!=NULL)
286     CcsSendReply(0,NULL);/*Send an empty reply if not*/
287 }
288
289 /*Unpacks request message to call above routine*/
290 int _ccsHandlerIdx = 0;/*Converse handler index of below routine*/
291 static void req_fw_handler(char *msg)
292 {
293   int offset = CmiMsgHeaderSizeBytes + sizeof(CcsImplHeader);
294   CcsImplHeader *hdr = (CcsImplHeader *)(msg+CmiMsgHeaderSizeBytes);
295   int destPE = (int)ChMessageInt(hdr->pe);
296   if (CmiMyPe() == 0 && destPE == -1) {
297     /* Broadcast message to all other processors */
298     int len=CmiMsgHeaderSizeBytes+sizeof(CcsImplHeader)+ChMessageInt(hdr->len);
299     CmiSyncBroadcast(len, msg);
300   }
301   else if (destPE < -1) {
302     /* Multicast the message to your children */
303     int len=CmiMsgHeaderSizeBytes+sizeof(CcsImplHeader)+ChMessageInt(hdr->len)-destPE*sizeof(ChMessageInt_t);
304     int index, child, i;
305     int *pes = (int*)(msg+CmiMsgHeaderSizeBytes+sizeof(CcsImplHeader));
306     ChMessageInt_t *pes_nbo = (ChMessageInt_t *)pes;
307     offset -= destPE * sizeof(ChMessageInt_t);
308     if (ChMessageInt(pes_nbo[0]) == CmiMyPe()) {
309       for (index=0; index<-destPE; ++index) pes[index] = ChMessageInt(pes_nbo[index]);
310     }
311     for (index=0; index<-destPE; ++index) {
312       if (pes[index] == CmiMyPe()) break;
313     }
314     child = (index << 2) + 1;
315     for (i=0; i<4; ++i) {
316       if (child+i < -destPE) {
317         CmiSyncSend(pes[child+i], len, msg);
318       }
319     }
320   }
321   CcsHandleRequest(hdr, msg+offset);
322   CmiFree(msg);
323 }
324
325 #if ! NODE_0_IS_CONVHOST
326 /* The followings are necessary to prevent CCS requests to be processed before
327  * CCS has been initialized. Really it matters only when NODE_0_IS_CONVHOST=0, but
328  * it doesn't hurt having it in the other case as well */
329 static char **bufferedMessages = NULL;
330 static int CcsNumBufferedMsgs = 0;
331 #define CCS_MAX_NUM_BUFFERED_MSGS  100
332 #endif
333
334 /*Convert CCS header & message data into a converse message 
335  addressed to handler*/
336 char *CcsImpl_ccs2converse(const CcsImplHeader *hdr,const void *data,int *ret_len)
337 {
338   int reqLen=ChMessageInt(hdr->len);
339   int destPE = ChMessageInt(hdr->pe);
340   int len;
341   char *msg;
342   if (destPE < -1) reqLen -= destPE*sizeof(int);
343   len=CmiMsgHeaderSizeBytes+sizeof(CcsImplHeader)+reqLen;
344   msg=(char *)CmiAlloc(len);
345   memcpy(msg+CmiMsgHeaderSizeBytes,hdr,sizeof(CcsImplHeader));
346   memcpy(msg+CmiMsgHeaderSizeBytes+sizeof(CcsImplHeader),data,reqLen);
347   if (ret_len!=NULL) *ret_len=len;
348   if (_ccsHandlerIdx != 0) {
349     CmiSetHandler(msg, _ccsHandlerIdx);
350     return msg;
351   } else {
352 #if NODE_0_IS_CONVHOST
353     CmiAbort("Why do we need to buffer messages when node 0 is Convhost?");
354 #else
355     //CmiPrintf("Buffering CCS message\n");
356     CmiAssert(CcsNumBufferedMsgs < CCS_MAX_NUM_BUFFERED_MSGS);
357     if (CcsNumBufferedMsgs < 0) CmiAbort("Why is a CCS message being buffered now???");
358     if (bufferedMessages == NULL) bufferedMessages = malloc(sizeof(char*)*CCS_MAX_NUM_BUFFERED_MSGS);
359     bufferedMessages[CcsNumBufferedMsgs] = msg;
360     CcsNumBufferedMsgs ++;
361     return NULL;
362 #endif
363   }
364 }
365
366 /*Receives reply messages passed up from
367 converse to node 0.*/
368 static void rep_fw_handler(char *msg)
369 {
370   int len;
371   char *r=msg+CmiMsgHeaderSizeBytes;
372   CcsImplHeader *hdr=(CcsImplHeader *)r; 
373   r+=sizeof(CcsImplHeader);
374   len=ChMessageInt(hdr->len);
375   CcsImpl_reply(hdr,len,r);
376   CmiFree(msg);
377 }
378
379 #if NODE_0_IS_CONVHOST
380 /************** NODE_0_IS_CONVHOST ***********
381 Non net- versions of charm++ are run without a 
382 (real) conv-host program.  This is fine, except 
383 CCS clients connect via conv-host; so for CCS
384 on non-net- versions of charm++, node 0 carries
385 out the CCS forwarding normally done in conv-host.
386
387 CCS works by listening to a TCP connection on a 
388 port-- the Ccs server socket.  A typical communcation
389 pattern is:
390
391 1.) Random program (CCS client) from the net
392 connects to the CCS server socket and sends
393 a CCS request.
394
395 2.) Node 0 forwards the request to the proper
396 PE as a regular converse message (built in CcsImpl_netReq)
397 for CcsHandleRequest.
398
399 3.) CcsHandleRequest looks up the user's pre-registered
400 CCS handler, and passes the user's handler the request data.
401
402 4.) The user's handler calls CcsSendReply with some
403 reply data; OR finishes without calling CcsSendReply,
404 in which case CcsHandleRequest does it.
405
406 5.) CcsSendReply forwards the reply back to node 0,
407 which sends the reply back to the original requestor,
408 on the (still-open) request socket.
409  */
410
411 /**
412 Send a Ccs reply back to the requestor, down the given socket.
413 Since there is no conv-host, node 0 does all the CCS 
414 communication-- this means all requests come to node 0
415 and are forwarded out; all replies are forwarded back to node 0.
416
417 Note: on Net- versions, CcsImpl_reply is implemented in machine.c
418 */
419 void CcsImpl_reply(CcsImplHeader *rep,int repLen,const void *repData)
420 {
421   const int repPE=0;
422   rep->len=ChMessageInt_new(repLen);
423   if (CmiMyPe()==repPE) {
424     /*Actually deliver reply data*/
425     CcsServer_sendReply(rep,repLen,repData);
426   } else {
427     /*Forward data & socket # to the replyPE*/
428     int len=CmiMsgHeaderSizeBytes+
429            sizeof(CcsImplHeader)+repLen;
430     char *msg=CmiAlloc(len);
431     char *r=msg+CmiMsgHeaderSizeBytes;
432     *(CcsImplHeader *)r=*rep; r+=sizeof(CcsImplHeader);
433     memcpy(r,repData,repLen);
434     CmiSetHandler(msg,rep_fw_handler_idx);
435     CmiSyncSendAndFree(repPE,len,msg);
436   }
437 }
438
439 /*No request will be sent through this socket.
440 Closes it.
441 */
442 /*void CcsImpl_noReply(CcsImplHeader *hdr)
443 {
444   int fd=ChMessageInt(hdr->replyFd);
445   skt_close(fd);
446 }*/
447
448 /**
449  * This is the entrance point of a CCS request into the server.
450  * It is executed only on proc 0, and it forwards the request to the appropriate PE.
451  */
452 void CcsImpl_netRequest(CcsImplHeader *hdr,const void *reqData)
453 {
454   char *msg;
455   int len,repPE=ChMessageInt(hdr->pe);
456   if (repPE<=-CmiNumPes() || repPE>=CmiNumPes()) {
457     /*Treat out of bound values as errors. Helps detecting bugs*/
458     if (repPE==-CmiNumPes()) CmiPrintf("Invalid processor index in CCS request: are you trying to do a broadcast instead?");
459     else CmiPrintf("Invalid processor index in CCS request.");
460     CpvAccess(ccsReq)=hdr;
461     CcsSendReply(0,NULL); /*Send an empty reply to the possibly waiting client*/
462     return;
463   }
464
465   msg=CcsImpl_ccs2converse(hdr,reqData,&len);
466   if (repPE >= 0) {
467     CmiSyncSendAndFree(repPE,len,msg);
468   } else if (repPE == -1) {
469     /* Broadcast to all processors */
470     CmiPushPE(0, msg);
471   } else {
472     /* Multicast to -repPE processors, specified right at the beginning of reqData (as a list of pes) */
473     int firstPE = ChMessageInt(*(ChMessageInt_t*)reqData);
474     CmiSyncSendAndFree(firstPE,len,msg);
475   }
476 }
477
478 /*
479 We have to run a CCS server socket here on
480 node 0.  To keep the speed impact minimal,
481 we only probe for new connections (with CcsServerCheck)
482 occasionally.  
483  */
484 #include <signal.h>
485 #include "ccs-server.c" /*Include implementation here in this case*/
486 #include "ccs-auth.c"
487
488 /*Check for ready Ccs messages:*/
489 void CcsServerCheck(void)
490 {
491   while (1==skt_select1(CcsServer_fd(),0)) {
492     CcsImplHeader hdr;
493     void *data;
494     /* printf("Got CCS connect...\n"); */
495     if (CcsServer_recvRequest(&hdr,&data))
496     {/*We got a network request*/
497       /* printf("Got CCS request...\n"); */
498       if (! check_stdio_header(&hdr)) {
499         CcsImpl_netRequest(&hdr,data);
500       }
501       free(data);
502     }
503   }
504 }
505
506 #endif /*NODE_0_IS_CONVHOST*/
507
508 int _isCcsHandlerIdx(int hIdx) {
509   if (hIdx==_ccsHandlerIdx) return 1;
510   if (hIdx==rep_fw_handler_idx) return 1;
511   return 0;
512 }
513
514 void CcsBuiltinsInit(char **argv);
515
516 CpvDeclare(int, cmiArgDebugFlag);
517 CpvDeclare(char *, displayArgument);
518 CpvDeclare(int, cpdSuspendStartup);
519
520 void CcsInit(char **argv)
521 {
522   CpvInitialize(CkHashtable_c, ccsTab);
523   CpvAccess(ccsTab) = CkCreateHashtable_string(sizeof(CcsHandlerRec),5);
524   CpvInitialize(CcsImplHeader *, ccsReq);
525   CpvAccess(ccsReq) = NULL;
526   _ccsHandlerIdx = CmiRegisterHandler((CmiHandler)req_fw_handler);
527   CpvInitialize(int, cmiArgDebugFlag);
528   CpvInitialize(char *, displayArgument);
529   CpvInitialize(int, cpdSuspendStartup);
530   CpvAccess(cmiArgDebugFlag) = 0;
531   CpvAccess(displayArgument) = NULL;
532   CpvAccess(cpdSuspendStartup) = 0;
533   
534   CcsBuiltinsInit(argv);
535
536   rep_fw_handler_idx = CmiRegisterHandler((CmiHandler)rep_fw_handler);
537 #if NODE_0_IS_CONVHOST
538 #if ! CMK_CMIPRINTF_IS_A_BUILTIN
539   print_fw_handler_idx = CmiRegisterHandler((CmiHandler)print_fw_handler);
540 #endif
541   {
542    int ccs_serverPort=0;
543    char *ccs_serverAuth=NULL;
544    
545    if (CmiGetArgFlagDesc(argv,"++server", "Create a CCS server port") | 
546       CmiGetArgIntDesc(argv,"++server-port",&ccs_serverPort, "Listen on this TCP/IP port number") |
547       CmiGetArgStringDesc(argv,"++server-auth",&ccs_serverAuth, "Use this CCS authentication file")) 
548     if (CmiMyPe()==0)
549     {/*Create and occasionally poll on a CCS server port*/
550       CcsServer_new(NULL,&ccs_serverPort,ccs_serverAuth);
551       CcdCallOnConditionKeep(CcdPERIODIC,(CcdVoidFn)CcsServerCheck,NULL);
552     }
553   }
554 #endif
555   /* if in parallel debug mode i.e ++cpd, freeze */
556   if (CmiGetArgFlagDesc(argv, "+cpd", "Used *only* in conjunction with parallel debugger"))
557   {
558      CpvAccess(cmiArgDebugFlag) = 1;
559      if (CmiGetArgStringDesc(argv, "+DebugDisplay",&(CpvAccess(displayArgument)), "X display for gdb used only in cpd mode"))
560      {
561         if (CpvAccess(displayArgument) == NULL)
562             CmiPrintf("WARNING> NULL parameter for +DebugDisplay\n***");
563      }
564      else if (CmiMyPe() == 0)
565      {
566             /* only one processor prints the warning */
567             CmiPrintf("WARNING> x term for gdb needs to be specified as +DebugDisplay by debugger\n***\n");
568      }
569
570      if (CmiGetArgFlagDesc(argv, "+DebugSuspend", "Suspend execution at beginning of program")) {
571        CpvAccess(cpdSuspendStartup) = 1;
572      }
573   }
574
575 #if ! NODE_0_IS_CONVHOST
576   if (CcsNumBufferedMsgs > 0) {
577     int i;
578     for (i=0; i<CcsNumBufferedMsgs; ++i) {
579       CmiSetHandler(bufferedMessages[i], _ccsHandlerIdx);
580       CmiPushPE(0, bufferedMessages[i]);
581     }
582     free(bufferedMessages);
583     bufferedMessages = NULL;
584     CcsNumBufferedMsgs = -1;
585   }
586 #endif
587 }
588
589 #endif /*CMK_CCS_AVAILABLE*/
590