Bug #647 follow-on: Avoid pure virtual methods in NDMeshStreamer definition
[charm.git] / src / conv-ccs / conv-ccs.c
blobb39895872ffd60feb4de8e020aebedbc3cc934f6
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <errno.h>
4 #include <string.h>
6 #include "converse.h"
7 #include "conv-ccs.h"
8 #include "ccs-server.h"
9 #include "sockRoutines.h"
10 #include "queueing.h"
12 void CpdEndConditionalDeliver_master(void);
14 #if CMK_CCS_AVAILABLE
16 int CcsReply(CcsImplHeader *rep,int repLen,const void *repData);
18 /*****************************************************************************
20 * Converse Client-Server Functions
22 *****************************************************************************/
24 static void initHandlerRec(CcsHandlerRec *c,const char *name) {
25 if (strlen(name)>=CCS_MAXHANDLER)
26 CmiAbort("CCS handler names cannot exceed 32 characters");
27 c->name=strdup(name);
28 c->fn=NULL;
29 c->fnOld=NULL;
30 c->userPtr=NULL;
31 c->mergeFn=NULL;
32 c->nCalls=0;
35 static void callHandlerRec(CcsHandlerRec *c,int reqLen,const void *reqData) {
36 c->nCalls++;
37 if (c->fnOld)
38 { /* Backward compatability version:
39 Pack user data into a converse message (cripes! why bother?);
40 user will delete the message.
42 char *cmsg = (char *) CmiAlloc(CmiReservedHeaderSize+reqLen);
43 memcpy(cmsg+CmiReservedHeaderSize, reqData, reqLen);
44 (c->fnOld)(cmsg);
46 else { /* Pass read-only copy of data straight to user */
47 (c->fn)(c->userPtr, reqLen, reqData);
51 /*Table maps handler name to CcsHandler object*/
52 CpvDeclare(CcsHandlerTable, ccsTab);
54 CpvStaticDeclare(CcsImplHeader*,ccsReq);/*Identifies CCS requestor (client)*/
56 void CcsRegisterHandler(const char *name, CmiHandler fn) {
57 CcsHandlerRec cp;
58 initHandlerRec(&cp,name);
59 cp.fnOld=fn;
60 *(CcsHandlerRec *)CkHashtablePut(CpvAccess(ccsTab),(void *)&cp.name)=cp;
62 void CcsRegisterHandlerFn(const char *name, CcsHandlerFn fn, void *ptr) {
63 CcsHandlerRec cp;
64 initHandlerRec(&cp,name);
65 cp.fn=fn;
66 cp.userPtr=ptr;
67 *(CcsHandlerRec *)CkHashtablePut(CpvAccess(ccsTab),(void *)&cp.name)=cp;
69 CcsHandlerRec *CcsGetHandler(const char *name) {
70 return CkHashtableGet(CpvAccess(ccsTab),(void *)&name);
72 void CcsSetMergeFn(const char *name, CmiReduceMergeFn newMerge) {
73 CcsHandlerRec *rec=(CcsHandlerRec *)CkHashtableGet(CpvAccess(ccsTab),(void *)&name);
74 if (rec==NULL) {
75 CmiAbort("CCS: Unknown CCS handler name.\n");
77 rec->mergeFn=newMerge;
78 rec->redID=CmiGetGlobalReduction();
81 void * CcsMerge_concat(int *size,void *local,void **remote,int n) {
82 CcsImplHeader *hdr;
83 int total = *size;
84 void *reply;
85 char *ptr;
86 int i;
87 for (i=0; i<n; ++i) {
88 hdr = (CcsImplHeader*)(((char*)remote[i])+CmiReservedHeaderSize);
89 total += ChMessageInt(hdr->len);
91 reply = CmiAlloc(total);
92 memcpy(reply, local, *size);
93 ((CcsImplHeader*)(((char*)reply)+CmiReservedHeaderSize))->len = ChMessageInt_new(total-CmiReservedHeaderSize-sizeof(CcsImplHeader));
94 CmiFree(local);
95 ptr = ((char*)reply)+*size;
96 for (i=0; i<n; ++i) {
97 int len = ChMessageInt(((CcsImplHeader*)(((char*)remote[i])+CmiReservedHeaderSize))->len);
98 memcpy(ptr, ((char*)remote[i])+CmiReservedHeaderSize+sizeof(CcsImplHeader), len);
99 ptr += len;
101 *size = total;
102 return reply;
105 #define SIMPLE_REDUCTION(name, dataType, loop) \
106 void * CcsMerge_##name(int *size,void *local,void **remote,int n) { \
107 int i, m; \
108 CcsImplHeader *hdrLocal = (CcsImplHeader*)(((char*)local)+CmiReservedHeaderSize); \
109 int lenLocal = ChMessageInt(hdrLocal->len); \
110 int nElem = lenLocal / sizeof(dataType); \
111 dataType *ret = (dataType *) (hdrLocal+1); \
112 CcsImplHeader *hdr; \
113 for (m=0; m<n; ++m) { \
114 int len; \
115 dataType *value; \
116 hdr = (CcsImplHeader*)(((char*)remote[m])+CmiReservedHeaderSize); \
117 len = ChMessageInt(hdr->len); \
118 value = (dataType *)(hdr+1); \
119 CmiAssert(lenLocal == len); \
120 for (i=0; i<nElem; ++i) loop; \
122 return local; \
125 SIMPLE_REDUCTION(logical_and, int, ret[i]=(ret[i]&&value[i])?1:0)
126 SIMPLE_REDUCTION(logical_or, int, ret[i]=(ret[i]||value[i])?1:0)
127 SIMPLE_REDUCTION(bitvec_and, int, ret[i]&=value[i])
128 SIMPLE_REDUCTION(bitvec_or, int, ret[i]|=value[i])
130 /*Use this macro for reductions that have the same type for all inputs */
131 #define SIMPLE_POLYMORPH_REDUCTION(nameBase,loop) \
132 SIMPLE_REDUCTION(nameBase##_int, int, loop) \
133 SIMPLE_REDUCTION(nameBase##_float, float, loop) \
134 SIMPLE_REDUCTION(nameBase##_double, double, loop)
136 SIMPLE_POLYMORPH_REDUCTION(sum, ret[i]+=value[i])
137 SIMPLE_POLYMORPH_REDUCTION(product, ret[i]*=value[i])
138 SIMPLE_POLYMORPH_REDUCTION(max, if (ret[i]<value[i]) ret[i]=value[i])
139 SIMPLE_POLYMORPH_REDUCTION(min, if (ret[i]>value[i]) ret[i]=value[i])
141 #undef SIMPLE_REDUCTION
142 #undef SIMPLE_POLYMORPH_REDUCTION
144 int CcsEnabled(void)
146 return 1;
149 int CcsIsRemoteRequest(void)
151 return CpvAccess(ccsReq)!=NULL;
154 void CcsCallerId(skt_ip_t *pip, unsigned int *pport)
156 *pip = CpvAccess(ccsReq)->attr.ip;
157 *pport = ChMessageInt(CpvAccess(ccsReq)->attr.port);
160 int rep_fw_handler_idx;
162 CcsDelayedReply CcsDelayReply(void)
164 CcsDelayedReply ret;
165 int len = sizeof(CcsImplHeader);
166 if (ChMessageInt(CpvAccess(ccsReq)->pe) < -1)
167 len += ChMessageInt(CpvAccess(ccsReq)->pe) * sizeof(int);
168 ret.hdr = (CcsImplHeader*)malloc(len);
169 memcpy(ret.hdr, CpvAccess(ccsReq), len);
170 CpvAccess(ccsReq)=NULL;
171 return ret;
174 void CcsSendReply(int replyLen, const void *replyData)
176 if (CpvAccess(ccsReq)==NULL)
177 CmiAbort("CcsSendReply: reply already sent!\n");
178 CpvAccess(ccsReq)->len = ChMessageInt_new(1);
179 CcsReply(CpvAccess(ccsReq),replyLen,replyData);
180 CpvAccess(ccsReq) = NULL;
183 void CcsSendReplyNoError(int replyLen, const void *replyData) {
184 if (CpvAccess(ccsReq)==NULL) return;
185 CcsSendReply(replyLen, replyData);
188 void CcsSendDelayedReply(CcsDelayedReply d,int replyLen, const void *replyData)
190 CcsImplHeader *h = d.hdr;
191 h->len=ChMessageInt_new(1);
192 CcsReply(h,replyLen,replyData);
193 free(h);
196 void CcsNoReply()
198 if (CpvAccess(ccsReq)==NULL) return;
199 CpvAccess(ccsReq)->len = ChMessageInt_new(0);
200 CcsReply(CpvAccess(ccsReq),0,NULL);
201 CpvAccess(ccsReq) = NULL;
204 void CcsNoDelayedReply(CcsDelayedReply d)
206 CcsImplHeader *h = d.hdr;
207 h->len = ChMessageInt_new(0);
208 CcsReply(h,0,NULL);
209 free(h);
213 /**********************************
214 _CCS Implementation Routines:
215 These do the request forwarding and
216 delivery.
217 ***********************************/
219 /*CCS Bottleneck:
220 Deliver the given message data to the given
221 CCS handler.
223 void CcsHandleRequest(CcsImplHeader *hdr,const char *reqData)
225 char *cmsg;
226 int reqLen=ChMessageInt(hdr->len);
227 /*Look up handler's converse ID*/
228 char *handlerStr=hdr->handler;
229 CcsHandlerRec *fn=(CcsHandlerRec *)CkHashtableGet(CpvAccess(ccsTab),(void *)&handlerStr);
230 if (fn==NULL) {
231 CmiPrintf("CCS: Unknown CCS handler name '%s' requested. Ignoring...\n",
232 hdr->handler);
233 CpvAccess(ccsReq)=hdr;
234 CcsSendReply(0,NULL); /*Send an empty reply to the possibly waiting client*/
235 return;
236 /* CmiAbort("CCS: Unknown CCS handler name.\n");*/
239 /* Call the handler */
240 CpvAccess(ccsReq)=hdr;
241 #if CMK_CHARMDEBUG
242 if (conditionalPipe[1]!=0 && _conditionalDelivery==0) {
243 /* We are conditionally delivering, the message has been sent to the child, wait for its response */
244 int bytes;
245 if (4==read(conditionalPipe[0], &bytes, 4)) {
246 char *buf = malloc(bytes);
247 read(conditionalPipe[0], buf, bytes);
248 CcsSendReply(bytes,buf);
249 free(buf);
250 } else {
251 /* the pipe has been closed */
252 CpdEndConditionalDeliver_master();
255 else
256 #endif
258 callHandlerRec(fn,reqLen,reqData);
260 /*Check if a reply was sent*/
261 if (CpvAccess(ccsReq)!=NULL)
262 CcsSendReply(0,NULL);/*Send an empty reply if not*/
266 #if ! NODE_0_IS_CONVHOST || CMK_BIGSIM_CHARM
267 /* The followings are necessary to prevent CCS requests to be processed before
268 * CCS has been initialized. Really it matters only when NODE_0_IS_CONVHOST=0, but
269 * it doesn't hurt having it in the other case as well */
270 static char **bufferedMessages = NULL;
271 static int CcsNumBufferedMsgs = 0;
272 #define CCS_MAX_NUM_BUFFERED_MSGS 100
274 void CcsBufferMessage(char *msg) {
275 CmiPrintf("Buffering CCS message\n");
276 CmiAssert(CcsNumBufferedMsgs < CCS_MAX_NUM_BUFFERED_MSGS);
277 if (CcsNumBufferedMsgs < 0) CmiAbort("Why is a CCS message being buffered now???");
278 if (bufferedMessages == NULL) bufferedMessages = malloc(sizeof(char*)*CCS_MAX_NUM_BUFFERED_MSGS);
279 bufferedMessages[CcsNumBufferedMsgs] = msg;
280 CcsNumBufferedMsgs ++;
282 #endif
284 /*Unpacks request message to call above routine*/
285 int _ccsHandlerIdx = 0;/*Converse handler index of routine req_fw_handler*/
287 #if CMK_BIGSIM_CHARM
288 CpvDeclare(int, _bgCcsHandlerIdx);
289 CpvDeclare(int, _bgCcsAck);
290 /* This routine is needed when the application is built on top of the bigemulator
291 * layer of Charm. In this case, the real CCS handler must be called within a
292 * worker thread. The function of this function is to receive the CCS message in
293 * the bottom converse layer and forward it to the emulated layer. */
294 static void bg_req_fw_handler(char *msg) {
295 /* Get out of the message who is the destination pe */
296 int offset = CmiReservedHeaderSize + sizeof(CcsImplHeader);
297 CcsImplHeader *hdr = (CcsImplHeader *)(msg+CmiReservedHeaderSize);
298 int destPE = (int)ChMessageInt(hdr->pe);
299 if (CpvAccess(_bgCcsAck) < BgNodeSize()) {
300 CcsBufferMessage(msg);
301 return;
303 //CmiPrintf("CCS scheduling message\n");
304 if (destPE == -1) destPE = 0;
305 if (destPE < -1) {
306 ChMessageInt_t *pes_nbo = (ChMessageInt_t *)(msg+CmiReservedHeaderSize+sizeof(CcsImplHeader));
307 destPE = ChMessageInt(pes_nbo[0]);
309 //CmiAssert(destPE >= 0); // FixME: should cover also broadcast and multicast -> create generic function to extract destpe
310 (((CmiBlueGeneMsgHeader*)msg)->tID) = 0;
311 (((CmiBlueGeneMsgHeader*)msg)->n) = 0;
312 (((CmiBlueGeneMsgHeader*)msg)->flag) = 0;
313 (((CmiBlueGeneMsgHeader*)msg)->t) = 0;
314 (((CmiBlueGeneMsgHeader*)msg)->hID) = CpvAccess(_bgCcsHandlerIdx);
315 /* Get the right thread to deliver to (for now assume it is using CyclicMapInfo) */
316 addBgNodeInbuffer(msg, destPE/CmiNumPes());
317 //CmiPrintf("message CCS added %d to %d\n",((CmiBlueGeneMsgHeader*)msg)->hID, ((CmiBlueGeneMsgHeader*)msg)->tID);
319 #define req_fw_handler bg_req_fw_handler
320 #endif
321 extern void req_fw_handler(char *msg);
323 void CcsReleaseMessages() {
324 #if ! NODE_0_IS_CONVHOST || CMK_BIGSIM_CHARM
325 #if CMK_BIGSIM_CHARM
326 if (CpvAccess(_bgCcsAck) == 0 || CpvAccess(_bgCcsAck) < BgNodeSize()) return;
327 #endif
328 if (CcsNumBufferedMsgs > 0) {
329 int i;
330 //CmiPrintf("CCS: %d messages released\n",CcsNumBufferedMsgs);
331 for (i=0; i<CcsNumBufferedMsgs; ++i) {
332 CmiSetHandler(bufferedMessages[i], _ccsHandlerIdx);
333 CsdEnqueue(bufferedMessages[i]);
335 free(bufferedMessages);
336 bufferedMessages = NULL;
337 CcsNumBufferedMsgs = -1;
339 #endif
342 /*Convert CCS header & message data into a converse message
343 addressed to handler*/
344 char *CcsImpl_ccs2converse(const CcsImplHeader *hdr,const void *data,int *ret_len)
346 int reqLen=ChMessageInt(hdr->len);
347 int destPE = ChMessageInt(hdr->pe);
348 int len;
349 char *msg;
350 if (destPE < -1) reqLen -= destPE*sizeof(int);
351 len=CmiReservedHeaderSize+sizeof(CcsImplHeader)+reqLen;
352 msg=(char *)CmiAlloc(len);
353 memcpy(msg+CmiReservedHeaderSize,hdr,sizeof(CcsImplHeader));
354 memcpy(msg+CmiReservedHeaderSize+sizeof(CcsImplHeader),data,reqLen);
355 if (ret_len!=NULL) *ret_len=len;
356 if (_ccsHandlerIdx != 0) {
357 CmiSetHandler(msg, _ccsHandlerIdx);
358 return msg;
359 } else {
360 #if NODE_0_IS_CONVHOST
361 CmiAbort("Why do we need to buffer messages when node 0 is Convhost?");
362 #else
363 CcsBufferMessage(msg);
364 return NULL;
365 #endif
369 /*Receives reply messages passed up from
370 converse to node 0.*/
371 static void rep_fw_handler(char *msg)
373 int len;
374 char *r=msg+CmiReservedHeaderSize;
375 CcsImplHeader *hdr=(CcsImplHeader *)r;
376 r+=sizeof(CcsImplHeader);
377 len=ChMessageInt(hdr->len);
378 CcsImpl_reply(hdr,len,r);
379 CmiFree(msg);
382 #if NODE_0_IS_CONVHOST
383 /************** NODE_0_IS_CONVHOST ***********
384 Non net- versions of charm++ are run without a
385 (real) conv-host program. This is fine, except
386 CCS clients connect via conv-host; so for CCS
387 on non-net- versions of charm++, node 0 carries
388 out the CCS forwarding normally done in conv-host.
390 CCS works by listening to a TCP connection on a
391 port-- the Ccs server socket. A typical communcation
392 pattern is:
394 1.) Random program (CCS client) from the net
395 connects to the CCS server socket and sends
396 a CCS request.
398 2.) Node 0 forwards the request to the proper
399 PE as a regular converse message (built in CcsImpl_netReq)
400 for CcsHandleRequest.
402 3.) CcsHandleRequest looks up the user's pre-registered
403 CCS handler, and passes the user's handler the request data.
405 4.) The user's handler calls CcsSendReply with some
406 reply data; OR finishes without calling CcsSendReply,
407 in which case CcsHandleRequest does it.
409 5.) CcsSendReply forwards the reply back to node 0,
410 which sends the reply back to the original requestor,
411 on the (still-open) request socket.
415 Send a Ccs reply back to the requestor, down the given socket.
416 Since there is no conv-host, node 0 does all the CCS
417 communication-- this means all requests come to node 0
418 and are forwarded out; all replies are forwarded back to node 0.
420 Note: on Net- versions, CcsImpl_reply is implemented in machine.c
422 void CcsImpl_reply(CcsImplHeader *rep,int repLen,const void *repData)
424 const int repPE=0;
425 rep->len=ChMessageInt_new(repLen);
426 if (CmiMyPe()==repPE) {
427 /*Actually deliver reply data*/
428 CcsServer_sendReply(rep,repLen,repData);
429 } else {
430 /*Forward data & socket # to the replyPE*/
431 int len=CmiReservedHeaderSize+
432 sizeof(CcsImplHeader)+repLen;
433 char *msg=CmiAlloc(len);
434 char *r=msg+CmiReservedHeaderSize;
435 *(CcsImplHeader *)r=*rep; r+=sizeof(CcsImplHeader);
436 memcpy(r,repData,repLen);
437 CmiSetHandler(msg,rep_fw_handler_idx);
438 CmiSyncSendAndFree(repPE,len,msg);
442 /*No request will be sent through this socket.
443 Closes it.
445 /*void CcsImpl_noReply(CcsImplHeader *hdr)
447 int fd=ChMessageInt(hdr->replyFd);
448 skt_close(fd);
452 * This is the entrance point of a CCS request into the server.
453 * It is executed only on proc 0, and it forwards the request to the appropriate PE.
455 void CcsImpl_netRequest(CcsImplHeader *hdr,const void *reqData)
457 char *msg;
458 int len,repPE=ChMessageInt(hdr->pe);
459 if (repPE<=-CmiNumPes() || repPE>=CmiNumPes()) {
460 #if ! CMK_BIGSIM_CHARM
461 /*Treat out of bound values as errors. Helps detecting bugs*/
462 if (repPE==-CmiNumPes()) CmiPrintf("Invalid processor index in CCS request: are you trying to do a broadcast instead?");
463 else CmiPrintf("Invalid processor index in CCS request.");
464 CpvAccess(ccsReq)=hdr;
465 CcsSendReply(0,NULL); /*Send an empty reply to the possibly waiting client*/
466 return;
467 #endif
470 msg=CcsImpl_ccs2converse(hdr,reqData,&len);
471 if (repPE >= 0) {
472 /* The following %CmiNumPes() follows the assumption that in BigSim the mapping is round-robin */
473 //CmiPrintf("CCS message received for %d\n",repPE);
474 CmiSyncSendAndFree(repPE%CmiNumPes(),len,msg);
475 } else if (repPE == -1) {
476 /* Broadcast to all processors */
477 //CmiPrintf("CCS broadcast received\n");
478 CmiSyncSendAndFree(0,len,msg);
479 } else {
480 /* Multicast to -repPE processors, specified right at the beginning of reqData (as a list of pes) */
481 int firstPE = ChMessageInt(*(ChMessageInt_t*)reqData);
482 /* The following %CmiNumPes() follows the assumption that in BigSim the mapping is round-robin */
483 //CmiPrintf("CCS multicast received\n");
484 CmiSyncSendAndFree(firstPE%CmiNumPes(),len,msg);
489 We have to run a CCS server socket here on
490 node 0. To keep the speed impact minimal,
491 we only probe for new connections (with CcsServerCheck)
492 occasionally.
494 #include <signal.h>
495 #include "ccs-server.c" /*Include implementation here in this case*/
496 #include "ccs-auth.c"
498 /*Check for ready Ccs messages:*/
499 void CcsServerCheck(void)
501 while (1==skt_select1(CcsServer_fd(),0)) {
502 CcsImplHeader hdr;
503 void *data;
504 /* printf("Got CCS connect...\n"); */
505 if (CcsServer_recvRequest(&hdr,&data))
506 {/*We got a network request*/
507 /* printf("Got CCS request...\n"); */
508 if (! check_stdio_header(&hdr)) {
509 CcsImpl_netRequest(&hdr,data);
511 free(data);
516 #endif /*NODE_0_IS_CONVHOST*/
518 int _isCcsHandlerIdx(int hIdx) {
519 if (hIdx==_ccsHandlerIdx) return 1;
520 if (hIdx==rep_fw_handler_idx) return 1;
521 return 0;
524 void CcsBuiltinsInit(char **argv);
526 CpvDeclare(int, cmiArgDebugFlag);
527 CpvDeclare(char *, displayArgument);
528 CpvDeclare(int, cpdSuspendStartup);
530 void CcsInit(char **argv)
532 CpvInitialize(CkHashtable_c, ccsTab);
533 CpvAccess(ccsTab) = CkCreateHashtable_string(sizeof(CcsHandlerRec),5);
534 CpvInitialize(CcsImplHeader *, ccsReq);
535 CpvAccess(ccsReq) = NULL;
536 _ccsHandlerIdx = CmiRegisterHandler((CmiHandler)req_fw_handler);
537 #if CMK_BIGSIM_CHARM
538 CpvInitialize(int, _bgCcsHandlerIdx);
539 CpvAccess(_bgCcsHandlerIdx) = 0;
540 CpvInitialize(int, _bgCcsAck);
541 CpvAccess(_bgCcsAck) = 0;
542 #endif
543 CpvInitialize(char *, displayArgument);
544 CpvInitialize(int, cpdSuspendStartup);
545 CpvAccess(displayArgument) = NULL;
546 CpvAccess(cpdSuspendStartup) = 0;
548 CcsBuiltinsInit(argv);
550 rep_fw_handler_idx = CmiRegisterHandler((CmiHandler)rep_fw_handler);
551 #if NODE_0_IS_CONVHOST
552 #if ! CMK_CMIPRINTF_IS_A_BUILTIN
553 print_fw_handler_idx = CmiRegisterHandler((CmiHandler)print_fw_handler);
554 #endif
556 int ccs_serverPort=0;
557 char *ccs_serverAuth=NULL;
559 if (CmiGetArgFlagDesc(argv,"++server", "Create a CCS server port") |
560 CmiGetArgIntDesc(argv,"++server-port",&ccs_serverPort, "Listen on this TCP/IP port number") |
561 CmiGetArgStringDesc(argv,"++server-auth",&ccs_serverAuth, "Use this CCS authentication file"))
562 if (CmiMyPe()==0)
563 {/*Create and occasionally poll on a CCS server port*/
564 CcsServer_new(NULL,&ccs_serverPort,ccs_serverAuth);
565 CcdCallOnConditionKeep(CcdPERIODIC,(CcdVoidFn)CcsServerCheck,NULL);
568 #endif
569 /* if in parallel debug mode i.e ++cpd, freeze */
570 if (CmiGetArgFlagDesc(argv, "+cpd", "Used *only* in conjunction with parallel debugger"))
572 if(CmiMyRank() == 0) CpvAccess(cmiArgDebugFlag) = 1;
573 if (CmiGetArgStringDesc(argv, "+DebugDisplay",&(CpvAccess(displayArgument)), "X display for gdb used only in cpd mode"))
575 if (CpvAccess(displayArgument) == NULL)
576 CmiPrintf("WARNING> NULL parameter for +DebugDisplay\n***");
578 else if (CmiMyPe() == 0)
580 /* only one processor prints the warning */
581 CmiPrintf("WARNING> x term for gdb needs to be specified as +DebugDisplay by debugger\n***\n");
584 if (CmiGetArgFlagDesc(argv, "+DebugSuspend", "Suspend execution at beginning of program")) {
585 if(CmiMyRank() == 0) CpvAccess(cpdSuspendStartup) = 1;
589 CcsReleaseMessages();
592 #endif /*CMK_CCS_AVAILABLE*/