libcm is a C development framework with an emphasis on audio signal processing applications.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmRtNet.c 31KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157
  1. //| Copyright: (C) 2009-2020 Kevin Larke <contact AT larke DOT org>
  2. //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
  3. #include "cmGlobal.h"
  4. #include "cmRpt.h"
  5. #include "cmErr.h"
  6. #include "cmCtx.h"
  7. #include "cmMem.h"
  8. #include "cmMallocDebug.h"
  9. #include "cmLinkedHeap.h"
  10. #include "cmUdpPort.h"
  11. #include "cmRtSysMsg.h"
  12. #include "cmRtNet.h"
  13. #include "cmTime.h"
  14. #include "cmText.h"
  15. // flags for cmRtNetNode_t.flags;
  16. enum
  17. {
  18. kLocalNodeNetFl = 0x01,
  19. kValidNodeNetFl = 0x02
  20. };
  21. // flags for cmRtNet_t.flags
  22. enum
  23. {
  24. kReportSyncNetFl = 0x01
  25. };
  26. struct cmRtNetNode_str;
  27. typedef struct cmRtNetEnd_str
  28. {
  29. cmChar_t* label;
  30. unsigned id;
  31. struct cmRtNetNode_str* np; // Owner node.
  32. struct cmRtNetEnd_str* link;
  33. } cmRtNetEnd_t;
  34. struct cmRtNet_str;
  35. typedef struct cmRtNetNode_str
  36. {
  37. unsigned rtSubIdx; // rtSubIdx of the sub-system which owns this node
  38. cmChar_t* label; // Node label.
  39. struct sockaddr_in sockaddr; // Socket address
  40. cmChar_t* addr; // IP Address (human readable)
  41. cmUdpPort_t port; // Socket port
  42. unsigned flags; // See kXXXNodeNetFl flags above.
  43. unsigned endPtIdx; // tracks the next endpoint to send during sync-mode
  44. unsigned endPtCnt; // local-node=actual cnt of endpt's remote-node:expected cnt of endpt's
  45. cmTimeSpec_t lastSendTime; // Time of last message sent
  46. cmRtNetEnd_t* ends; // End point list for this node
  47. struct cmRtNetNode_str* link;
  48. } cmRtNetNode_t;
  49. typedef struct cmRtNet_str
  50. {
  51. cmErr_t err; // Error state object
  52. unsigned flags; // See kXXXNetFl above.
  53. unsigned rtSubIdx; // rtSubIdx of the owning sub-system
  54. cmUdpH_t udpH; // UDP port handle
  55. cmUdpCallback_t cbFunc; // Client callback to receive incoming messages from network.
  56. void* cbArg; //
  57. cmRtNetNode_t* nodes; // Node list.
  58. cmRtNetNode_t* localNode; // Pointer to local node (which is also in node list)
  59. unsigned udpRecvBufByteCnt; // UDP port receive buffer size.
  60. unsigned udpTimeOutMs; // UDP time-out period
  61. cmChar_t* bcastAddr; // Network broadcast address
  62. } cmRtNet_t;
  63. cmRtNetH_t cmRtNetNullHandle = cmSTATIC_NULL_HANDLE;
  64. cmRtNetEndptH_t cmRtNetEndptNullHandle = cmSTATIC_NULL_HANDLE;
  65. cmRtNet_t* _cmRtNetHandleToPtr( cmRtNetH_t h )
  66. {
  67. cmRtNet_t* p = (cmRtNet_t*)h.h;
  68. assert( p != NULL );
  69. return p;
  70. }
  71. cmRtNetEnd_t* _cmRtNetEndptHandleToPtr( cmRtNetEndptH_t h )
  72. {
  73. cmRtNetEnd_t* p = (cmRtNetEnd_t*)h.h;
  74. assert( p != NULL );
  75. return p;
  76. }
  77. void _cmRtNetVRpt( cmRtNet_t* p, const cmChar_t* fmt, va_list vl )
  78. {
  79. if( cmIsFlag(p->flags,kReportSyncNetFl) )
  80. cmRptVPrintf(p->err.rpt,fmt,vl);
  81. }
  82. void _cmRtNetRpt( cmRtNet_t* p, const cmChar_t* fmt, ... )
  83. {
  84. va_list vl;
  85. va_start(vl,fmt);
  86. _cmRtNetVRpt(p,fmt,vl);
  87. va_end(vl);
  88. }
  89. cmRtNetNode_t* _cmRtNetFindNode( cmRtNet_t* p, const cmChar_t* label, unsigned* idxRef )
  90. {
  91. if( label == NULL )
  92. return NULL;
  93. if( idxRef != NULL )
  94. *idxRef = cmInvalidIdx;
  95. cmRtNetNode_t* np = p->nodes;
  96. unsigned i;
  97. for(i=0; np!=NULL; np=np->link,++i)
  98. if( strcmp(label,np->label)==0)
  99. {
  100. if( idxRef != NULL )
  101. *idxRef = i;
  102. return np;
  103. }
  104. return NULL;
  105. }
  106. cmRtNetNode_t* _cmRtNetFindNodeFromSockAddr( cmRtNet_t* p, const struct sockaddr_in* saddr )
  107. {
  108. if( saddr == NULL )
  109. return NULL;
  110. cmRtNetNode_t* np = p->nodes;
  111. for(; np!=NULL; np=np->link)
  112. if( np->sockaddr.sin_addr.s_addr == saddr->sin_addr.s_addr && np->sockaddr.sin_port == saddr->sin_port )
  113. return np;
  114. return NULL;
  115. }
  116. void _cmRtNetFreeNode( cmRtNetNode_t* np )
  117. {
  118. cmRtNetEnd_t* ep = np->ends;
  119. while( ep != NULL )
  120. {
  121. cmRtNetEnd_t* nep = ep->link;
  122. cmMemFree(ep->label);
  123. cmMemFree(ep);
  124. ep = nep;
  125. }
  126. cmMemFree(np->label);
  127. cmMemFree(np->addr);
  128. cmMemFree(np);
  129. }
  130. void _cmRtNetReleaseNodes( cmRtNet_t* p )
  131. {
  132. cmRtNetNode_t* np = p->nodes;
  133. while( np != NULL )
  134. {
  135. cmRtNetNode_t* nnp = np->link;
  136. _cmRtNetFreeNode(np);
  137. np = nnp;
  138. }
  139. p->nodes = NULL;
  140. p->localNode = NULL;
  141. }
  142. cmRtNetRC_t _cmRtNetReleaseNode( cmRtNet_t* p, cmRtNetNode_t* np )
  143. {
  144. cmRtNetNode_t* cnp = p->nodes;
  145. cmRtNetNode_t* pnp = NULL;
  146. while( cnp != NULL )
  147. {
  148. cmRtNetNode_t* nnp = cnp->link;
  149. if( np == cnp )
  150. {
  151. if( pnp == NULL )
  152. p->nodes = np->link;
  153. else
  154. pnp->link = np->link;
  155. _cmRtNetFreeNode(np);
  156. return kOkNetRC;
  157. }
  158. pnp = np;
  159. cnp = nnp;
  160. }
  161. assert(0);
  162. return cmErrMsg(&p->err,kNodeNotFoundNetRC,"Node to release not found.");
  163. }
  164. cmRtNetRC_t _cmRtNetCreateNode( cmRtNet_t* p, const cmChar_t* label, unsigned rtSubIdx, const cmChar_t* addr, cmUdpPort_t port, const struct sockaddr_in* saddr, unsigned flags, unsigned endPtCnt )
  165. {
  166. cmRtNetRC_t rc = kOkNetRC;
  167. cmRtNetNode_t* np;
  168. if( cmTextIsEmpty(label) )
  169. return cmErrMsg(&p->err,kInvalidLabelNetRC,"A null or blank node label was encountered.");
  170. if((np = _cmRtNetFindNode(p,label,NULL)) != NULL )
  171. cmErrWarnMsg(&p->err,kOkNetRC/*kDuplLabelNetRC*/,"The node label '%s' is being reused.",cmStringNullGuard(label));
  172. else
  173. {
  174. np = cmMemAllocZ(cmRtNetNode_t,1);
  175. np->label = cmMemAllocStr(label);
  176. }
  177. if( saddr != NULL )
  178. np->sockaddr = *saddr;
  179. np->rtSubIdx = rtSubIdx;
  180. np->addr = addr==NULL ? NULL : cmMemResizeStr(np->addr,addr);
  181. np->port = port;
  182. np->flags = flags;
  183. np->endPtCnt = endPtCnt;
  184. np->link = p->nodes;
  185. p->nodes = np;
  186. return rc;
  187. }
  188. cmRtNetEnd_t* _cmRtNetFindNodeEnd(cmRtNetNode_t* np, const cmChar_t* endPtLabel )
  189. {
  190. cmRtNetEnd_t* ep = np->ends;
  191. for(; ep!=NULL; ep=ep->link)
  192. if( strcmp(ep->label,endPtLabel)==0 )
  193. return ep;
  194. return NULL;
  195. }
  196. cmRtNetEnd_t* _cmRtNetIndexToEndpoint( cmRtNet_t* p, cmRtNetNode_t* np, unsigned endIndex )
  197. {
  198. cmRtNetEnd_t* ep = np->ends;
  199. unsigned i;
  200. for(i=0; ep!=NULL; ep=ep->link)
  201. {
  202. if( i == endIndex )
  203. return ep;
  204. ++i;
  205. }
  206. return NULL;
  207. }
  208. cmRtNetRC_t _cmRtNetCreateEndpoint( cmRtNet_t* p, cmRtNetNode_t* np, const cmChar_t* endPtLabel, unsigned endPtId )
  209. {
  210. if( endPtLabel == NULL )
  211. return cmErrMsg(&p->err,kInvalidLabelNetRC,"A null or blank node label was encountered.");
  212. if( _cmRtNetFindNodeEnd( np, endPtLabel) != NULL)
  213. return cmErrMsg(&p->err,kDuplEndNetRC,"A duplicate endpoint ('%s') was encountered on node '%s'.",endPtLabel,np->label);
  214. cmRtNetRC_t rc = kOkNetRC;
  215. cmRtNetEnd_t* ep = cmMemAllocZ(cmRtNetEnd_t,1);
  216. ep->label = cmMemAllocStr(endPtLabel);
  217. ep->id = endPtId;
  218. ep->np = np;
  219. ep->link = np->ends;
  220. np->ends = ep;
  221. return rc;
  222. }
  223. unsigned _cmRtNetNodeEndpointCount( cmRtNetNode_t* np )
  224. {
  225. cmRtNetEnd_t* ep = np->ends;
  226. unsigned n = 0;
  227. for(; ep!=NULL; ep=ep->link)
  228. ++n;
  229. return n;
  230. }
  231. unsigned _cmRtNetSyncMsgSerialByteCount( const cmRtNetSyncMsg_t* m )
  232. {
  233. return sizeof(cmRtNetSyncMsg_t) + (m->label==NULL ? 1 : strlen(m->label) + 1);
  234. }
  235. cmRtNetRC_t _cmRtNetSerializeSyncMsg( cmRtNet_t* p, const cmRtNetSyncMsg_t* m, void* buf, unsigned n )
  236. {
  237. unsigned bn = _cmRtNetSyncMsgSerialByteCount(m);
  238. char* b = (char*)buf;
  239. if( bn > n )
  240. return cmErrMsg(&p->err,kBufToSmallNetRC,"Serialize buffer too small.");
  241. memcpy(b,m,sizeof(*m));
  242. strcpy(b + sizeof(*m),m->label==NULL ? "" : m->label);
  243. return kOkNetRC;
  244. }
  245. cmRtNetRC_t _cmRtNetDeserializeSyncMsg( const void* buf, unsigned n, cmRtNetSyncMsg_t* m )
  246. {
  247. const cmRtNetSyncMsg_t* mp = (const cmRtNetSyncMsg_t*)buf;
  248. m->hdr.rtSubIdx = mp->hdr.rtSubIdx;
  249. m->hdr.selId = mp->hdr.selId;
  250. m->selId = mp->selId;
  251. m->hdrByteCnt = sizeof(cmRtNetSyncMsg_t);
  252. m->rtSubIdx = mp->rtSubIdx;
  253. m->id = mp->id;
  254. const cmChar_t* s = ((const cmChar_t*)(mp)) + mp->hdrByteCnt;
  255. m->label = cmMemAllocStr(s);
  256. return kOkNetRC;
  257. }
  258. cmRtNetRC_t _cmRtNetSendSyncMsg( cmRtNet_t* p, cmRtNetNode_t* np, cmRtNetSelId_t selId, const cmChar_t* msgLabel, unsigned msgId, unsigned msgRtSubIdx )
  259. {
  260. cmRtNetSyncMsg_t m;
  261. cmRtNetRC_t rc = kOkNetRC;
  262. cmUdpRC_t udpRC = kOkUdpRC;
  263. m.hdr.rtSubIdx = cmInvalidIdx;
  264. m.hdr.selId = kNetSyncSelRtId;
  265. m.selId = selId;
  266. m.hdrByteCnt = sizeof(cmRtNetSyncMsg_t);
  267. m.label = msgLabel;
  268. m.id = msgId;
  269. m.rtSubIdx = msgRtSubIdx;
  270. // determine size of msg to send
  271. unsigned n = _cmRtNetSyncMsgSerialByteCount(&m);
  272. cmChar_t buf[n];
  273. // serialize msg into buf[]
  274. if((rc = _cmRtNetSerializeSyncMsg(p,&m,buf,n)) != kOkNetRC )
  275. return rc;
  276. // send the msg
  277. if( selId == kHelloSelNetId )
  278. udpRC = cmUdpSend2(p->udpH, buf, n, p->bcastAddr, np->port );
  279. else
  280. udpRC = cmUdpSendTo(p->udpH, buf, n, &np->sockaddr );
  281. // check for send errors
  282. if( udpRC != kOkUdpRC )
  283. {
  284. rc = cmErrMsg(&p->err,kUdpPortFailNetRC,"Sync msg. send on UDP port failed.");
  285. }
  286. else
  287. {
  288. // record the last send time
  289. cmTimeGet(&np->lastSendTime);
  290. }
  291. return rc;
  292. }
  293. cmRtNetRC_t _cmRtNetFree( cmRtNet_t* p )
  294. {
  295. cmRtNetRC_t rc = kOkNetRC;
  296. if( cmUdpFree(&p->udpH) != kOkUdpRC )
  297. cmErrMsg(&p->err,kUdpPortFailNetRC,"UDP Port free failed.");
  298. _cmRtNetReleaseNodes(p);
  299. cmMemFree(p->bcastAddr);
  300. cmMemFree(p);
  301. return rc;
  302. }
  303. const cmRtNetNode_t* _cmRtNetIndexToRemoteNode( cmRtNet_t* p, unsigned idx )
  304. {
  305. const cmRtNetNode_t* np = p->nodes;
  306. unsigned i = 0;
  307. for(; np!=NULL; np=np->link)
  308. if( np != p->localNode )
  309. {
  310. if( i == idx )
  311. return np;
  312. ++i;
  313. }
  314. return NULL;
  315. }
  316. const cmRtNetEnd_t* _cmRtNetIndexToEndpt( const cmRtNetNode_t* np, unsigned endIdx )
  317. {
  318. const cmRtNetEnd_t* ep = np->ends;
  319. unsigned i = 0;
  320. for(; ep!=NULL; ep=ep->link,++i)
  321. if( i==endIdx )
  322. return ep;
  323. return NULL;
  324. }
  325. const cmRtNetEnd_t* _cmRtNetFindEndpt( cmRtNet_t* p, unsigned nodeIdx, unsigned epIdx )
  326. {
  327. const cmRtNetNode_t* np;
  328. const cmRtNetEnd_t* ep;
  329. if((np = _cmRtNetIndexToRemoteNode( p, nodeIdx )) == NULL )
  330. return NULL;
  331. if((ep = _cmRtNetIndexToEndpt( np, epIdx )) == NULL )
  332. return NULL;
  333. return ep;
  334. }
  335. const cmChar_t* cmRtNetSyncMsgLabel( const cmRtNetSyncMsg_t* m )
  336. {
  337. if( m->selId==kNodeSelNetId || m->selId==kEndpointSelNetId )
  338. return (const cmChar_t*)(m+1);
  339. return "";
  340. }
  341. cmRtNetRC_t cmRtNetAlloc( cmCtx_t* ctx, cmRtNetH_t* hp, unsigned rtSubIdx, cmUdpCallback_t cbFunc, void* cbArg )
  342. {
  343. cmRtNetRC_t rc;
  344. if((rc = cmRtNetFree(hp)) != kOkNetRC )
  345. return rc;
  346. cmRtNet_t* p = cmMemAllocZ(cmRtNet_t,1);
  347. cmErrSetup(&p->err,&ctx->rpt,"cmRtNet");
  348. // allocate the UDP port
  349. if(cmUdpAlloc(ctx,&p->udpH) != kOkUdpRC )
  350. {
  351. cmErrMsg(&p->err,kUdpPortFailNetRC,"UDP Port allocate failed.");
  352. goto errLabel;
  353. }
  354. p->rtSubIdx = rtSubIdx;
  355. p->udpTimeOutMs = 50;
  356. p->udpRecvBufByteCnt = 8192;
  357. p->cbFunc = cbFunc;
  358. p->cbArg = cbArg;
  359. hp->h = p;
  360. errLabel:
  361. if(rc != kOkNetRC )
  362. _cmRtNetFree(p);
  363. return rc;
  364. }
  365. cmRtNetRC_t cmRtNetFree( cmRtNetH_t* hp )
  366. {
  367. cmRtNetRC_t rc = kOkNetRC;
  368. if( hp==NULL || cmRtNetIsValid(*hp)==false )
  369. return rc;
  370. cmRtNet_t* p = _cmRtNetHandleToPtr(*hp);
  371. if((rc = _cmRtNetFree(p)) != kOkNetRC )
  372. return rc;
  373. hp->h = NULL;
  374. return rc;
  375. }
  376. const cmChar_t* cmRtNetLocalHostName( cmRtNetH_t h )
  377. {
  378. cmRtNet_t* p = _cmRtNetHandleToPtr(h);
  379. return cmUdpHostName(p->udpH);
  380. }
  381. bool cmRtNetIsValid( cmRtNetH_t h )
  382. { return h.h !=NULL; }
  383. cmUdpH_t cmRtNetUdpPortHandle( cmRtNetH_t h )
  384. {
  385. cmRtNet_t* p = _cmRtNetHandleToPtr(h);
  386. return p->udpH;
  387. }
  388. cmRtNetRC_t _cmRtNetSendEndpointReplyMsg( cmRtNet_t* p, cmRtNetNode_t* np, cmRtNetSelId_t srcSelId )
  389. {
  390. cmRtNetRC_t rc = kOkNetRC;
  391. cmRtNetEnd_t* ep = NULL;
  392. const cmChar_t* msgLabel = NULL;
  393. unsigned msgId = cmInvalidId;
  394. unsigned msgRtSubIdx = cmInvalidIdx;
  395. cmRtNetSelId_t selId = kEndpointSelNetId;
  396. const cmChar_t* rptLabel = "endpoint";
  397. if( np == NULL )
  398. return cmErrMsg(&p->err,kNodeNotFoundNetRC,"The net node associated with an endpoint reply was not found.");
  399. // if we got here by receiving a 'done' msg from the remote node ...
  400. if( srcSelId == kDoneSelNetId )
  401. {
  402. // ... then mark the remote node as having recieved all endpoints
  403. unsigned n;
  404. if((n = _cmRtNetNodeEndpointCount(np)) != np->endPtCnt )
  405. rc = cmErrMsg(&p->err,kNodeEndCntErrNetRC,"The node '%s' expected %i endpoints but received %i.",cmStringNullGuard(np->label),np->endPtCnt,n);
  406. else
  407. np->flags = cmSetFlag(np->flags,kValidNodeNetFl);
  408. }
  409. // attempt to get the next local endpoint to send ...
  410. if((ep = _cmRtNetIndexToEndpoint(p,p->localNode,np->endPtIdx)) != NULL )
  411. {
  412. msgLabel = ep->label; // ... send next local endpoint
  413. msgId = ep->id;
  414. msgRtSubIdx = ep->np->rtSubIdx;
  415. }
  416. else // .... all local endpoints have been sent
  417. {
  418. selId = kInvalidSelNetId;
  419. rptLabel = "done";
  420. // verify that no endpoints are available
  421. if( np->endPtIdx < p->localNode->endPtCnt )
  422. rc = cmErrMsg(&p->err,kSyncFailNetRC,"More endpoints are available to send but are not reachable.");
  423. else
  424. {
  425. // if the remote node still has endpts to send then continue
  426. // sending 'done' messages.
  427. if( np->endPtIdx==p->localNode->endPtCnt || srcSelId != kDoneSelNetId )
  428. selId = kDoneSelNetId;
  429. }
  430. }
  431. // selId is set to kInvalidSelNetId when we encounter the (stop) criteria
  432. if( selId != kInvalidSelNetId )
  433. {
  434. if((rc = _cmRtNetSendSyncMsg(p,np,selId,msgLabel,msgId,msgRtSubIdx)) != kOkNetRC )
  435. rc = cmErrMsg(&p->err,rc,"Send '%s' to %s:%s:%i failed.",rptLabel,cmStringNullGuard(np->label),cmStringNullGuard(np->addr),np->port);
  436. else
  437. _cmRtNetRpt(p,"Sent %s.\n",cmStringNullGuard(rptLabel));
  438. np->endPtIdx += 1;
  439. }
  440. return rc;
  441. }
  442. bool _cmRtNetIsSyncModeMsg( const void* data, unsigned dataByteCnt )
  443. {
  444. cmRtNetSyncMsg_t* m = (cmRtNetSyncMsg_t*)data;
  445. return dataByteCnt >= sizeof(cmRtSysMsgHdr_t) && m->hdr.selId == kNetSyncSelRtId;
  446. }
  447. // When the network message recieve function (See cmRtNetAlloc() 'cbFunc')
  448. // receives a message with the cmRtSysMsgHdr_t.selId == kNetSyncSelRtId
  449. // it should call this function to update the current sync state of the
  450. // cmRtNet.
  451. cmRtNetRC_t _cmRtNetSyncModeRecv( cmRtNet_t* p, const char* data, unsigned dataByteCnt, const struct sockaddr_in* fromAddr )
  452. {
  453. cmRtNetRC_t rc = kOkNetRC;
  454. cmRtNetSyncMsg_t m;
  455. m.label = NULL;
  456. assert( _cmRtNetIsSyncModeMsg(data,dataByteCnt));
  457. if( _cmRtNetDeserializeSyncMsg(data,dataByteCnt,&m) != kOkNetRC )
  458. {
  459. rc = cmErrMsg(&p->err,rc,"Net sync. receive failed due to deserialize fail.");
  460. goto errLabel;
  461. }
  462. _cmRtNetRpt(p,"recv from:%s\n",cmUdpAddrToString(p->udpH, fromAddr ));
  463. assert( m.hdr.selId == kNetSyncSelRtId );
  464. // attempt to locate the remote node which sent the msg
  465. cmRtNetNode_t* np = _cmRtNetFindNodeFromSockAddr(p,fromAddr);
  466. switch( m.selId )
  467. {
  468. case kHelloSelNetId:
  469. // if this is a response to a broadcast from the local node then ignore it
  470. if(m.label!=NULL && p->localNode->label!=NULL && (np = _cmRtNetFindNode(p,m.label,NULL)) != NULL && strcmp(p->localNode->label,m.label)==0 )
  471. {
  472. const cmChar_t* fromAddrStr = cmUdpAddrToString(p->udpH,fromAddr);
  473. const cmChar_t* localAddrStr = cmUdpAddrToString(p->udpH,cmUdpLocalAddr(p->udpH));
  474. if( fromAddrStr!=NULL && localAddrStr!=NULL && strcmp(fromAddrStr,localAddrStr)!=0)
  475. cmErrMsg(&p->err,kDuplLocalNetRC,"The node label '%s' appears to be duplicated at address %s and locally.",cmStringNullGuard(m.label),fromAddrStr);
  476. np->sockaddr = *fromAddr;
  477. goto errLabel;
  478. }
  479. // fall through
  480. case kNodeSelNetId:
  481. {
  482. // if the node already exists ...
  483. if( np != NULL )
  484. {
  485. // ... delete it because we are about to get new info. about it.
  486. if((rc = _cmRtNetReleaseNode(p,np )) != kOkNetRC )
  487. goto errLabel;
  488. }
  489. // create a node proxy to represent the remote node
  490. // (Note:m.id == remote node endpoint count (i.e. the count of endpoints expected for the remote node.))
  491. if(( rc = _cmRtNetCreateNode(p,m.label,m.rtSubIdx,NULL,0,fromAddr,0,m.id)) != kOkNetRC )
  492. goto errLabel;
  493. np = p->nodes; // newest node is always the first node
  494. // send response
  495. switch( m.selId )
  496. {
  497. case kHelloSelNetId:
  498. _cmRtNetRpt(p,"rcv hello\n"); // reply with local node
  499. rc = _cmRtNetSendSyncMsg( p, np, kNodeSelNetId, p->localNode->label, p->localNode->endPtCnt, p->localNode->rtSubIdx );
  500. break;
  501. case kNodeSelNetId:
  502. _cmRtNetRpt(p,"rcv node\n");
  503. _cmRtNetSendEndpointReplyMsg( p, np, m.selId ); // reply with first endpoint
  504. break;
  505. default:
  506. assert(0);
  507. }
  508. }
  509. break;
  510. case kDoneSelNetId:
  511. //case kEndpointAckSelNetId:
  512. rc = _cmRtNetSendEndpointReplyMsg(p,np,m.selId);
  513. break;
  514. case kEndpointSelNetId:
  515. {
  516. cmRtNetEnd_t* ep;
  517. // verify the remote node exists.
  518. if( np == NULL )
  519. {
  520. rc = cmErrMsg(&p->err,kNodeNotFoundNetRC,"The net node associated with an endpoint receive was not found.");
  521. goto errLabel;
  522. }
  523. // attempt to find the end point
  524. if((ep = _cmRtNetFindNodeEnd(np, m.label)) != NULL )
  525. ep->id = m.id; // the endpoint was found update the endPtId
  526. else
  527. {
  528. // create a local proxy for the endpoint
  529. if((rc = _cmRtNetCreateEndpoint(p,np,m.label,m.id)) != kOkNetRC )
  530. goto errLabel;
  531. }
  532. // reply with a local endpoint or 'done' msg
  533. rc = _cmRtNetSendEndpointReplyMsg( p, np, m.selId );
  534. }
  535. break;
  536. default:
  537. assert(0);
  538. break;
  539. }
  540. errLabel:
  541. cmMemFree((cmChar_t*)m.label);
  542. return rc;
  543. }
  544. unsigned _cmRtNetAddrToNodeIndex( cmRtNet_t* p, const struct sockaddr_in* addr )
  545. {
  546. unsigned i;
  547. cmRtNetNode_t* np = p->nodes;
  548. for(i=0; np!=NULL; np=np->link,++i)
  549. if( cmUdpAddrIsEqual( addr, &np->sockaddr ) )
  550. return i;
  551. return cmInvalidIdx;
  552. }
  553. // This is called in the context of cmRtNetReceive().
  554. void _cmRtNetRecv( void* cbArg, const char* data, unsigned dataByteCnt, const struct sockaddr_in* fromAddr )
  555. {
  556. cmRtNet_t* p = (cmRtNet_t*)cbArg;
  557. // if this is a sync msg - then handle it here
  558. if( _cmRtNetIsSyncModeMsg(data,dataByteCnt))
  559. _cmRtNetSyncModeRecv(p,data,dataByteCnt,fromAddr);
  560. else
  561. {
  562. // All non-sync messages arriving here are prefixed by a cmRtNetMsg_t header - fill in the source addr here.
  563. // NOTE: the source addr could be filled in by the sender but this would increase the size
  564. // of the msg. Instead we choose the more time consuming method of looking up the
  565. // soure node here - hmmmm????.
  566. cmRtNetMsg_t* hdr = (cmRtNetMsg_t*)(data);
  567. hdr->srcNodeIdx = _cmRtNetAddrToNodeIndex(p,fromAddr);
  568. }
  569. p->cbFunc(p->cbArg,data,dataByteCnt,fromAddr);
  570. }
  571. cmRtNetRC_t cmRtNetInitialize( cmRtNetH_t h, const cmChar_t* bcastAddr, const cmChar_t* nodeLabel, const cmChar_t* ipAddr, cmUdpPort_t port )
  572. {
  573. cmRtNet_t* p = _cmRtNetHandleToPtr(h);
  574. cmRtNetRC_t rc;
  575. // release the local node
  576. if((rc = cmRtNetFinalize(h)) != kOkNetRC )
  577. goto errLabel;
  578. if( cmTextIsEmpty(bcastAddr) )
  579. {
  580. rc = cmErrMsg(&p->err,kInvalidArgNetRC,"The 'broadcast address' is not valid.");
  581. goto errLabel;
  582. }
  583. // if this is the local node then initialze the local socket
  584. if( cmUdpInit(p->udpH,port,kNonBlockingUdpFl | kBroadcastUdpFl,_cmRtNetRecv,p,NULL,0,p->udpRecvBufByteCnt,p->udpTimeOutMs) != kOkUdpRC )
  585. {
  586. rc = cmErrMsg(&p->err,kUdpPortFailNetRC,"The UDP port initialization failed.");
  587. goto errLabel;
  588. }
  589. // create the local node
  590. if((rc = _cmRtNetCreateNode(p,nodeLabel, p->rtSubIdx, ipAddr, port, NULL, kLocalNodeNetFl, 0)) != kOkNetRC )
  591. goto errLabel;
  592. // the last created node is always the first node on the list
  593. p->localNode = p->nodes;
  594. p->bcastAddr = cmMemResizeStr(p->bcastAddr,bcastAddr);
  595. // begin listening on the local port
  596. if( cmUdpEnableListen(p->udpH, true ) != kOkUdpRC )
  597. {
  598. rc = cmErrMsg(&p->err,kUdpPortFailNetRC,"The UDP port failed to enter 'listen' mode.");
  599. goto errLabel;
  600. }
  601. errLabel:
  602. return rc;
  603. }
  604. bool cmRtNetIsInitialized( cmRtNetH_t h )
  605. {
  606. if( cmRtNetIsValid(h) == false )
  607. return false;
  608. cmRtNet_t* p = _cmRtNetHandleToPtr(h);
  609. return p->localNode != NULL && cmTextIsNotEmpty(p->bcastAddr);
  610. }
  611. cmRtNetRC_t cmRtNetRegisterEndPoint( cmRtNetH_t h, const cmChar_t* endPtLabel, unsigned endPtId )
  612. {
  613. cmRtNetRC_t rc = kOkNetRC;
  614. cmRtNet_t* p = _cmRtNetHandleToPtr(h);
  615. if( p->localNode == NULL )
  616. return cmErrMsg(&p->err,kLocalNodeNetRC,"Local endpoints may not be added if a local node has not been defined.");
  617. if((rc = _cmRtNetCreateEndpoint(p, p->localNode,endPtLabel,endPtId )) == kOkNetRC )
  618. p->localNode->endPtCnt += 1;
  619. return rc;
  620. }
  621. cmRtNetRC_t cmRtNetFinalize( cmRtNetH_t h )
  622. {
  623. cmRtNet_t* p = _cmRtNetHandleToPtr(h);
  624. _cmRtNetReleaseNodes(p);
  625. return kOkNetRC;
  626. }
  627. cmRtNetRC_t cmRtNetDoSync( cmRtNetH_t h )
  628. {
  629. cmRtNet_t* p = _cmRtNetHandleToPtr(h);
  630. // broadcast 'node' msg
  631. return _cmRtNetSendSyncMsg( p, p->localNode, kHelloSelNetId, p->localNode->label, p->localNode->endPtCnt, cmInvalidIdx );
  632. }
  633. cmRtNetRC_t cmRtNetReceive( cmRtNetH_t h )
  634. {
  635. cmRtNetRC_t rc = kOkNetRC;
  636. cmRtNet_t* p = _cmRtNetHandleToPtr(h);
  637. // Calling this function results in callbacks to _cmRtNetRecv() (above)
  638. if( cmUdpGetAvailData(p->udpH, NULL, NULL, NULL ) != kOkUdpRC )
  639. {
  640. cmErrMsg(&p->err,kUdpPortFailNetRC,"UDP port query failed.");
  641. goto errLabel;
  642. }
  643. errLabel:
  644. return rc;
  645. }
  646. cmRtNetRC_t cmRtNetEndpointHandle( cmRtNetH_t h, const cmChar_t* nodeLabel, const cmChar_t* endptLabel, cmRtNetEndptH_t* hp )
  647. {
  648. cmRtNetRC_t rc = kOkNetRC;
  649. cmRtNet_t* p = _cmRtNetHandleToPtr(h);
  650. cmRtNetNode_t* np;
  651. cmRtNetEnd_t* ep;
  652. if(( np = _cmRtNetFindNode(p,nodeLabel,NULL)) == NULL )
  653. return cmErrMsg(&p->err,kNodeNotFoundNetRC,"The node '%s' was not found.",cmStringNullGuard(nodeLabel));
  654. if(( ep = _cmRtNetFindNodeEnd(np, endptLabel )) == NULL )
  655. return cmErrMsg(&p->err,kEndNotFoundNetRC,"The endpoint '%s' on '%s' on node was not found.",cmStringNullGuard(endptLabel),cmStringNullGuard(nodeLabel));
  656. hp->h = ep;
  657. return rc;
  658. }
  659. bool cmRtNetEndpointIsValid( cmRtNetEndptH_t endPtH )
  660. { return endPtH.h != NULL; }
  661. unsigned cmRtNetEndpointId( cmRtNetEndptH_t endPtH )
  662. {
  663. if( !cmRtNetEndpointIsValid(endPtH) )
  664. return cmInvalidId;
  665. cmRtNetEnd_t* ep = _cmRtNetEndptHandleToPtr( endPtH );
  666. return ep->id;
  667. }
  668. const cmChar_t* cmRtNetEndpointLabel( cmRtNetEndptH_t endPtH )
  669. {
  670. if( !cmRtNetEndpointIsValid(endPtH) )
  671. return NULL;
  672. cmRtNetEnd_t* ep = _cmRtNetEndptHandleToPtr( endPtH );
  673. return ep->label;
  674. }
  675. cmRtNetRC_t _cmRtNetSend( cmRtNet_t* p, unsigned srcEndPtId, const cmRtNetEnd_t* ep, const void* msg, unsigned msgByteCnt )
  676. {
  677. cmRtNetRC_t rc = kOkNetRC;
  678. unsigned hN = sizeof(cmRtNetMsg_t);
  679. unsigned dN = hN + msgByteCnt;
  680. char data[ dN ];
  681. cmRtNetMsg_t* r = (cmRtNetMsg_t*)data;
  682. r->hdr.rtSubIdx = ep->np->rtSubIdx;
  683. r->hdr.selId = kMsgSelRtId;
  684. r->dstEndPtId = ep->id;
  685. r->srcEndPtId = srcEndPtId;
  686. memcpy(data+hN,msg,msgByteCnt);
  687. // ep->np->sockaddr identifies the node on the receiving cmRtNet.
  688. // cmRtNetMsg_t* r.endptId is then used by the receiving cmRtNet to indicate which endpoint on
  689. // the node the incoming message should be associated with.
  690. if( cmUdpSendTo(p->udpH, data, dN, &ep->np->sockaddr ) != kOkUdpRC )
  691. return cmErrMsg(&p->err,kUdpPortFailNetRC,"Send to node:%s endpt:%s failed.\n",cmStringNullGuard(ep->np->label),cmStringNullGuard(ep->label));
  692. return rc;
  693. }
  694. cmRtNetRC_t cmRtNetSend( cmRtNetH_t h, unsigned srcEndPtId, cmRtNetEndptH_t epH, const void* msg, unsigned msgByteCnt )
  695. {
  696. cmRtNet_t* p = _cmRtNetHandleToPtr(h);
  697. cmRtNetEnd_t* ep = _cmRtNetEndptHandleToPtr(epH);
  698. assert( ep != NULL );
  699. return _cmRtNetSend(p,srcEndPtId,ep,msg,msgByteCnt);
  700. }
  701. cmRtNetRC_t cmRtNetSendByLabels( cmRtNetH_t h, unsigned srcEndPtId, const cmChar_t* nodeLabel, const cmChar_t* endptLabel, const void* msg, unsigned msgByteCnt )
  702. {
  703. cmRtNetRC_t rc = kOkNetRC;
  704. cmRtNetEndptH_t epH = cmRtNetEndptNullHandle;
  705. if((rc = cmRtNetEndpointHandle(h,nodeLabel,endptLabel,&epH)) != kOkNetRC )
  706. return rc;
  707. return cmRtNetSend(h,srcEndPtId,epH,msg,msgByteCnt);
  708. }
  709. cmRtNetRC_t cmRtNetSendByIndex( cmRtNetH_t h, unsigned srcEndPtId, unsigned nodeIdx, unsigned endptIdx, const void* msg, unsigned msgByteCnt )
  710. {
  711. cmRtNet_t* p = _cmRtNetHandleToPtr(h);
  712. const cmRtNetEnd_t* ep;
  713. if((ep = _cmRtNetFindEndpt(p, nodeIdx, endptIdx )) == NULL )
  714. return cmErrMsg(&p->err,kEndNotFoundNetRC,"The endpoint at node index %i endpoint index %i was not found.",nodeIdx,endptIdx);
  715. return _cmRtNetSend( p, srcEndPtId, ep, msg, msgByteCnt );
  716. }
  717. bool cmRtNetReportSyncEnable( cmRtNetH_t h, bool enableFl )
  718. {
  719. cmRtNet_t* p = _cmRtNetHandleToPtr(h);
  720. bool fl = cmIsFlag(p->flags,kReportSyncNetFl);
  721. p->flags = cmEnaFlag(p->flags,kReportSyncNetFl,enableFl);
  722. return fl;
  723. }
  724. bool cmRtNetReportSyncIsEnabled( cmRtNetH_t h )
  725. {
  726. cmRtNet_t* p = _cmRtNetHandleToPtr(h);
  727. return cmIsFlag(p->flags,kReportSyncNetFl);
  728. }
  729. void cmRtNetReport( cmRtNetH_t h )
  730. {
  731. cmRtNet_t* p = _cmRtNetHandleToPtr(h);
  732. cmRpt_t* rpt = p->err.rpt;
  733. cmRtNetNode_t* np = p->nodes;
  734. for(; np!=NULL; np=np->link)
  735. {
  736. cmRptPrintf(rpt,"Node: %s ",np->label);
  737. if( np->addr != NULL )
  738. cmRptPrintf(rpt,"%s ",np->addr );
  739. if( cmIsFlag(np->flags,kLocalNodeNetFl) )
  740. cmRptPrintf(rpt,"LOCAL ");
  741. cmRptPrintf(rpt,"%s ",cmStringNullGuard(cmUdpAddrToString(p->udpH,&np->sockaddr)));
  742. if( np->port != kInvalidUdpPortNumber )
  743. cmRptPrintf(rpt,"%i ",np->port );
  744. cmRptPrintf(rpt,"\n");
  745. cmRtNetEnd_t* ep = np->ends;
  746. for(; ep!=NULL; ep=ep->link)
  747. {
  748. cmRptPrintf(rpt," endpt: %i %s\n",ep->id,cmStringNullGuard(ep->label ));
  749. }
  750. }
  751. }
  752. const cmChar_t* cmRtNetLocalNodeLabel( cmRtNetH_t h )
  753. {
  754. cmRtNet_t* p = _cmRtNetHandleToPtr( h );
  755. return p->localNode->label;
  756. }
  757. unsigned cmRtNetRemoteNodeCount( cmRtNetH_t h )
  758. {
  759. cmRtNet_t* p = _cmRtNetHandleToPtr( h );
  760. const cmRtNetNode_t* np = p->nodes;
  761. unsigned n = 0;
  762. for(; np!=NULL; np=np->link)
  763. if( np != p->localNode )
  764. ++n;
  765. return n;
  766. }
  767. unsigned cmRtNetAddrToNodeIndex( cmRtNetH_t h, const struct sockaddr_in* a )
  768. {
  769. cmRtNet_t* p = _cmRtNetHandleToPtr(h);
  770. return _cmRtNetAddrToNodeIndex(p,a);
  771. }
  772. unsigned cmRtNetRemoteNodeIndex( cmRtNetH_t h, const cmChar_t* label )
  773. {
  774. cmRtNet_t* p = _cmRtNetHandleToPtr( h );
  775. unsigned idx = cmInvalidIdx;
  776. _cmRtNetFindNode(p,label,&idx);
  777. return idx;
  778. }
  779. const cmChar_t* cmRtNetRemoteNodeLabel( cmRtNetH_t h, unsigned idx )
  780. {
  781. cmRtNet_t* p = _cmRtNetHandleToPtr( h );
  782. const cmRtNetNode_t* np;
  783. if((np = _cmRtNetIndexToRemoteNode( p, idx )) == NULL )
  784. return NULL;
  785. return np->label;
  786. }
  787. unsigned cmRtNetRemoteNodeEndPointCount( cmRtNetH_t h, unsigned nodeIdx )
  788. {
  789. const cmRtNetNode_t* np;
  790. const cmRtNetEnd_t* ep;
  791. cmRtNet_t* p = _cmRtNetHandleToPtr( h );
  792. unsigned n = 0;
  793. if((np = _cmRtNetIndexToRemoteNode( p, nodeIdx )) == NULL )
  794. return 0;
  795. for(ep=np->ends; ep!=NULL; ep=ep->link)
  796. ++n;
  797. return n;
  798. }
  799. cmRtNetRC_t cmRtNetRemoteNodeEndPoint(
  800. cmRtNetH_t h,
  801. unsigned nodeIdx,
  802. unsigned epIdx,
  803. const cmChar_t** labelRef,
  804. unsigned* idRef,
  805. unsigned* rsiRef )
  806. {
  807. const cmRtNetEnd_t* ep;
  808. cmRtNet_t* p = _cmRtNetHandleToPtr( h );
  809. if(( ep = _cmRtNetFindEndpt(p, nodeIdx, epIdx )) == NULL )
  810. {
  811. *labelRef = NULL;
  812. *idRef = cmInvalidId;
  813. *rsiRef = cmInvalidIdx;
  814. return kEndNotFoundNetRC;
  815. }
  816. *labelRef = ep->label;
  817. *idRef = ep->id;
  818. *rsiRef = ep->np->rtSubIdx;
  819. return kOkNetRC;
  820. }
  821. //==========================================================================
  822. #include "cmThread.h"
  823. typedef struct
  824. {
  825. cmThreadH_t thH;
  826. cmRtNetH_t netH;
  827. unsigned msgVal;
  828. } _cmRtNetTest_t;
  829. // This function is called within the context of cmRtNetReceive().
  830. void _cmRtNetTestRecv( void* cbArg, const char* data, unsigned dataByteCnt, const struct sockaddr_in* fromAddr )
  831. {
  832. //_cmRtNetTest_t* p = (_cmRtNetTest_t*)cbArg;
  833. cmRtNetMsg_t* r = (cmRtNetMsg_t*)data;
  834. unsigned i = *(unsigned*)(data + sizeof(cmRtNetMsg_t));
  835. printf("rtSubIdx:%i endptId:%i %i\n",r->hdr.rtSubIdx,r->dstEndPtId,i);
  836. }
  837. bool _cmRtNetTestThreadFunc(void* param)
  838. {
  839. _cmRtNetTest_t* p = (_cmRtNetTest_t*)param;
  840. if( cmRtNetIsValid(p->netH) )
  841. {
  842. cmRtNetReceive(p->netH);
  843. }
  844. cmSleepMs(40);
  845. return true;
  846. }
  847. void cmRtNetTest( cmCtx_t* ctx, bool mstrFl )
  848. {
  849. char c;
  850. _cmRtNetTest_t t;
  851. const unsigned rtSubIdx = 0;
  852. cmUdpPort_t port = 5346;
  853. _cmRtNetTest_t* p = &t;
  854. cmRtNetRC_t rc = kOkNetRC;
  855. const cmChar_t* localHostStr = mstrFl ? "master" : "slave";
  856. const cmChar_t* localEndpStr = mstrFl ? "master_ep" : "slave_ep";
  857. const cmChar_t* remoteHostStr = !mstrFl ? "master" : "slave";
  858. const cmChar_t* remoteEndpStr = !mstrFl ? "master_ep" : "slave_ep";
  859. const cmChar_t* bcastAddr = "192.168.1.255";
  860. cmRtNetEndptH_t eH = cmRtNetEndptNullHandle;
  861. unsigned srcEndPtId = cmInvalidId;
  862. memset(&t,0,sizeof(t));
  863. if( cmThreadCreate(&p->thH,_cmRtNetTestThreadFunc,p,&ctx->rpt) != kOkThRC )
  864. goto errLabel;
  865. if((rc = cmRtNetAlloc(ctx,&p->netH,rtSubIdx,_cmRtNetTestRecv,p)) != kOkNetRC )
  866. goto errLabel;
  867. cmRtNetReportSyncEnable(p->netH,true); // enable sync. protocol reporting
  868. if((rc = cmRtNetInitialize(p->netH, bcastAddr, localHostStr, NULL, port )) != kOkNetRC)
  869. goto errLabel;
  870. if((rc = cmRtNetRegisterEndPoint(p->netH,localEndpStr, 0 )) != kOkNetRC )
  871. goto errLabel;
  872. if((rc = cmRtNetEndpointHandle(p->netH, localHostStr, localEndpStr, &eH )) != kOkNetRC )
  873. goto errLabel;
  874. if((srcEndPtId = cmRtNetEndpointId(eH)) == cmInvalidIdx )
  875. goto errLabel;
  876. if( cmThreadPause(p->thH,0) != kOkThRC )
  877. goto errLabel;
  878. cmRptPrintf(&ctx->rpt,"%s t=transmit s=sync r=report q=quit\n", localHostStr );
  879. while( (c=getchar()) != 'q' )
  880. {
  881. switch(c)
  882. {
  883. case 'r':
  884. cmRtNetReport(p->netH);
  885. break;
  886. case 's':
  887. cmRtNetDoSync(p->netH);
  888. break;
  889. case 't':
  890. {
  891. if( cmRtNetSendByLabels(p->netH, srcEndPtId, remoteHostStr, remoteEndpStr, &p->msgVal, sizeof(p->msgVal)) == kOkNetRC )
  892. p->msgVal += 1;
  893. }
  894. break;
  895. }
  896. }
  897. errLabel:
  898. cmThreadDestroy(&p->thH);
  899. cmRtNetFree(&p->netH);
  900. return;
  901. }