PageRenderTime 64ms CodeModel.GetById 37ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://github.com/babo/jungerl
C# | 682 lines | 317 code | 45 blank | 320 comment | 30 complexity | 9c767d301c11cfa40aea714357f09a1c 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. // package constructor: called by OtpNode:createMbox(name)
  80. // to create a named mbox
  81. internal OtpMbox(OtpNode home, Erlang.Pid self, System.String name)
  82. {
  83. this._self = self;
  84. this.home = home;
  85. this.name = name;
  86. this.queue = new GenericQueue();
  87. this.links = new Links(10);
  88. }
  89. // package constructor: called by OtpNode:createMbox()
  90. // to create an anonymous
  91. internal OtpMbox(OtpNode home, Erlang.Pid self):this(home, self, null)
  92. {
  93. }
  94. /*
  95. * <p> Get the identifying {@link Pid pid} associated with
  96. * this mailbox.</p>
  97. *
  98. * <p> The {@link Pid pid} associated with this mailbox
  99. * uniquely identifies the mailbox and can be used to address the
  100. * mailbox. You can send the {@link Pid pid} to a remote
  101. * communicating part so that he can know where to send his
  102. * response. </p>
  103. *
  104. * @return the self pid for this mailbox.
  105. **/
  106. public virtual Erlang.Pid self()
  107. {
  108. return _self;
  109. }
  110. /*
  111. * <p> Register or remove a name for this mailbox. Registering a
  112. * name for a mailbox enables others to send messages without
  113. * knowing the {@link Pid pid} of the mailbox. A mailbox
  114. * can have at most one name; if the mailbox already had a name,
  115. * calling this method will supercede that name. </p>
  116. *
  117. * @param name the name to register for the mailbox. Specify null to
  118. * unregister the existing name from this mailbox.
  119. *
  120. * @return true if the name was available, or false otherwise.
  121. **/
  122. public virtual bool registerName(System.String name)
  123. {
  124. lock(this)
  125. {
  126. return home.registerName(name, this);
  127. }
  128. }
  129. /*
  130. * Get the registered name of this mailbox.
  131. *
  132. * @return the registered name of this mailbox, or null if the
  133. * mailbox had no registerd name.
  134. **/
  135. public virtual System.String getName()
  136. {
  137. return this.name;
  138. }
  139. /*
  140. * Block until a message arrives for this mailbox.
  141. *
  142. * @return an {@link Object Object} representing
  143. * the body of the next message waiting in this mailbox.
  144. *
  145. * @exception Exit if a linked {@link Pid pid} has
  146. * exited or has sent an exit signal to this mailbox.
  147. *
  148. **/
  149. public virtual Erlang.Object receive()
  150. {
  151. try
  152. {
  153. return receiveMsg().getMsg();
  154. }
  155. catch (Erlang.Exit e)
  156. {
  157. throw e;
  158. }
  159. catch (System.Exception)
  160. {
  161. }
  162. return null;
  163. }
  164. /*
  165. * Wait for a message to arrive for this mailbox.
  166. *
  167. * @param timeout the time, in milliseconds, to wait for a message
  168. * before returning null.
  169. *
  170. * @return an {@link Object Object} representing
  171. * the body of the next message waiting in this mailbox.
  172. *
  173. * @exception Exit if a linked {@link Pid pid} has
  174. * exited or has sent an exit signal to this mailbox.
  175. *
  176. **/
  177. public virtual Erlang.Object receive(long timeout)
  178. {
  179. try
  180. {
  181. OtpMsg m = receiveMsg(timeout);
  182. if (m != null)
  183. return m.getMsg();
  184. }
  185. catch (Erlang.Exit e)
  186. {
  187. throw e;
  188. }
  189. catch (System.Exception)
  190. {
  191. }
  192. return null;
  193. }
  194. /*
  195. * Block until a message arrives for this mailbox.
  196. *
  197. * @return a byte array representing the still-encoded body of the
  198. * next message waiting in this mailbox.
  199. *
  200. * @exception Exit if a linked {@link Pid pid} has
  201. * exited or has sent an exit signal to this mailbox.
  202. *
  203. **/
  204. public virtual OtpInputStream receiveBuf()
  205. {
  206. return receiveMsg().getMsgBuf();
  207. }
  208. /*
  209. * Wait for a message to arrive for this mailbox.
  210. *
  211. * @param timeout the time, in milliseconds, to wait for a message
  212. * before returning null.
  213. *
  214. * @return a byte array representing the still-encoded body of the
  215. * next message waiting in this mailbox.
  216. *
  217. * @exception Exit if a linked {@link Pid pid} has
  218. * exited or has sent an exit signal to this mailbox.
  219. *
  220. * @exception InterruptedException if no message if the method
  221. * times out before a message becomes available.
  222. **/
  223. public virtual OtpInputStream receiveBuf(long timeout)
  224. {
  225. OtpMsg m = receiveMsg(timeout);
  226. if (m != null)
  227. return m.getMsgBuf();
  228. return null;
  229. }
  230. /*
  231. * Block until a message arrives for this mailbox.
  232. *
  233. * @return an {@link OtpMsg OtpMsg} containing the header
  234. * information as well as the body of the next message waiting in
  235. * this mailbox.
  236. *
  237. * @exception Exit if a linked {@link Pid pid} has
  238. * exited or has sent an exit signal to this mailbox.
  239. *
  240. **/
  241. public virtual OtpMsg receiveMsg()
  242. {
  243. OtpMsg m = (OtpMsg) (queue.get());
  244. switch (m.type())
  245. {
  246. case OtpMsg.exitTag:
  247. case OtpMsg.exit2Tag:
  248. try
  249. {
  250. Erlang.Object o = m.getMsg();
  251. throw new Erlang.Exit(o.ToString(), m.getSenderPid());
  252. }
  253. catch (Erlang.DecodeException)
  254. {
  255. throw new Erlang.Exit("unknown", m.getSenderPid());
  256. }
  257. goto default;
  258. default:
  259. return m;
  260. }
  261. }
  262. /*
  263. * Wait for a message to arrive for this mailbox.
  264. *
  265. * @param timeout the time, in milliseconds, to wait for a message.
  266. *
  267. * @return an {@link OtpMsg OtpMsg} containing the header
  268. * information as well as the body of the next message waiting in
  269. * this mailbox.
  270. *
  271. * @exception Exit if a linked {@link Pid pid} has
  272. * exited or has sent an exit signal to this mailbox.
  273. *
  274. * @exception InterruptedException if no message if the method
  275. * times out before a message becomes available.
  276. **/
  277. public virtual OtpMsg receiveMsg(long timeout)
  278. {
  279. OtpMsg m = (OtpMsg) (queue.get(timeout));
  280. if (m == null)
  281. return null;
  282. switch (m.type())
  283. {
  284. case OtpMsg.exitTag:
  285. case OtpMsg.exit2Tag:
  286. try
  287. {
  288. Erlang.Object o = m.getMsg();
  289. throw new Erlang.Exit(o.ToString(), m.getSenderPid());
  290. }
  291. catch (Erlang.DecodeException)
  292. {
  293. throw new Erlang.Exit("unknown", m.getSenderPid());
  294. }
  295. goto default;
  296. default:
  297. return m;
  298. }
  299. }
  300. /*
  301. * Send a message to a remote {@link Pid pid}, representing
  302. * either another {@link OtpMbox mailbox} or an Erlang process.
  303. *
  304. * @param to the {@link Pid pid} identifying the intended
  305. * recipient of the message.
  306. *
  307. * @param msg the body of the message to send.
  308. *
  309. **/
  310. public virtual void send(Erlang.Pid to, Erlang.Object msg)
  311. {
  312. try
  313. {
  314. System.String node = to.node();
  315. if (node.Equals(home.node()))
  316. {
  317. home.deliver(new OtpMsg(to, (Erlang.Object) (msg.clone())));
  318. }
  319. else
  320. {
  321. OtpCookedConnection conn = home.getConnection(node);
  322. if (conn == null)
  323. return ;
  324. conn.send(_self, to, msg);
  325. }
  326. }
  327. catch (System.Exception)
  328. {
  329. }
  330. }
  331. /*
  332. * Send a message to a named mailbox created from the same node as
  333. * this mailbox.
  334. *
  335. * @param name the registered name of recipient mailbox.
  336. *
  337. * @param msg the body of the message to send.
  338. *
  339. **/
  340. public virtual void send(System.String name, Erlang.Object msg)
  341. {
  342. home.deliver(new OtpMsg(_self, name, (Erlang.Object) (msg.clone())));
  343. }
  344. /*
  345. * Send a message to a named mailbox created from another node.
  346. *
  347. * @param name the registered name of recipient mailbox.
  348. *
  349. * @param node the name of the remote node where the recipient
  350. * mailbox is registered.
  351. *
  352. * @param msg the body of the message to send.
  353. *
  354. **/
  355. public virtual void send(System.String name, System.String node, Erlang.Object msg)
  356. {
  357. try
  358. {
  359. if (node.Equals(home.node()))
  360. {
  361. send(name, msg);
  362. }
  363. else
  364. {
  365. OtpCookedConnection conn = home.getConnection(node);
  366. if (conn == null)
  367. return ;
  368. conn.send(_self, name, msg);
  369. }
  370. }
  371. catch (System.Exception)
  372. {
  373. }
  374. }
  375. /*
  376. * <p> Send an exit signal to a remote {@link Pid pid}.
  377. * This method does not cause any links to be broken, except
  378. * indirectly if the remote {@link Pid pid} exits as a
  379. * result of this exit signal. </p>
  380. *
  381. * @param to the {@link Pid pid} to which the exit signal
  382. * should be sent.
  383. *
  384. * @param reason a string indicating the reason for the exit.
  385. **/
  386. // it's called exit, but it sends exit2
  387. public virtual void exit(Erlang.Pid to, System.String reason)
  388. {
  389. exit(2, to, reason);
  390. }
  391. // this function used internally when "process" dies
  392. // since Erlang discerns between exit and exit/2.
  393. private void exit(int arity, Erlang.Pid to, System.String reason)
  394. {
  395. try
  396. {
  397. System.String node = to.node();
  398. if (node.Equals(home.node()))
  399. {
  400. home.deliver(new OtpMsg(OtpMsg.exitTag, _self, to, reason));
  401. }
  402. else
  403. {
  404. OtpCookedConnection conn = home.getConnection(node);
  405. if (conn == null)
  406. return ;
  407. switch (arity)
  408. {
  409. case 1:
  410. conn.exit(_self, to, reason);
  411. break;
  412. case 2:
  413. conn.exit2(_self, to, reason);
  414. break;
  415. }
  416. }
  417. }
  418. catch (System.Exception)
  419. {
  420. }
  421. }
  422. /*
  423. * <p> Link to a remote mailbox or Erlang process. Links are
  424. * idempotent, calling this method multiple times will not result in
  425. * more than one link being created. </p>
  426. *
  427. * <p> If the remote process subsequently exits or the mailbox is
  428. * closed, a subsequent attempt to retrieve a message through this
  429. * mailbox will cause an {@link Exit Exit}
  430. * exception to be raised. Similarly, if the sending mailbox is
  431. * closed, the linked mailbox or process will receive an exit
  432. * signal. </p>
  433. *
  434. * <p> If the remote process cannot be reached in order to set the
  435. * link, the exception is raised immediately. </p>
  436. *
  437. * @param to the {@link Pid pid} representing the object to
  438. * link to.
  439. *
  440. * @exception Exit if the {@link Pid pid} referred
  441. * to does not exist or could not be reached.
  442. *
  443. **/
  444. public virtual void link(Erlang.Pid to)
  445. {
  446. try
  447. {
  448. System.String node = to.node();
  449. if (node.Equals(home.node()))
  450. {
  451. if (!home.deliver(new OtpMsg(OtpMsg.linkTag, _self, to)))
  452. {
  453. throw new Erlang.Exit("noproc", to);
  454. }
  455. }
  456. else
  457. {
  458. OtpCookedConnection conn = home.getConnection(node);
  459. if (conn != null)
  460. conn.link(_self, to);
  461. else
  462. throw new Erlang.Exit("noproc", to);
  463. }
  464. }
  465. catch (Erlang.Exit e)
  466. {
  467. throw e;
  468. }
  469. catch (System.Exception)
  470. {
  471. }
  472. links.addLink(_self, to);
  473. }
  474. /*
  475. * <p> Remove a link to a remote mailbox or Erlang process. This
  476. * method removes a link created with {@link #link link()}.
  477. * Links are idempotent; calling this method once will remove all
  478. * links between this mailbox and the remote {@link Pid
  479. * pid}. </p>
  480. *
  481. * @param to the {@link Pid pid} representing the object to
  482. * unlink from.
  483. *
  484. **/
  485. public virtual void unlink(Erlang.Pid to)
  486. {
  487. links.removeLink(_self, to);
  488. try
  489. {
  490. System.String node = to.node();
  491. if (node.Equals(home.node()))
  492. {
  493. home.deliver(new OtpMsg(OtpMsg.unlinkTag, _self, to));
  494. }
  495. else
  496. {
  497. OtpCookedConnection conn = home.getConnection(node);
  498. if (conn != null)
  499. conn.unlink(_self, to);
  500. }
  501. }
  502. catch (System.Exception)
  503. {
  504. }
  505. }
  506. /*
  507. * <p> Create a connection to a remote node. </p>
  508. *
  509. * <p> Strictly speaking, this method is not necessary simply to set
  510. * up a connection, since connections are created automatically
  511. * first time a message is sent to a {@link Pid pid} on the
  512. * remote node. </p>
  513. *
  514. * <p> This method makes it possible to wait for a node to come up,
  515. * however, or check that a node is still alive. </p>
  516. *
  517. * <p> This method calls a method with the same name in {@link
  518. * OtpNode#ping Otpnode} but is provided here for convenience. </p>
  519. *
  520. * @param node the name of the node to ping.
  521. *
  522. * @param timeout the time, in milliseconds, before reporting
  523. * failure.
  524. **/
  525. public virtual bool ping(System.String node, long timeout)
  526. {
  527. return home.ping(node, timeout);
  528. }
  529. /*
  530. * <p> Get a list of all known registered names on the same {@link
  531. * OtpNode node} as this mailbox. </p>
  532. *
  533. * <p> This method calls a method with the same name in {@link
  534. * OtpNode#getNames Otpnode} but is provided here for convenience.
  535. * </p>
  536. *
  537. * @return an array of Strings containing all registered names on
  538. * this {@link OtpNode node}.
  539. **/
  540. public virtual System.String[] getNames()
  541. {
  542. return home.getNames();
  543. }
  544. /*
  545. * Determine the {@link Pid pid} corresponding to a
  546. * registered name on this {@link OtpNode node}.
  547. *
  548. * <p> This method calls a method with the same name in {@link
  549. * OtpNode#whereis Otpnode} but is provided here for convenience.
  550. * </p>
  551. *
  552. * @return the {@link Pid pid} corresponding to the
  553. * registered name, or null if the name is not known on this node.
  554. **/
  555. public virtual Erlang.Pid whereis(System.String name)
  556. {
  557. return home.whereis(name);
  558. }
  559. /*
  560. * Close this mailbox.
  561. *
  562. * <p> After this operation, the mailbox will no longer be able to
  563. * receive messages. Any delivered but as yet unretrieved messages
  564. * can still be retrieved however. </p>
  565. *
  566. * <p> If there are links from this mailbox to other {@link
  567. * Pid pids}, they will be broken when this method is
  568. * called and exit signals will be sent. </p>
  569. *
  570. **/
  571. public virtual void close()
  572. {
  573. home.closeMbox(this);
  574. }
  575. ~OtpMbox()
  576. {
  577. this.close();
  578. queue.flush();
  579. }
  580. /*
  581. * Determine if two mailboxes are equal.
  582. *
  583. * @return true if both Objects are mailboxes with the same
  584. * identifying {@link Pid pids}.
  585. **/
  586. public override bool Equals(System.Object o)
  587. {
  588. if (!(o is OtpMbox))
  589. return false;
  590. OtpMbox m = (OtpMbox) o;
  591. return m._self.Equals(this._self);
  592. }
  593. public override int GetHashCode()
  594. {
  595. return _self.GetHashCode();
  596. }
  597. /*
  598. * called by OtpNode to deliver message to this mailbox.
  599. *
  600. * About exit and exit2: both cause exception to be raised upon
  601. * receive(). However exit (not 2) causes any link to be removed as
  602. * well, while exit2 leaves any links intact.
  603. */
  604. internal virtual void deliver(OtpMsg m)
  605. {
  606. switch (m.type())
  607. {
  608. case OtpMsg.linkTag:
  609. links.addLink(_self, m.getSenderPid());
  610. break;
  611. case OtpMsg.unlinkTag:
  612. links.removeLink(_self, m.getSenderPid());
  613. break;
  614. case OtpMsg.exitTag:
  615. links.removeLink(_self, m.getSenderPid());
  616. queue.put(m);
  617. break;
  618. case OtpMsg.exit2Tag: default:
  619. queue.put(m);
  620. break;
  621. }
  622. }
  623. // used to break all known links to this mbox
  624. internal virtual void breakLinks(System.String reason)
  625. {
  626. Link[] l = links.clearLinks();
  627. if (l != null)
  628. {
  629. int len = (int) (l.Length);
  630. for (int i = 0; i < len; i++)
  631. {
  632. exit(1, l[i].remote(), reason);
  633. }
  634. }
  635. }
  636. }
  637. }