Using shmem to provide consistency and allow the un-delivery of a user-specified...
[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 static void initHandlerRec(CcsHandlerRec *c,const char *name) {
28   if (strlen(name)>=CCS_MAXHANDLER) 
29         CmiAbort("CCS handler names cannot exceed 32 characters");
30   c->name=strdup(name);
31   c->fn=NULL;
32   c->fnOld=NULL;
33   c->userPtr=NULL;
34   c->mergeFn=NULL;
35   c->nCalls=0;
36 }
37
38 static void callHandlerRec(CcsHandlerRec *c,int reqLen,const void *reqData) {
39         c->nCalls++;
40         if (c->fnOld) 
41         { /* Backward compatability version:
42             Pack user data into a converse message (cripes! why bother?);
43             user will delete the message. 
44           */
45                 char *cmsg = (char *) CmiAlloc(CmiReservedHeaderSize+reqLen);
46                 memcpy(cmsg+CmiReservedHeaderSize, reqData, reqLen);
47                 (c->fnOld)(cmsg);
48         }
49         else { /* Pass read-only copy of data straight to user */
50                 (c->fn)(c->userPtr, reqLen, reqData);
51         }
52 }
53
54 /*Table maps handler name to CcsHandler object*/
55 CpvDeclare(CcsHandlerTable, ccsTab);
56
57 CpvStaticDeclare(CcsImplHeader*,ccsReq);/*Identifies CCS requestor (client)*/
58
59 void CcsRegisterHandler(const char *name, CmiHandler fn) {
60   CcsHandlerRec cp;
61   initHandlerRec(&cp,name);
62   cp.fnOld=fn;
63   *(CcsHandlerRec *)CkHashtablePut(CpvAccess(ccsTab),(void *)&cp.name)=cp;
64 }
65 void CcsRegisterHandlerFn(const char *name, CcsHandlerFn fn, void *ptr) {
66   CcsHandlerRec cp;
67   initHandlerRec(&cp,name);
68   cp.fn=fn;
69   cp.userPtr=ptr;
70   *(CcsHandlerRec *)CkHashtablePut(CpvAccess(ccsTab),(void *)&cp.name)=cp;
71 }
72 CcsHandlerRec *CcsGetHandler(const char *name) {
73   return CkHashtableGet(CpvAccess(ccsTab),(void *)&name);
74 }
75 void CcsSetMergeFn(const char *name, CmiReduceMergeFn newMerge) {
76   CcsHandlerRec *rec=(CcsHandlerRec *)CkHashtableGet(CpvAccess(ccsTab),(void *)&name);
77   if (rec==NULL) {
78     CmiAbort("CCS: Unknown CCS handler name.\n");
79   }
80   rec->mergeFn=newMerge;
81   rec->redID=CmiGetGlobalReduction();
82 }
83
84 void * CcsMerge_concat(int *size,void *local,void **remote,int n) {
85   CcsImplHeader *hdr;
86   int total = *size;
87   void *reply;
88   char *ptr;
89   int i;
90   for (i=0; i<n; ++i) {
91     hdr = (CcsImplHeader*)(((char*)remote[i])+CmiReservedHeaderSize);
92     total += ChMessageInt(hdr->len);
93   }
94   reply = CmiAlloc(total);
95   memcpy(reply, local, *size);
96   ((CcsImplHeader*)(((char*)reply)+CmiReservedHeaderSize))->len = ChMessageInt_new(total-CmiReservedHeaderSize-sizeof(CcsImplHeader));
97   CmiFree(local);
98   ptr = ((char*)reply)+*size;
99   for (i=0; i<n; ++i) {
100     int len = ChMessageInt(((CcsImplHeader*)(((char*)remote[i])+CmiReservedHeaderSize))->len);
101     memcpy(ptr, ((char*)remote[i])+CmiReservedHeaderSize+sizeof(CcsImplHeader), len);
102     ptr += len;
103   }
104   *size = total;
105   return reply;
106 }
107
108 #define SIMPLE_REDUCTION(name, dataType, loop) \
109 void * CcsMerge_##name(int *size,void *local,void **remote,int n) { \
110   int i, m; \
111   CcsImplHeader *hdrLocal = (CcsImplHeader*)(((char*)local)+CmiReservedHeaderSize); \
112   int lenLocal = ChMessageInt(hdrLocal->len); \
113   int nElem = lenLocal / sizeof(dataType); \
114   dataType *ret = (dataType *) (hdrLocal+1); \
115   CcsImplHeader *hdr; \
116   for (m=0; m<n; ++m) { \
117     int len; \
118     dataType *value; \
119     hdr = (CcsImplHeader*)(((char*)remote[m])+CmiReservedHeaderSize); \
120     len = ChMessageInt(hdr->len); \
121     value = (dataType *)(hdr+1); \
122     CmiAssert(lenLocal == len); \
123     for (i=0; i<nElem; ++i) loop; \
124   } \
125   return local; \
126 }
127
128 SIMPLE_REDUCTION(logical_and, int, ret[i]=(ret[i]&&value[i])?1:0)
129 SIMPLE_REDUCTION(logical_or, int, ret[i]=(ret[i]||value[i])?1:0)
130 SIMPLE_REDUCTION(bitvec_and, int, ret[i]&=value[i])
131 SIMPLE_REDUCTION(bitvec_or, int, ret[i]|=value[i])
132
133 /*Use this macro for reductions that have the same type for all inputs */
134 #define SIMPLE_POLYMORPH_REDUCTION(nameBase,loop) \
135   SIMPLE_REDUCTION(nameBase##_int, int, loop) \
136   SIMPLE_REDUCTION(nameBase##_float, float, loop) \
137   SIMPLE_REDUCTION(nameBase##_double, double, loop)
138
139 SIMPLE_POLYMORPH_REDUCTION(sum, ret[i]+=value[i])
140 SIMPLE_POLYMORPH_REDUCTION(product, ret[i]*=value[i])
141 SIMPLE_POLYMORPH_REDUCTION(max, if (ret[i]<value[i]) ret[i]=value[i])
142 SIMPLE_POLYMORPH_REDUCTION(min, if (ret[i]>value[i]) ret[i]=value[i])
143
144 #undef SIMPLE_REDUCTION
145 #undef SIMPLE_POLYMORPH_REDUCTION
146
147 int CcsEnabled(void)
148 {
149   return 1;
150 }
151
152 int CcsIsRemoteRequest(void)
153 {
154   return CpvAccess(ccsReq)!=NULL;
155 }
156
157 void CcsCallerId(skt_ip_t *pip, unsigned int *pport)
158 {
159   *pip = CpvAccess(ccsReq)->attr.ip;
160   *pport = ChMessageInt(CpvAccess(ccsReq)->attr.port);
161 }
162
163 int rep_fw_handler_idx;
164
165 CcsDelayedReply CcsDelayReply(void)
166 {
167   CcsDelayedReply ret;
168   int len = sizeof(CcsImplHeader);
169   if (ChMessageInt(CpvAccess(ccsReq)->pe) < -1)
170     len += ChMessageInt(CpvAccess(ccsReq)->pe) * sizeof(int);
171   ret.hdr = (CcsImplHeader*)malloc(len);
172   memcpy(ret.hdr, CpvAccess(ccsReq), len);
173   CpvAccess(ccsReq)=NULL;
174   return ret;
175 }
176
177 void CcsSendReply(int replyLen, const void *replyData)
178 {
179   if (CpvAccess(ccsReq)==NULL)
180     CmiAbort("CcsSendReply: reply already sent!\n");
181   CpvAccess(ccsReq)->len = ChMessageInt_new(1);
182   CcsReply(CpvAccess(ccsReq),replyLen,replyData);
183   CpvAccess(ccsReq) = NULL;
184 }
185
186 void CcsSendDelayedReply(CcsDelayedReply d,int replyLen, const void *replyData)
187 {
188   CcsImplHeader *h = d.hdr;
189   h->len=ChMessageInt_new(1);
190   CcsReply(h,replyLen,replyData);
191   free(h);
192 }
193
194 void CcsNoReply()
195 {
196   if (CpvAccess(ccsReq)==NULL) return;
197   CpvAccess(ccsReq)->len = ChMessageInt_new(0);
198   CcsReply(CpvAccess(ccsReq),0,NULL);
199   CpvAccess(ccsReq) = NULL;
200 }
201
202 void CcsNoDelayedReply(CcsDelayedReply d)
203 {
204   CcsImplHeader *h = d.hdr;
205   h->len = ChMessageInt_new(0);
206   CcsReply(h,0,NULL);
207   free(h);
208 }
209
210
211 /**********************************
212 _CCS Implementation Routines:
213   These do the request forwarding and
214 delivery.
215 ***********************************/
216
217 /*CCS Bottleneck:
218   Deliver the given message data to the given
219 CCS handler.
220 */
221 void CcsHandleRequest(CcsImplHeader *hdr,const char *reqData)
222 {
223   char *cmsg;
224   int reqLen=ChMessageInt(hdr->len);
225 /*Look up handler's converse ID*/
226   char *handlerStr=hdr->handler;
227   CcsHandlerRec *fn=(CcsHandlerRec *)CkHashtableGet(CpvAccess(ccsTab),(void *)&handlerStr);
228   if (fn==NULL) {
229     CmiPrintf("CCS: Unknown CCS handler name '%s' requested. Ignoring...\n",
230               hdr->handler);
231     CpvAccess(ccsReq)=hdr;
232     CcsSendReply(0,NULL); /*Send an empty reply to the possibly waiting client*/
233     return;
234  /*   CmiAbort("CCS: Unknown CCS handler name.\n");*/
235   }
236
237 /* Call the handler */
238   CpvAccess(ccsReq)=hdr;
239   if (conditionalPipe[1]!=0 && _conditionalDelivery==0) {
240     /* We are conditionally delivering, send the message to the child and wait for its response */
241     int bytes = reqLen+((int)(reqData-((char*)hdr)))+CmiReservedHeaderSize;
242     write(conditionalPipe[1], &bytes, 4);
243     write(conditionalPipe[1], ((char*)hdr)-CmiReservedHeaderSize, bytes);
244     if (4==read(conditionalPipe[0], &bytes, 4)) {
245       char *buf = malloc(bytes);
246       read(conditionalPipe[0], buf, bytes);
247       CcsSendReply(bytes,buf);
248       free(buf);
249     } else {
250       /* the pipe has been closed */
251       CpdEndConditionalDeliver_master();
252    }
253   }
254   else {
255     callHandlerRec(fn,reqLen,reqData);
256   
257 /*Check if a reply was sent*/
258     if (CpvAccess(ccsReq)!=NULL)
259       CcsSendReply(0,NULL);/*Send an empty reply if not*/
260   }
261 }
262
263 #if ! NODE_0_IS_CONVHOST || CMK_BLUEGENE_CHARM
264 /* The followings are necessary to prevent CCS requests to be processed before
265  * CCS has been initialized. Really it matters only when NODE_0_IS_CONVHOST=0, but
266  * it doesn't hurt having it in the other case as well */
267 static char **bufferedMessages = NULL;
268 static int CcsNumBufferedMsgs = 0;
269 #define CCS_MAX_NUM_BUFFERED_MSGS  100
270
271 void CcsBufferMessage(char *msg) {
272   CmiPrintf("Buffering CCS message\n");
273   CmiAssert(CcsNumBufferedMsgs < CCS_MAX_NUM_BUFFERED_MSGS);
274   if (CcsNumBufferedMsgs < 0) CmiAbort("Why is a CCS message being buffered now???");
275   if (bufferedMessages == NULL) bufferedMessages = malloc(sizeof(char*)*CCS_MAX_NUM_BUFFERED_MSGS);
276   bufferedMessages[CcsNumBufferedMsgs] = msg;
277   CcsNumBufferedMsgs ++;
278 }
279 #endif
280   
281 /*Unpacks request message to call above routine*/
282 int _ccsHandlerIdx = 0;/*Converse handler index of routine req_fw_handler*/
283
284 #if CMK_BLUEGENE_CHARM
285 CpvDeclare(int, _bgCcsHandlerIdx);
286 CpvDeclare(int, _bgCcsAck);
287 /* This routine is needed when the application is built on top of the bigemulator
288  * layer of Charm. In this case, the real CCS handler must be called within a
289  * worker thread. The function of this function is to receive the CCS message in
290  * the bottom converse layer and forward it to the emulated layer. */
291 static void bg_req_fw_handler(char *msg) {
292   if (CpvAccess(_bgCcsAck) < BgNodeSize()) {
293     CcsBufferMessage(msg);
294     return;
295   }
296   //CmiPrintf("CCS scheduling message\n");
297   /* Get out of the message who is the destination pe */
298   int offset = CmiReservedHeaderSize + sizeof(CcsImplHeader);
299   CcsImplHeader *hdr = (CcsImplHeader *)(msg+CmiReservedHeaderSize);
300   int destPE = (int)ChMessageInt(hdr->pe);
301   if (destPE == -1) destPE = 0;
302   if (destPE < -1) {
303     ChMessageInt_t *pes_nbo = (ChMessageInt_t *)(msg+CmiReservedHeaderSize+sizeof(CcsImplHeader));
304     destPE = ChMessageInt(pes_nbo[0]);
305   }
306   //CmiAssert(destPE >= 0); // FixME: should cover also broadcast and multicast -> create generic function to extract destpe
307   (((CmiBlueGeneMsgHeader*)msg)->tID) = 0;
308   (((CmiBlueGeneMsgHeader*)msg)->n) = 0;
309   (((CmiBlueGeneMsgHeader*)msg)->flag) = 0;
310   (((CmiBlueGeneMsgHeader*)msg)->t) = 0;
311   (((CmiBlueGeneMsgHeader*)msg)->hID) = CpvAccess(_bgCcsHandlerIdx);
312   /* Get the right thread to deliver to (for now assume it is using CyclicMapInfo) */
313   addBgNodeInbuffer(msg, destPE/CmiNumPes());
314   //CmiPrintf("message CCS added %d to %d\n",((CmiBlueGeneMsgHeader*)msg)->hID, ((CmiBlueGeneMsgHeader*)msg)->tID);
315 }
316 #define req_fw_handler bg_req_fw_handler
317 #endif
318 extern void req_fw_handler(char *msg);
319
320 void CcsReleaseMessages() {
321 #if ! NODE_0_IS_CONVHOST || CMK_BLUEGENE_CHARM
322 #if CMK_BLUEGENE_CHARM
323   if (CpvAccess(_bgCcsAck) == 0 || CpvAccess(_bgCcsAck) < BgNodeSize()) return;
324 #endif
325   if (CcsNumBufferedMsgs > 0) {
326     int i;
327     //CmiPrintf("CCS: %d messages released\n",CcsNumBufferedMsgs);
328     for (i=0; i<CcsNumBufferedMsgs; ++i) {
329       CmiSetHandler(bufferedMessages[i], _ccsHandlerIdx);
330       CsdEnqueue(bufferedMessages[i]);
331     }
332     free(bufferedMessages);
333     bufferedMessages = NULL;
334     CcsNumBufferedMsgs = -1;
335   }
336 #endif
337 }
338
339 /*Convert CCS header & message data into a converse message 
340  addressed to handler*/
341 char *CcsImpl_ccs2converse(const CcsImplHeader *hdr,const void *data,int *ret_len)
342 {
343   int reqLen=ChMessageInt(hdr->len);
344   int destPE = ChMessageInt(hdr->pe);
345   int len;
346   char *msg;
347   if (destPE < -1) reqLen -= destPE*sizeof(int);
348   len=CmiReservedHeaderSize+sizeof(CcsImplHeader)+reqLen;
349   msg=(char *)CmiAlloc(len);
350   memcpy(msg+CmiReservedHeaderSize,hdr,sizeof(CcsImplHeader));
351   memcpy(msg+CmiReservedHeaderSize+sizeof(CcsImplHeader),data,reqLen);
352   if (ret_len!=NULL) *ret_len=len;
353   if (_ccsHandlerIdx != 0) {
354     CmiSetHandler(msg, _ccsHandlerIdx);
355     return msg;
356   } else {
357 #if NODE_0_IS_CONVHOST
358     CmiAbort("Why do we need to buffer messages when node 0 is Convhost?");
359 #else
360     CcsBufferMessage(msg);
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+CmiReservedHeaderSize;
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=CmiReservedHeaderSize+
429            sizeof(CcsImplHeader)+repLen;
430     char *msg=CmiAlloc(len);
431     char *r=msg+CmiReservedHeaderSize;
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 #if ! CMK_BLUEGENE_CHARM
458     /*Treat out of bound values as errors. Helps detecting bugs*/
459     if (repPE==-CmiNumPes()) CmiPrintf("Invalid processor index in CCS request: are you trying to do a broadcast instead?");
460     else CmiPrintf("Invalid processor index in CCS request.");
461     CpvAccess(ccsReq)=hdr;
462     CcsSendReply(0,NULL); /*Send an empty reply to the possibly waiting client*/
463     return;
464 #endif
465   }
466
467   msg=CcsImpl_ccs2converse(hdr,reqData,&len);
468   if (repPE >= 0) {
469     /* The following %CmiNumPes() follows the assumption that in BigSim the mapping is round-robin */
470     //CmiPrintf("CCS message received for %d\n",repPE);
471     CmiSyncSendAndFree(repPE%CmiNumPes(),len,msg);
472   } else if (repPE == -1) {
473     /* Broadcast to all processors */
474     //CmiPrintf("CCS broadcast received\n");
475     CmiSyncSendAndFree(0,len,msg);
476   } else {
477     /* Multicast to -repPE processors, specified right at the beginning of reqData (as a list of pes) */
478     int firstPE = ChMessageInt(*(ChMessageInt_t*)reqData);
479     /* The following %CmiNumPes() follows the assumption that in BigSim the mapping is round-robin */
480     //CmiPrintf("CCS multicast received\n");
481     CmiSyncSendAndFree(firstPE%CmiNumPes(),len,msg);
482   }
483 }
484
485 /*
486 We have to run a CCS server socket here on
487 node 0.  To keep the speed impact minimal,
488 we only probe for new connections (with CcsServerCheck)
489 occasionally.  
490  */
491 #include <signal.h>
492 #include "ccs-server.c" /*Include implementation here in this case*/
493 #include "ccs-auth.c"
494
495 /*Check for ready Ccs messages:*/
496 void CcsServerCheck(void)
497 {
498   while (1==skt_select1(CcsServer_fd(),0)) {
499     CcsImplHeader hdr;
500     void *data;
501     /* printf("Got CCS connect...\n"); */
502     if (CcsServer_recvRequest(&hdr,&data))
503     {/*We got a network request*/
504       /* printf("Got CCS request...\n"); */
505       if (! check_stdio_header(&hdr)) {
506         CcsImpl_netRequest(&hdr,data);
507       }
508       free(data);
509     }
510   }
511 }
512
513 #endif /*NODE_0_IS_CONVHOST*/
514
515 int _isCcsHandlerIdx(int hIdx) {
516   if (hIdx==_ccsHandlerIdx) return 1;
517   if (hIdx==rep_fw_handler_idx) return 1;
518   return 0;
519 }
520
521 void CcsBuiltinsInit(char **argv);
522
523 CpvDeclare(int, cmiArgDebugFlag);
524 CpvDeclare(char *, displayArgument);
525 CpvDeclare(int, cpdSuspendStartup);
526
527 void CcsInit(char **argv)
528 {
529   CpvInitialize(CkHashtable_c, ccsTab);
530   CpvAccess(ccsTab) = CkCreateHashtable_string(sizeof(CcsHandlerRec),5);
531   CpvInitialize(CcsImplHeader *, ccsReq);
532   CpvAccess(ccsReq) = NULL;
533   _ccsHandlerIdx = CmiRegisterHandler((CmiHandler)req_fw_handler);
534 #if CMK_BLUEGENE_CHARM
535   CpvInitialize(int, _bgCcsHandlerIdx);
536   CpvAccess(_bgCcsHandlerIdx) = 0;
537   CpvInitialize(int, _bgCcsAck);
538   CpvAccess(_bgCcsAck) = 0;
539 #endif
540   CpvInitialize(int, cmiArgDebugFlag);
541   CpvInitialize(char *, displayArgument);
542   CpvInitialize(int, cpdSuspendStartup);
543   CpvAccess(cmiArgDebugFlag) = 0;
544   CpvAccess(displayArgument) = NULL;
545   CpvAccess(cpdSuspendStartup) = 0;
546   
547   CcsBuiltinsInit(argv);
548
549   rep_fw_handler_idx = CmiRegisterHandler((CmiHandler)rep_fw_handler);
550 #if NODE_0_IS_CONVHOST
551 #if ! CMK_CMIPRINTF_IS_A_BUILTIN
552   print_fw_handler_idx = CmiRegisterHandler((CmiHandler)print_fw_handler);
553 #endif
554   {
555    int ccs_serverPort=0;
556    char *ccs_serverAuth=NULL;
557    
558    if (CmiGetArgFlagDesc(argv,"++server", "Create a CCS server port") | 
559       CmiGetArgIntDesc(argv,"++server-port",&ccs_serverPort, "Listen on this TCP/IP port number") |
560       CmiGetArgStringDesc(argv,"++server-auth",&ccs_serverAuth, "Use this CCS authentication file")) 
561     if (CmiMyPe()==0)
562     {/*Create and occasionally poll on a CCS server port*/
563       CcsServer_new(NULL,&ccs_serverPort,ccs_serverAuth);
564       CcdCallOnConditionKeep(CcdPERIODIC,(CcdVoidFn)CcsServerCheck,NULL);
565     }
566   }
567 #endif
568   /* if in parallel debug mode i.e ++cpd, freeze */
569   if (CmiGetArgFlagDesc(argv, "+cpd", "Used *only* in conjunction with parallel debugger"))
570   {
571      CpvAccess(cmiArgDebugFlag) = 1;
572      if (CmiGetArgStringDesc(argv, "+DebugDisplay",&(CpvAccess(displayArgument)), "X display for gdb used only in cpd mode"))
573      {
574         if (CpvAccess(displayArgument) == NULL)
575             CmiPrintf("WARNING> NULL parameter for +DebugDisplay\n***");
576      }
577      else if (CmiMyPe() == 0)
578      {
579             /* only one processor prints the warning */
580             CmiPrintf("WARNING> x term for gdb needs to be specified as +DebugDisplay by debugger\n***\n");
581      }
582
583      if (CmiGetArgFlagDesc(argv, "+DebugSuspend", "Suspend execution at beginning of program")) {
584        CpvAccess(cpdSuspendStartup) = 1;
585      }
586   }
587
588   CcsReleaseMessages();
589 }
590
591 #endif /*CMK_CCS_AVAILABLE*/
592