PageRenderTime 47ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/otp.net/Otp/OtpNode.cs

https://github.com/gebi/jungerl
C# | 850 lines | 509 code | 90 blank | 251 comment | 68 complexity | b0446030eaa21ab36b23b69b9cbc4f05 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, AGPL-1.0
  1. /*``The contents of this file are subject to the Erlang Public License,
  2. * Version 1.1, (the "License"); you may not use this file except in
  3. * compliance with the License. You should have received a copy of the
  4. * Erlang Public License along with this software. If not, it can be
  5. * retrieved via the world wide web at http://www.erlang.org/.
  6. *
  7. * Software distributed under the License is distributed on an "AS IS"
  8. * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  9. * the License for the specific language governing rights and limitations
  10. * under the License.
  11. *
  12. * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
  13. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
  14. * AB. All Rights Reserved.''
  15. *
  16. * Converted from Java to C# by Vlad Dumitrescu (vlad_Dumitrescu@hotmail.com)
  17. */
  18. namespace Otp
  19. {
  20. using System;
  21. /*
  22. * <p> Represents a local OTP node. This class is used when you do not
  23. * wish to manage connections yourself - outgoing connections are
  24. * established as needed, and incoming connections accepted
  25. * automatically. This class supports the use of a mailbox API for
  26. * communication, while management of the underlying communication
  27. * mechanism is automatic and hidden from the application programmer.
  28. * </p>
  29. *
  30. * <p> Once an instance of this class has been created, obtain one or
  31. * more mailboxes in order to send or receive messages. The first
  32. * message sent to a given node will cause a connection to be set up
  33. * to that node. Any messages received will be delivered to the
  34. * appropriate mailboxes. </p>
  35. *
  36. * <p> To shut down the node, call {@link #close close()}. This will
  37. * prevent the node from accepting additional connections and it will
  38. * cause all existing connections to be closed. Any unread messages in
  39. * existing mailboxes can still be read, however no new messages will
  40. * be delivered to the mailboxes. </p>
  41. *
  42. * <p> Note that the use of this class requires that Epmd (Erlang Port
  43. * Mapper Daemon) is running on each cooperating host. This class does
  44. * not start Epmd automatically as Erlang does, you must start it
  45. * manually or through some other means. See the Erlang documentation
  46. * for more information about this. </p>
  47. **/
  48. public class OtpNode:OtpLocalNode
  49. {
  50. private bool initDone = false;
  51. private int listenPort;
  52. // thread to manage incoming connections
  53. private Acceptor acceptor = null;
  54. // keep track of all connections
  55. internal System.Collections.Hashtable connections = null;
  56. // keep track of all mailboxes
  57. internal Mailboxes mboxes = null;
  58. /*
  59. * <p> Create a node using the default cookie. The default
  60. * cookie is found by reading the first line of the .erlang.cookie
  61. * file in the user's home directory. The home directory is obtained
  62. * from the System property "user.home". </p>
  63. *
  64. * <p> If the file does not exist, an empty string is used. This
  65. * method makes no attempt to create the file. </p>
  66. *
  67. * @param node the name of this node.
  68. *
  69. * @exception IOException if communication could not be initialized.
  70. *
  71. **/
  72. public OtpNode(System.String node)
  73. : this(node, true, defaultCookie, 0, false)
  74. {
  75. }
  76. public OtpNode(System.String node, bool acceptConnections)
  77. : this(node, acceptConnections, defaultCookie, 0, false)
  78. {
  79. }
  80. public OtpNode(System.String node, bool acceptConnections, bool shortName)
  81. : this(node, acceptConnections, defaultCookie, 0, shortName)
  82. {
  83. }
  84. /*
  85. * Create a node.
  86. *
  87. * @param node the name of this node.
  88. *
  89. * @param cookie the authorization cookie that will be used by this
  90. * node when it communicates with other nodes.
  91. *
  92. * @exception IOException if communication could not be initialized.
  93. *
  94. **/
  95. public OtpNode(System.String node, bool acceptConnections, System.String cookie)
  96. : this(node, acceptConnections, cookie, 0, false)
  97. {
  98. }
  99. /*
  100. * Create a node.
  101. *
  102. * @param acceptConnections when true this node will register with epmd and
  103. * start listening for connections.
  104. *
  105. * @param node the name of this node.
  106. *
  107. * @param cookie the authorization cookie that will be used by this
  108. * node when it communicates with other nodes.
  109. *
  110. * @param shortName defines whether the node should use short or long names when
  111. * referencing other nodes.
  112. *
  113. * @exception IOException if communication could not be initialized.
  114. *
  115. **/
  116. public OtpNode(bool acceptConnections, System.String node, System.String cookie, bool shortName)
  117. : this(node, acceptConnections, cookie, 0, shortName)
  118. {
  119. }
  120. /*
  121. * Create a node.
  122. *
  123. * @param node the name of this node.
  124. *
  125. * @param cookie the authorization cookie that will be used by this
  126. * node when it communicates with other nodes.
  127. *
  128. * @param port the port number you wish to use for incoming
  129. * connections. Specifying 0 lets the system choose an available
  130. * port.
  131. *
  132. * @exception IOException if communication could not be initialized.
  133. *
  134. **/
  135. public OtpNode(System.String node, bool acceptConnections, System.String cookie, int port, bool shortName)
  136. : base(node, cookie, shortName)
  137. {
  138. init(acceptConnections, port);
  139. }
  140. private void init(bool acceptConnections, int port)
  141. {
  142. lock(this)
  143. {
  144. if (!initDone)
  145. {
  146. connections = new System.Collections.Hashtable(17, (float) 0.95);
  147. mboxes = new Mailboxes(this);
  148. if (acceptConnections)
  149. acceptor = new Acceptor(this, port);
  150. listenPort = port;
  151. initDone = true;
  152. }
  153. }
  154. }
  155. /*
  156. * When a node instance is created is started with acceptConnections set to false
  157. * then no incoming connections are accepted. This method allows to begin
  158. * accepting connections.
  159. **/
  160. public bool startConnectionAcceptor()
  161. {
  162. if (acceptor != null)
  163. return true;
  164. else if (!initDone)
  165. return false;
  166. lock (this)
  167. {
  168. if (acceptor != null)
  169. acceptor = new Acceptor(this, listenPort);
  170. }
  171. return true;
  172. }
  173. /*
  174. * Close the node. Unpublish the node from Epmd (preventing new
  175. * connections) and close all existing connections.
  176. **/
  177. public virtual void close()
  178. {
  179. lock(this)
  180. {
  181. if (acceptor != null)
  182. acceptor.quit();
  183. OtpCookedConnection conn;
  184. System.Collections.IDictionaryEnumerator it = connections.GetEnumerator();
  185. mboxes.clear();
  186. it.Reset();
  187. while (it.MoveNext())
  188. {
  189. conn = (OtpCookedConnection) it.Value;
  190. conn.close();
  191. }
  192. initDone = false;
  193. }
  194. }
  195. ~OtpNode()
  196. {
  197. close();
  198. }
  199. /*
  200. * Create an unnamed {@link OtpMbox mailbox} that can be used to
  201. * send and receive messages with other, similar mailboxes and with
  202. * Erlang processes. Messages can be sent to this mailbox by using
  203. * its associated {@link OtpMbox#self pid}.
  204. *
  205. * @return a mailbox.
  206. **/
  207. public virtual OtpMbox createMbox()
  208. {
  209. return mboxes.create();
  210. }
  211. /*
  212. * Close the specified mailbox.
  213. *
  214. * @param the mailbox to close.
  215. *
  216. * <p> After this operation, the mailbox will no longer be able to
  217. * receive messages. Any delivered but as yet unretrieved messages
  218. * can still be retrieved however. </p>
  219. *
  220. * <p> If there are links from the mailbox to other {@link
  221. * OtpErlangPid pids}, they will be broken when this method is
  222. * called and exit signals will be sent. </p>
  223. *
  224. **/
  225. public virtual void closeMbox(OtpMbox mbox)
  226. {
  227. if (mbox != null)
  228. {
  229. mboxes.remove(mbox);
  230. mbox.name = null;
  231. mbox.breakLinks("normal");
  232. }
  233. }
  234. /*
  235. * Create an named mailbox that can be used to send and receive
  236. * messages with other, similar mailboxes and with Erlang processes.
  237. * Messages can be sent to this mailbox by using its registered name
  238. * or the associated {@link OtpMbox#self pid}.
  239. *
  240. * @param name a name to register for this mailbox. The name must be
  241. * unique within this OtpNode.
  242. *
  243. * @return a mailbox, or null if the name was already in use.
  244. *
  245. **/
  246. public virtual OtpMbox createMbox(System.String name)
  247. {
  248. return mboxes.create(name);
  249. }
  250. /*
  251. * <p> Register or remove a name for the given mailbox. Registering
  252. * a name for a mailbox enables others to send messages without
  253. * knowing the {@link OtpErlangPid pid} of the mailbox. A mailbox
  254. * can have at most one name; if the mailbox already had a name,
  255. * calling this method will supercede that name. </p>
  256. *
  257. * @param name the name to register for the mailbox. Specify null to
  258. * unregister the existing name from this mailbox.
  259. *
  260. * @param mbox the mailbox to associate with the name.
  261. *
  262. * @return true if the name was available, or false otherwise.
  263. **/
  264. public virtual bool registerName(System.String name, OtpMbox mbox)
  265. {
  266. return mboxes.register(name, mbox);
  267. }
  268. /*
  269. * Get a list of all known registered names on this node.
  270. *
  271. * @return an array of Strings, containins all known registered
  272. * names on this node.
  273. **/
  274. public virtual System.String[] getNames()
  275. {
  276. return mboxes.names();
  277. }
  278. /*
  279. * Determine the {@link Pid pid} corresponding to a
  280. * registered name on this node.
  281. *
  282. * @return the {@link Pid pid} corresponding to the
  283. * registered name, or null if the name is not known on this node.
  284. **/
  285. public virtual Erlang.Pid whereis(System.String name)
  286. {
  287. OtpMbox m = mboxes.get(name);
  288. if (m != null)
  289. return m.self();
  290. return null;
  291. }
  292. /*
  293. * <p> Determine if another node is alive. This method has the side
  294. * effect of setting up a connection to the remote node (if
  295. * possible). Only a single outgoing message is sent; the timeout is
  296. * how long to wait for a response. </p>
  297. *
  298. * <p> Only a single attempt is made to connect to the remote node,
  299. * so for example it is not possible to specify an extremely long
  300. * timeout and expect to be notified when the node eventually comes
  301. * up. If you wish to wait for a remote node to be started, the
  302. * following construction may be useful: </p>
  303. *
  304. <pre>
  305. // ping every 2 seconds until positive response
  306. while (!me.ping(him,2000));
  307. </pre>
  308. *
  309. * @param node the name of the node to ping.
  310. *
  311. * @param timeout the time, in milliseconds, to wait for response
  312. * before returning false.
  313. *
  314. * @return true if the node was alive and the correct ping response
  315. * was returned. false if the correct response was not returned on
  316. * time.
  317. **/
  318. /*internal info about the message formats...
  319. *
  320. * the request:
  321. * -> REG_SEND {6,#Pid<bingo@aule.1.0>,'',net_kernel}
  322. * {'$gen_call',{#Pid<bingo@aule.1.0>,#Ref<bingo@aule.2>},{is_auth,bingo@aule}}
  323. *
  324. * the reply:
  325. * <- SEND {2,'',#Pid<bingo@aule.1.0>}
  326. * {#Ref<bingo@aule.2>,yes}
  327. */
  328. public virtual bool ping(System.String node, long timeout)
  329. {
  330. if (node.Equals(this._node))
  331. return true;
  332. OtpMbox mbox = null;
  333. try
  334. {
  335. mbox = createMbox();
  336. mbox.send("net_kernel", node, getPingTuple(mbox));
  337. Erlang.Object reply = mbox.receive(timeout);
  338. Erlang.Tuple t = (Erlang.Tuple) reply;
  339. Erlang.Atom a = (Erlang.Atom) (t.elementAt(1));
  340. return "yes".Equals(a.atomValue());
  341. }
  342. catch (System.Exception)
  343. {
  344. }
  345. finally
  346. {
  347. closeMbox(mbox);
  348. }
  349. return false;
  350. }
  351. /*create the outgoing ping message */
  352. private Erlang.Tuple getPingTuple(OtpMbox mbox)
  353. {
  354. Erlang.Object[] ping = new Erlang.Object[3];
  355. Erlang.Object[] pid = new Erlang.Object[2];
  356. Erlang.Object[] _node = new Erlang.Object[2];
  357. pid[0] = mbox.self();
  358. pid[1] = createRef();
  359. _node[0] = new Erlang.Atom("is_auth");
  360. _node[1] = new Erlang.Atom(node());
  361. ping[0] = new Erlang.Atom("$gen_call");
  362. ping[1] = new Erlang.Tuple(pid);
  363. ping[2] = new Erlang.Tuple(_node);
  364. return new Erlang.Tuple(ping);
  365. }
  366. /*
  367. * this method simulates net_kernel only for
  368. * the purpose of replying to pings.
  369. */
  370. private bool netKernel(OtpMsg m)
  371. {
  372. OtpMbox mbox = null;
  373. try
  374. {
  375. Erlang.Tuple t = (Erlang.Tuple) (m.getMsg());
  376. Erlang.Tuple req = (Erlang.Tuple) t.elementAt(1); // actual request
  377. Erlang.Pid pid = (Erlang.Pid) req.elementAt(0); // originating pid
  378. Erlang.Object[] pong = new Erlang.Object[2];
  379. pong[0] = req.elementAt(1); // his #Ref
  380. pong[1] = new Erlang.Atom("yes");
  381. mbox = createMbox();
  382. mbox.send(pid, new Erlang.Tuple(pong));
  383. return true;
  384. }
  385. catch (System.Exception)
  386. {
  387. }
  388. finally
  389. {
  390. closeMbox(mbox);
  391. }
  392. return false;
  393. }
  394. /*
  395. * OtpCookedConnection delivers messages here
  396. * return true if message was delivered successfully, or false otherwise.
  397. */
  398. internal virtual bool deliver(OtpMsg m)
  399. {
  400. OtpMbox mbox = null;
  401. try
  402. {
  403. OtpMsg.Tag t = m.type();
  404. if (t == OtpMsg.Tag.regSendTag)
  405. {
  406. System.String name = m.getRecipientName();
  407. /*special case for netKernel requests */
  408. if (name.Equals("net_kernel"))
  409. return netKernel(m);
  410. else
  411. mbox = mboxes.get(name);
  412. }
  413. else
  414. {
  415. mbox = mboxes.get(m.getRecipientPid());
  416. }
  417. if (mbox == null)
  418. return false;
  419. mbox.deliver(m);
  420. }
  421. catch (System.Exception)
  422. {
  423. return false;
  424. }
  425. return true;
  426. }
  427. /*
  428. * OtpCookedConnection delivers errors here, we send them on to the
  429. * handler specified by the application
  430. */
  431. internal virtual void deliverError(OtpCookedConnection conn, System.Exception e)
  432. {
  433. removeConnection(conn);
  434. remoteStatus(conn.name, false, e);
  435. }
  436. /*
  437. * find or create a connection to the given node using specified cookie
  438. */
  439. public virtual OtpCookedConnection connection(System.String node)
  440. {
  441. return connection(node, null);
  442. }
  443. /*
  444. * find or create a connection to the given node
  445. */
  446. public virtual OtpCookedConnection connection(System.String node, string cookie)
  447. {
  448. OtpPeer peer = null;
  449. OtpCookedConnection conn = null;
  450. lock(connections)
  451. {
  452. // first just try looking up the name as-is
  453. conn = (OtpCookedConnection) connections[node];
  454. if (conn == null)
  455. {
  456. // in case node had no '@' add localhost info and try again
  457. peer = new OtpPeer(node);
  458. conn = (OtpCookedConnection) connections[peer.node()];
  459. if (conn == null)
  460. {
  461. try
  462. {
  463. conn = new OtpCookedConnection(this, peer, cookie);
  464. addConnection(conn);
  465. }
  466. catch (System.Exception e)
  467. {
  468. /*false = outgoing */
  469. connAttempt(peer.node(), false, e);
  470. }
  471. }
  472. }
  473. return conn;
  474. }
  475. }
  476. private void addConnection(OtpCookedConnection conn)
  477. {
  478. if ((conn != null) && (conn.name != null))
  479. {
  480. connections[conn.name] = conn;
  481. remoteStatus(conn.name, true, null);
  482. }
  483. }
  484. private void removeConnection(OtpCookedConnection conn)
  485. {
  486. if ((conn != null) && (conn.name != null))
  487. {
  488. connections.Remove(conn.name);
  489. }
  490. }
  491. /*this class used to wrap the mailbox hashtables so we can use weak references */
  492. public class Mailboxes
  493. {
  494. private void InitBlock(OtpNode enclosingInstance)
  495. {
  496. this.enclosingInstance = enclosingInstance;
  497. }
  498. private OtpNode enclosingInstance;
  499. internal System.Collections.Hashtable byPid = null; // mbox pids here
  500. private System.Collections.Hashtable byName = null; // mbox names here
  501. public Mailboxes(OtpNode enclosingInstance)
  502. {
  503. InitBlock(enclosingInstance);
  504. byPid = new System.Collections.Hashtable(17, (float) 0.95);
  505. byName = new System.Collections.Hashtable(17, (float) 0.95);
  506. }
  507. public virtual OtpMbox create(System.String name)
  508. {
  509. OtpMbox m = null;
  510. lock(byName)
  511. {
  512. if (get(name) != null)
  513. return null;
  514. Erlang.Pid pid = enclosingInstance.createPid();
  515. m = new OtpMbox(enclosingInstance, pid, name);
  516. byPid[pid] = new WeakReference(m);
  517. byName[name] = new WeakReference(m);
  518. }
  519. return m;
  520. }
  521. public virtual OtpMbox create()
  522. {
  523. Erlang.Pid pid = enclosingInstance.createPid();
  524. OtpMbox m = new OtpMbox(enclosingInstance, pid);
  525. byPid[pid] = new WeakReference(m);
  526. return m;
  527. }
  528. public virtual void clear()
  529. {
  530. byPid.Clear();
  531. byName.Clear();
  532. }
  533. public virtual System.String[] names()
  534. {
  535. System.String[] allnames = null;
  536. lock(byName)
  537. {
  538. int n = byName.Count;
  539. System.Collections.IDictionaryEnumerator keys = byName.GetEnumerator();
  540. allnames = new System.String[n];
  541. int i = 0;
  542. keys.Reset();
  543. while (keys.MoveNext())
  544. {
  545. allnames[i++] = (System.String) (keys.Key);
  546. }
  547. }
  548. return allnames;
  549. }
  550. public virtual System.String[] pids()
  551. {
  552. System.String[] allpids = null;
  553. lock(byPid)
  554. {
  555. int n = byPid.Count;
  556. System.Collections.IDictionaryEnumerator keys = byPid.GetEnumerator();
  557. allpids = new System.String[n];
  558. int i = 0;
  559. keys.Reset();
  560. while (keys.MoveNext())
  561. {
  562. allpids[i++] = ((Erlang.Pid) (keys.Key)).ToString();
  563. }
  564. }
  565. return allpids;
  566. }
  567. public virtual bool register(System.String name, OtpMbox mbox)
  568. {
  569. if (name == null)
  570. {
  571. if (mbox.name != null)
  572. {
  573. byName.Remove(mbox.name);
  574. mbox.name = null;
  575. }
  576. }
  577. else
  578. {
  579. lock(byName)
  580. {
  581. if (get(name) != null)
  582. return false;
  583. byName[name] = new WeakReference(mbox);
  584. mbox.name = name;
  585. }
  586. }
  587. return true;
  588. }
  589. /*look up a mailbox based on its name. If the mailbox has gone out
  590. * of scope we also remove the reference from the hashtable so we
  591. * don't find it again.
  592. */
  593. public virtual OtpMbox get(System.String name)
  594. {
  595. WeakReference wr = (WeakReference) byName[name];
  596. if (wr != null)
  597. {
  598. OtpMbox m = (OtpMbox) wr.Target;
  599. if (m != null)
  600. return m;
  601. byName.Remove(name);
  602. }
  603. return null;
  604. }
  605. /*look up a mailbox based on its pid. If the mailbox has gone out
  606. * of scope we also remove the reference from the hashtable so we
  607. * don't find it again.
  608. */
  609. public virtual OtpMbox get(Erlang.Pid pid)
  610. {
  611. WeakReference wr = (WeakReference) byPid[pid];
  612. if (wr != null)
  613. {
  614. OtpMbox m = (OtpMbox) wr.Target;
  615. if (m != null)
  616. return m;
  617. byPid.Remove(pid);
  618. }
  619. return null;
  620. }
  621. public virtual void remove(OtpMbox mbox)
  622. {
  623. byPid.Remove(mbox._self);
  624. if (mbox.name != null)
  625. byName.Remove(mbox.name);
  626. }
  627. }
  628. /*
  629. * this thread simply listens for incoming connections
  630. */
  631. public class Acceptor
  632. {
  633. private void InitBlock(OtpNode a_node)
  634. {
  635. this.a_node = a_node;
  636. }
  637. private OtpNode a_node;
  638. private System.Net.Sockets.TcpListener a_sock;
  639. private int a_port;
  640. private bool a_done = false;
  641. private System.Threading.Thread thread;
  642. internal Acceptor(OtpNode a_node, int port)
  643. {
  644. InitBlock(a_node);
  645. a_sock = new System.Net.Sockets.TcpListener(System.Net.Dns.GetHostEntry("localhost").AddressList[0], port);
  646. a_sock.Start();
  647. this.a_port = ((System.Net.IPEndPoint)a_sock.LocalEndpoint).Port;
  648. a_node._port = this.a_port;
  649. publishPort();
  650. thread = new System.Threading.Thread(new System.Threading.ThreadStart(Start));
  651. thread.IsBackground = true;
  652. thread.Name = "acceptor "+a_node.node();
  653. thread.Start();
  654. }
  655. private bool publishPort()
  656. {
  657. if (a_node.getEpmd() != null)
  658. return false;
  659. // already published
  660. OtpEpmd.publishPort(a_node);
  661. return true;
  662. }
  663. private void unPublishPort()
  664. {
  665. // unregister with epmd
  666. OtpEpmd.unPublishPort(a_node);
  667. // close the local descriptor (if we have one)
  668. closeSock(a_node.epmd);
  669. a_node.epmd = null;
  670. }
  671. public virtual void quit()
  672. {
  673. unPublishPort();
  674. a_done = true;
  675. closeSock(a_sock);
  676. //thread.Interrupt();
  677. }
  678. private void closeSock(System.Net.Sockets.TcpListener s)
  679. {
  680. try
  681. {
  682. if (s != null)
  683. s.Stop();
  684. }
  685. catch (System.Exception)
  686. {
  687. }
  688. }
  689. private void closeSock(System.Net.Sockets.TcpClient s)
  690. {
  691. try
  692. {
  693. if (s != null)
  694. s.Close();
  695. }
  696. catch (System.Exception)
  697. {
  698. }
  699. }
  700. public virtual int port()
  701. {
  702. return this.a_port;
  703. }
  704. public void Start()
  705. {
  706. System.Net.Sockets.TcpClient newsock = null;
  707. OtpCookedConnection conn = null;
  708. a_node.localStatus(a_node._node, true, null);
  709. while (!a_done)
  710. {
  711. conn = null;
  712. try
  713. {
  714. newsock = a_sock.AcceptTcpClient();
  715. }
  716. catch (System.Exception e)
  717. {
  718. a_node.localStatus(a_node._node, false, e);
  719. goto accept_loop_brk;
  720. }
  721. try
  722. {
  723. lock(a_node.connections)
  724. {
  725. conn = new OtpCookedConnection(a_node, newsock);
  726. a_node.addConnection(conn);
  727. }
  728. }
  729. catch (OtpAuthException e)
  730. {
  731. if (conn != null && conn.name != null)
  732. a_node.connAttempt(conn.name, true, e);
  733. else
  734. a_node.connAttempt("unknown", true, e);
  735. closeSock(newsock);
  736. }
  737. catch (System.IO.IOException e)
  738. {
  739. if (conn != null && conn.name != null)
  740. a_node.connAttempt(conn.name, true, e);
  741. else
  742. a_node.connAttempt("unknown", true, e);
  743. closeSock(newsock);
  744. }
  745. catch (System.Exception e)
  746. {
  747. closeSock(newsock);
  748. closeSock(a_sock);
  749. a_node.localStatus(a_node._node, false, e);
  750. goto accept_loop_brk;
  751. }
  752. }
  753. accept_loop_brk: ;
  754. // while
  755. // if we have exited loop we must do this too
  756. unPublishPort();
  757. }
  758. }
  759. }
  760. }