PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/otp.net/Otp/OtpMbox.cs

https://github.com/gebi/jungerl
C# | 774 lines | 387 code | 59 blank | 328 comment | 39 complexity | bc231a2f836dd5bbc06eab3be9242815 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. /*
  21. * <p> Provides a simple mechanism for exchanging messages with Erlang
  22. * processes or other instances of this class.</p>
  23. *
  24. * <p> Each mailbox is associated with a unique {@link OtpErlangPid
  25. * pid} that contains information necessary for delivery of messages.
  26. * When sending messages to named processes or mailboxes, the sender
  27. * pid is made available to the recipient of the message. When sending
  28. * messages to other mailboxes, the recipient can only respond if the
  29. * sender includes the pid as part of the message contents. The sender
  30. * can determine his own pid by calling {@link #self self()}. </p>
  31. *
  32. * <p> Mailboxes can be named, either at creation or later. Messages
  33. * can be sent to named mailboxes and named Erlang processes without
  34. * knowing the {@link OtpErlangPid pid} that identifies the mailbox.
  35. * This is neccessary in order to set up initial communication between
  36. * parts of an application. Each mailbox can have at most one name.
  37. * </p>
  38. *
  39. * <p> Since this class was intended for communication with Erlang,
  40. * all of the send methods take {@link OtpErlangObject
  41. * OtpErlangObject} arguments. However this class can also be used to
  42. * transmit arbitrary C# objects (as long as they implement one of
  43. * C#.io.Serializable or C#.io.Externalizable) by encapsulating
  44. * the object in a {@link OtpErlangBinary OtpErlangBinary}. </p>
  45. *
  46. * <p> Messages to remote nodes are externalized for transmission, and
  47. * as a result the recipient receives a <b>copy</b> of the original
  48. * C# object. To ensure consistent behaviour when messages are sent
  49. * between local mailboxes, such messages are cloned before delivery.
  50. * </p>
  51. *
  52. * <p> Additionally, mailboxes can be linked in much the same way as
  53. * Erlang processes. If a link is active when a mailbox is {@link
  54. * #close closed}, any linked Erlang processes or OtpMboxes will be
  55. * sent an exit signal. As well, exit signals will be (eventually)
  56. * sent if a mailbox goes out of scope and its {@link #finalize
  57. * finalize()} method called. However due to the nature of
  58. * finalization (i.e. C# makes no guarantees about when {@link
  59. * #finalize finalize()} will be called) it is recommended that you
  60. * always explicitly close mailboxes if you are using links instead of
  61. * relying on finalization to notify other parties in a timely manner.
  62. * </p>
  63. *
  64. * When retrieving messages from a mailbox that has received an exit
  65. * signal, an {@link OtpErlangExit OtpErlangExit} exception will be
  66. * raised. Note that the exception is queued in the mailbox along with
  67. * other messages, and will not be raised until it reaches the head of
  68. * the queue and is about to be retrieved. </p>
  69. *
  70. **/
  71. using System;
  72. public class OtpMbox
  73. {
  74. internal OtpNode home;
  75. internal Erlang.Pid _self;
  76. internal GenericQueue queue;
  77. internal System.String name;
  78. internal Links links;
  79. internal System.Collections.Hashtable monitors;
  80. // package constructor: called by OtpNode:createMbox(name)
  81. // to create a named mbox
  82. internal OtpMbox(OtpNode home, Erlang.Pid self, System.String name)
  83. {
  84. this._self = self;
  85. this.home = home;
  86. this.name = name;
  87. this.queue = new GenericQueue();
  88. this.links = new Links(10);
  89. this.monitors = new System.Collections.Hashtable(49, (float)0.95);
  90. }
  91. // package constructor: called by OtpNode:createMbox()
  92. // to create an anonymous
  93. internal OtpMbox(OtpNode home, Erlang.Pid self):this(home, self, null)
  94. {
  95. }
  96. public void flush()
  97. {
  98. this.queue.flush();
  99. }
  100. /*
  101. * <p> Get the identifying {@link Pid pid} associated with
  102. * this mailbox.</p>
  103. *
  104. * <p> The {@link Pid pid} associated with this mailbox
  105. * uniquely identifies the mailbox and can be used to address the
  106. * mailbox. You can send the {@link Pid pid} to a remote
  107. * communicating part so that he can know where to send his
  108. * response. </p>
  109. *
  110. * @return the self pid for this mailbox.
  111. **/
  112. public virtual Erlang.Pid self()
  113. {
  114. return _self;
  115. }
  116. /*
  117. * <p> Register or remove a name for this mailbox. Registering a
  118. * name for a mailbox enables others to send messages without
  119. * knowing the {@link Pid pid} of the mailbox. A mailbox
  120. * can have at most one name; if the mailbox already had a name,
  121. * calling this method will supercede that name. </p>
  122. *
  123. * @param name the name to register for the mailbox. Specify null to
  124. * unregister the existing name from this mailbox.
  125. *
  126. * @return true if the name was available, or false otherwise.
  127. **/
  128. public virtual bool registerName(System.String name)
  129. {
  130. lock(this)
  131. {
  132. return home.registerName(name, this);
  133. }
  134. }
  135. /*
  136. * Get the registered name of this mailbox.
  137. *
  138. * @return the registered name of this mailbox, or null if the
  139. * mailbox had no registerd name.
  140. **/
  141. public virtual System.String getName()
  142. {
  143. return this.name;
  144. }
  145. /*
  146. * Block until a message arrives for this mailbox.
  147. *
  148. * @return an {@link Object Object} representing
  149. * the body of the next message waiting in this mailbox.
  150. *
  151. * @exception Exit if a linked {@link Pid pid} has
  152. * exited or has sent an exit signal to this mailbox.
  153. *
  154. **/
  155. public virtual Erlang.Object receive()
  156. {
  157. return receive(-1);
  158. }
  159. /*
  160. * Wait for a message to arrive for this mailbox.
  161. *
  162. * @param timeout the time, in milliseconds, to wait for a message
  163. * before returning null.
  164. *
  165. * @return an {@link Object Object} representing
  166. * the body of the next message waiting in this mailbox.
  167. *
  168. * @exception Exit if a linked {@link Pid pid} has
  169. * exited or has sent an exit signal to this mailbox.
  170. *
  171. **/
  172. public virtual Erlang.Object receive(long timeout)
  173. {
  174. try
  175. {
  176. OtpMsg m = receiveMsg(timeout);
  177. if (m != null)
  178. return m.getMsg();
  179. }
  180. catch (Erlang.Exit e)
  181. {
  182. throw e;
  183. }
  184. catch (System.Exception)
  185. {
  186. }
  187. return null;
  188. }
  189. /*
  190. * Block until a message arrives for this mailbox.
  191. *
  192. * @return a byte array representing the still-encoded body of the
  193. * next message waiting in this mailbox.
  194. *
  195. * @exception Exit if a linked {@link Pid pid} has
  196. * exited or has sent an exit signal to this mailbox.
  197. *
  198. **/
  199. public virtual OtpInputStream receiveBuf()
  200. {
  201. return receiveBuf(-1);
  202. }
  203. /*
  204. * Wait for a message to arrive for this mailbox.
  205. *
  206. * @param timeout the time, in milliseconds, to wait for a message
  207. * before returning null.
  208. *
  209. * @return a byte array representing the still-encoded body of the
  210. * next message waiting in this mailbox.
  211. *
  212. * @exception Exit if a linked {@link Pid pid} has
  213. * exited or has sent an exit signal to this mailbox.
  214. *
  215. * @exception InterruptedException if no message if the method
  216. * times out before a message becomes available.
  217. **/
  218. public virtual OtpInputStream receiveBuf(long timeout)
  219. {
  220. OtpMsg m = receiveMsg();
  221. return m == null ? null : m.getMsgBuf();
  222. }
  223. /*
  224. * Block until a message arrives for this mailbox.
  225. *
  226. * @return an {@link OtpMsg OtpMsg} containing the header
  227. * information as well as the body of the next message waiting in
  228. * this mailbox.
  229. *
  230. * @exception Exit if a linked {@link Pid pid} has
  231. * exited or has sent an exit signal to this mailbox.
  232. *
  233. **/
  234. public virtual OtpMsg receiveMsg()
  235. {
  236. return receiveMsg(-1);
  237. }
  238. /*
  239. * Wait for a message to arrive for this mailbox.
  240. *
  241. * @param timeout the time, in milliseconds, to wait for a message.
  242. *
  243. * @return an {@link OtpMsg OtpMsg} containing the header
  244. * information as well as the body of the next message waiting in
  245. * this mailbox.
  246. *
  247. * @exception Exit if a linked {@link Pid pid} has
  248. * exited or has sent an exit signal to this mailbox.
  249. *
  250. * @exception InterruptedException if no message if the method
  251. * times out before a message becomes available.
  252. **/
  253. public virtual OtpMsg receiveMsg(long timeout)
  254. {
  255. OtpMsg m;
  256. try {
  257. m = (OtpMsg) queue.get(timeout);
  258. } catch (System.Threading.ThreadInterruptedException) {
  259. m = null;
  260. }
  261. if (m == null)
  262. return null;
  263. switch (m.type())
  264. {
  265. case OtpMsg.Tag.exitTag:
  266. case OtpMsg.Tag.exit2Tag:
  267. try
  268. {
  269. Erlang.Object o = m.getMsg();
  270. throw new Erlang.Exit(o.ToString(), m.getSenderPid());
  271. }
  272. catch (Erlang.Exception)
  273. {
  274. throw new Erlang.Exit("unknown", m.getSenderPid());
  275. }
  276. default:
  277. return m;
  278. }
  279. }
  280. /*
  281. * Send a message to a remote {@link Pid pid}, representing
  282. * either another {@link OtpMbox mailbox} or an Erlang process.
  283. *
  284. * @param to the {@link Pid pid} identifying the intended
  285. * recipient of the message.
  286. *
  287. * @param msg the body of the message to send.
  288. *
  289. **/
  290. public void send(Erlang.Pid to, Erlang.Object msg)
  291. {
  292. try
  293. {
  294. System.String node = to.node();
  295. if (node.Equals(home.node()))
  296. {
  297. home.deliver(new OtpMsg(to, (Erlang.Object) (msg.clone())));
  298. }
  299. else
  300. {
  301. OtpCookedConnection conn = home.connection(node);
  302. if (conn == null)
  303. return ;
  304. conn.send(_self, to, msg);
  305. }
  306. }
  307. catch (System.Exception)
  308. {
  309. }
  310. }
  311. /*
  312. * Send a message to a named mailbox created from the same node as
  313. * this mailbox.
  314. *
  315. * @param name the registered name of recipient mailbox.
  316. *
  317. * @param msg the body of the message to send.
  318. *
  319. **/
  320. public void send(System.String name, Erlang.Object msg)
  321. {
  322. home.deliver(new OtpMsg(_self, name, (Erlang.Object) (msg.clone())));
  323. }
  324. /*
  325. * Send a message to a named mailbox created from another node.
  326. *
  327. * @param name the registered name of recipient mailbox.
  328. *
  329. * @param node the name of the remote node where the recipient
  330. * mailbox is registered.
  331. *
  332. * @param msg the body of the message to send.
  333. *
  334. **/
  335. public void send(System.String name, System.String node, Erlang.Object msg)
  336. {
  337. try
  338. {
  339. if (node.Equals(home.node()))
  340. {
  341. send(name, msg);
  342. }
  343. else
  344. {
  345. OtpCookedConnection conn = home.connection(node);
  346. if (conn == null)
  347. return ;
  348. conn.send(_self, name, msg);
  349. }
  350. }
  351. catch (System.Exception)
  352. {
  353. }
  354. }
  355. public Erlang.Object rpcCall(string node, string mod, string fun, Erlang.List args)
  356. {
  357. return this.rpcCall(node, mod, fun, args, -1);
  358. }
  359. public Erlang.Object rpcCall(string node, string mod, string fun, Erlang.List args, int timeout)
  360. {
  361. return this.rpcCall(node, new Erlang.Atom(mod), new Erlang.Atom(fun), args, -1);
  362. }
  363. public Erlang.Object rpcCall(string node, Erlang.Atom mod, Erlang.Atom fun, Erlang.List args)
  364. {
  365. return this.rpcCall(node, mod, fun, args, -1);
  366. }
  367. public Erlang.Object rpcCall(string node, Erlang.Atom mod, Erlang.Atom fun, Erlang.List args, int timeout)
  368. {
  369. sendRPC(node, mod, fun, args);
  370. return receiveRPC(timeout);
  371. }
  372. public void sendRPC(string node, string mod, string fun, Erlang.List args)
  373. {
  374. sendRPC(node, new Erlang.Atom(mod), new Erlang.Atom(fun), args);
  375. }
  376. public void sendRPC(string node, Erlang.Atom mod, Erlang.Atom fun, Erlang.List args)
  377. {
  378. sendRPC(node, mod, fun, args, new Erlang.Atom("user"));
  379. }
  380. public void sendRPC(string node, string mod, string fun, Erlang.List args, Erlang.Pid ioServer)
  381. {
  382. sendRPC(node, new Erlang.Atom(mod), new Erlang.Atom(fun), args, ioServer);
  383. }
  384. /// <summary>
  385. /// Send RPC call to a given node.
  386. /// </summary>
  387. /// <param name="node"></param>
  388. /// <param name="mod"></param>
  389. /// <param name="fun"></param>
  390. /// <param name="args"></param>
  391. /// <param name="ioServer">Either a PID or an Atom containing registered I/O server's name.</param>
  392. public void sendRPC(string node, Erlang.Atom mod, Erlang.Atom fun, Erlang.List args, Erlang.Object ioServer)
  393. {
  394. if (node.Equals(home.node()))
  395. throw new System.ArgumentException("Cannot make rpc calls on local node!");
  396. else
  397. {
  398. Erlang.Object msg = AbstractConnection.encodeRPC(_self, mod, fun, args, ioServer );
  399. OtpCookedConnection conn = home.connection(node);
  400. if (conn == null)
  401. throw new System.Exception("Cannot establish connection to node " + node);
  402. conn.send(_self, "rex", msg);
  403. }
  404. }
  405. public void sendRPCcast(string node, Erlang.Atom mod, Erlang.Atom fun, Erlang.List args)
  406. {
  407. sendRPCcast(node, mod, fun, args, new Erlang.Atom("user"));
  408. }
  409. public void sendRPCcast(string node, Erlang.Atom mod, Erlang.Atom fun, Erlang.List args, Erlang.Object ioServer)
  410. {
  411. if (node.Equals(home.node()))
  412. throw new System.ArgumentException("Cannot make rpc cast on local node!");
  413. else
  414. {
  415. Erlang.Object msg = AbstractConnection.encodeRPCcast(_self, mod, fun, args, ioServer);
  416. OtpCookedConnection conn = home.connection(node);
  417. if (conn == null)
  418. throw new System.Exception("Cannot establish connection to node " + node);
  419. conn.send(_self, "rex", msg);
  420. }
  421. }
  422. public Erlang.Object receiveRPC(int timeout)
  423. {
  424. Erlang.Object result = receive(timeout);
  425. if (result == null)
  426. return result;
  427. else
  428. {
  429. Erlang.Object rpcReply = AbstractConnection.decodeRPC(result);
  430. return rpcReply == null ? result : rpcReply;
  431. }
  432. }
  433. public string receiveIO(int timeout)
  434. {
  435. Erlang.Object result = receive(timeout);
  436. if (result == null)
  437. return null;
  438. else
  439. {
  440. return AbstractConnection.decodeIO(result);
  441. }
  442. }
  443. /*
  444. * <p> Send an exit signal to a remote {@link Pid pid}.
  445. * This method does not cause any links to be broken, except
  446. * indirectly if the remote {@link Pid pid} exits as a
  447. * result of this exit signal. </p>
  448. *
  449. * @param to the {@link Pid pid} to which the exit signal
  450. * should be sent.
  451. *
  452. * @param reason a string indicating the reason for the exit.
  453. **/
  454. // it's called exit, but it sends exit2
  455. public virtual void exit(Erlang.Pid to, System.String reason)
  456. {
  457. exit(2, to, reason);
  458. }
  459. // this function used internally when "process" dies
  460. // since Erlang discerns between exit and exit/2.
  461. private void exit(int arity, Erlang.Pid to, System.String reason)
  462. {
  463. try
  464. {
  465. System.String node = to.node();
  466. if (node.Equals(home.node()))
  467. {
  468. home.deliver(new OtpMsg(OtpMsg.Tag.exitTag, _self, to, reason));
  469. }
  470. else
  471. {
  472. OtpCookedConnection conn = home.connection(node);
  473. if (conn == null)
  474. return ;
  475. switch (arity)
  476. {
  477. case 1:
  478. conn.exit(_self, to, reason);
  479. break;
  480. case 2:
  481. conn.exit2(_self, to, reason);
  482. break;
  483. }
  484. }
  485. }
  486. catch (System.Exception)
  487. {
  488. }
  489. }
  490. /*
  491. * <p> Link to a remote mailbox or Erlang process. Links are
  492. * idempotent, calling this method multiple times will not result in
  493. * more than one link being created. </p>
  494. *
  495. * <p> If the remote process subsequently exits or the mailbox is
  496. * closed, a subsequent attempt to retrieve a message through this
  497. * mailbox will cause an {@link Exit Exit}
  498. * exception to be raised. Similarly, if the sending mailbox is
  499. * closed, the linked mailbox or process will receive an exit
  500. * signal. </p>
  501. *
  502. * <p> If the remote process cannot be reached in order to set the
  503. * link, the exception is raised immediately. </p>
  504. *
  505. * @param to the {@link Pid pid} representing the object to
  506. * link to.
  507. *
  508. * @exception Exit if the {@link Pid pid} referred
  509. * to does not exist or could not be reached.
  510. *
  511. **/
  512. public virtual void link(Erlang.Pid to)
  513. {
  514. try
  515. {
  516. System.String node = to.node();
  517. if (node.Equals(home.node()))
  518. {
  519. if (!home.deliver(new OtpMsg(OtpMsg.Tag.linkTag, _self, to)))
  520. {
  521. throw new Erlang.Exit("noproc", to);
  522. }
  523. }
  524. else
  525. {
  526. OtpCookedConnection conn = home.connection(node);
  527. if (conn != null)
  528. conn.link(_self, to);
  529. else
  530. throw new Erlang.Exit("noproc", to);
  531. }
  532. }
  533. catch (Erlang.Exit e)
  534. {
  535. throw e;
  536. }
  537. catch (System.Exception)
  538. {
  539. }
  540. links.addLink(_self, to);
  541. }
  542. /*
  543. * <p> Remove a link to a remote mailbox or Erlang process. This
  544. * method removes a link created with {@link #link link()}.
  545. * Links are idempotent; calling this method once will remove all
  546. * links between this mailbox and the remote {@link Pid
  547. * pid}. </p>
  548. *
  549. * @param to the {@link Pid pid} representing the object to
  550. * unlink from.
  551. *
  552. **/
  553. public virtual void unlink(Erlang.Pid to)
  554. {
  555. links.removeLink(_self, to);
  556. try
  557. {
  558. System.String node = to.node();
  559. if (node.Equals(home.node()))
  560. {
  561. home.deliver(new OtpMsg(OtpMsg.Tag.unlinkTag, _self, to));
  562. }
  563. else
  564. {
  565. OtpCookedConnection conn = home.connection(node);
  566. if (conn != null)
  567. conn.unlink(_self, to);
  568. }
  569. }
  570. catch (System.Exception)
  571. {
  572. }
  573. }
  574. /*
  575. * <p> Create a connection to a remote node. </p>
  576. *
  577. * <p> Strictly speaking, this method is not necessary simply to set
  578. * up a connection, since connections are created automatically
  579. * first time a message is sent to a {@link Pid pid} on the
  580. * remote node. </p>
  581. *
  582. * <p> This method makes it possible to wait for a node to come up,
  583. * however, or check that a node is still alive. </p>
  584. *
  585. * <p> This method calls a method with the same name in {@link
  586. * OtpNode#ping Otpnode} but is provided here for convenience. </p>
  587. *
  588. * @param node the name of the node to ping.
  589. *
  590. * @param timeout the time, in milliseconds, before reporting
  591. * failure.
  592. **/
  593. public virtual bool ping(System.String node, long timeout)
  594. {
  595. return home.ping(node, timeout);
  596. }
  597. /*
  598. * <p> Get a list of all known registered names on the same {@link
  599. * OtpNode node} as this mailbox. </p>
  600. *
  601. * <p> This method calls a method with the same name in {@link
  602. * OtpNode#getNames Otpnode} but is provided here for convenience.
  603. * </p>
  604. *
  605. * @return an array of Strings containing all registered names on
  606. * this {@link OtpNode node}.
  607. **/
  608. public virtual System.String[] getNames()
  609. {
  610. return home.getNames();
  611. }
  612. /*
  613. * Determine the {@link Pid pid} corresponding to a
  614. * registered name on this {@link OtpNode node}.
  615. *
  616. * <p> This method calls a method with the same name in {@link
  617. * OtpNode#whereis Otpnode} but is provided here for convenience.
  618. * </p>
  619. *
  620. * @return the {@link Pid pid} corresponding to the
  621. * registered name, or null if the name is not known on this node.
  622. **/
  623. public virtual Erlang.Pid whereis(System.String name)
  624. {
  625. return home.whereis(name);
  626. }
  627. /*
  628. * Close this mailbox.
  629. *
  630. * <p> After this operation, the mailbox will no longer be able to
  631. * receive messages. Any delivered but as yet unretrieved messages
  632. * can still be retrieved however. </p>
  633. *
  634. * <p> If there are links from this mailbox to other {@link
  635. * Pid pids}, they will be broken when this method is
  636. * called and exit signals will be sent. </p>
  637. *
  638. **/
  639. public void close()
  640. {
  641. home.closeMbox(this);
  642. }
  643. ~OtpMbox()
  644. {
  645. this.close();
  646. queue.close();
  647. }
  648. /*
  649. * Determine if two mailboxes are equal.
  650. *
  651. * @return true if both Objects are mailboxes with the same
  652. * identifying {@link Pid pids}.
  653. **/
  654. public override bool Equals(System.Object o)
  655. {
  656. if (!(o is OtpMbox))
  657. return false;
  658. OtpMbox m = (OtpMbox) o;
  659. return m._self.Equals(this._self);
  660. }
  661. public override int GetHashCode()
  662. {
  663. return _self.GetHashCode();
  664. }
  665. /*
  666. * called by OtpNode to deliver message to this mailbox.
  667. *
  668. * About exit and exit2: both cause exception to be raised upon
  669. * receive(). However exit (not 2) causes any link to be removed as
  670. * well, while exit2 leaves any links intact.
  671. */
  672. internal virtual void deliver(OtpMsg m)
  673. {
  674. switch (m.type())
  675. {
  676. case OtpMsg.Tag.linkTag:
  677. links.addLink(_self, m.getSenderPid());
  678. break;
  679. case OtpMsg.Tag.unlinkTag:
  680. links.removeLink(_self, m.getSenderPid());
  681. break;
  682. case OtpMsg.Tag.exitTag:
  683. links.removeLink(_self, m.getSenderPid());
  684. queue.put(m);
  685. break;
  686. case OtpMsg.Tag.monitorPTag:
  687. monitors[m.getSenderPid()] = m.getMsg();
  688. break;
  689. case OtpMsg.Tag.demonitorPTag:
  690. monitors.Remove(m.getSenderPid());
  691. break;
  692. case OtpMsg.Tag.monitorPexitTag:
  693. queue.put(m);
  694. break;
  695. case OtpMsg.Tag.exit2Tag:
  696. default:
  697. queue.put(m);
  698. break;
  699. }
  700. }
  701. // used to break all known links to this mbox
  702. internal virtual void breakLinks(System.String reason)
  703. {
  704. Link[] l = links.clearLinks();
  705. if (l != null)
  706. {
  707. int len = (int) (l.Length);
  708. for (int i = 0; i < len; i++)
  709. {
  710. exit(1, l[i].remote(), reason);
  711. }
  712. }
  713. }
  714. }
  715. }