libcm is a C development framework with an emphasis on audio signal processing applications.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

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. }