/// <summary> /// Deliver messages to the recipient /// </summary> protected override void Deliver(ErlMsg msg) { if (!m_Home.Deliver(msg)) { m_Home.OnUnhandledMsg(this, msg); } }
internal void UnhandledMsg(ErlConnection conn, ErlMsg msg) { if (m_OnUnhandledMsg != null) { m_OnUnhandledMsg(conn, msg); } }
/// <summary> /// Send an RPC cast request to the remote Erlang node /// </summary> public void SendRPCcast(ErlPid from, string mod, string fun, ErlList args) { IErlObject rpc = Internal.ErlRpcServer.EncodeRPCcast(from, mod, fun, args, ConstAtoms.User); var msg = ErlMsg.RegSend(from, ConstAtoms.Rex, rpc); Send(msg); }
internal bool Deliver(ErlMsg msg) { if (msg.Recipient is ErlPid) { var node = msg.RecipientPid.Node; if (node != NodeName) { return(Deliver(node, msg)); } } var mbox = msg.Type == ErlMsg.Tag.RegSend || msg.Type == ErlMsg.Tag.RegSendTT ? m_Mailboxes[msg.RecipientName] : m_Mailboxes[msg.RecipientPid]; if (mbox == null) { return(false); } OnTrace(ErlTraceLevel.Send, Direction.Inbound, () => "{0}{1} got: {2}".Args( mbox.Self, mbox.Name.Empty ? string.Empty : " ({0})".Args( mbox.Name.ToString()), msg.Msg)); mbox.Deliver(msg); return(true); }
/// <summary> /// Deliver messages to the recipient /// </summary> protected override void Deliver(ErlMsg msg, [CallerFilePath] string file = "", [CallerLineNumber] int line = 0) { if (!m_Home.Deliver(msg, file, line)) { m_Home.OnUnhandledMsg(this, msg, file, line); } }
/// <summary> /// Used to break all known links to this mbox /// </summary> internal void BreakLinks(ErlAtom reason) { foreach (var l in m_Links.Clear()) { m_Node.Deliver(ErlMsg.Exit(m_Self, l.Pid, reason)); } }
/// <summary> /// Link to a remote mailbox or Erlang process. Links are /// idempotent, calling this method multiple times will not result in /// more than one link being created /// </summary> /// <remarks> /// If the remote process subsequently exits or the mailbox is /// closed, a subsequent attempt to retrieve a message through this /// mailbox will cause an {@link Exit Exit} /// exception to be raised. Similarly, if the sending mailbox is /// closed, the linked mailbox or process will receive an exit /// signal. /// /// If the remote process cannot be reached in order to set the /// link, the exception is raised immediately. /// </remarks> public void Link(ErlPid to) { Debug.Assert(to != m_Self); if (m_Node.Deliver(ErlMsg.Link(m_Self, to))) { m_Links.Add(to); } }
/// <summary> /// Close this mailbox /// </summary> /// <remarks> /// After this operation, the mailbox will no longer be able to /// receive messages. Any delivered but as yet unretrieved messages /// can still be retrieved however. /// /// If there are links from this mailbox to other <see cref="ErlPid"/> /// pids they will be broken when this method is /// called and exit signals will be sent. /// </remarks> internal void Close() { // Notify all registered monitors that this pid is closing foreach (var monitor in m_Monitors) { var msg = ErlMsg.MonitorPexit(m_Self, monitor.Value, monitor.Key, ErlAtom.Normal); m_Node.Deliver(monitor.Value.Node, msg); } m_Node.CloseMbox(this); m_RegName = ErlAtom.Null; }
/// <summary> /// Called to deliver message to this mailbox /// </summary> internal void Deliver(ErlMsg m) { Debug.Assert((m.Recipient is ErlPid && m.RecipientPid == m_Self) || (m.Recipient is ErlAtom && m.RecipientName == m_RegName)); switch (m.Type) { case ErlMsg.Tag.Link: m_Links.Add(m.SenderPid); break; case ErlMsg.Tag.Unlink: m_Links.Remove(m.SenderPid); break; case ErlMsg.Tag.Exit: case ErlMsg.Tag.Exit2: { m_Monitors.Remove(m.Ref); if (m_Links.Remove(m.SenderPid)) { Enqueue(m); } break; } case ErlMsg.Tag.MonitorP: m_Monitors.Add(m.Ref, m.SenderPid); break; case ErlMsg.Tag.DemonitorP: { m_Monitors.Remove(m.Ref); break; } case ErlMsg.Tag.MonitorPexit: { m_Links.Remove(m.SenderPid); if (m_Monitors.Remove(m.Ref)) { Enqueue(new ErlDown(m.Ref, m.SenderPid, m.Reason)); } break; } default: Enqueue(m); break; } }
protected internal virtual void OnUnhandledMsg(ErlConnection conn, ErlMsg msg, [CallerFilePath] string file = "", [CallerLineNumber] int line = 0) { if (UnhandledMsg != null) { UnhandledMsg(this, conn, msg); } if (LogUnhandledMsgs) { Log(MessageType.TraceErl, "OnUnhandledMsg", "Connection {0} couldn't find mbox for: {1}".Args(conn.Name, msg), file: file, line: line); } }
protected internal virtual void OnUnhandledMsg(ErlConnection conn, ErlMsg msg) { if (UnhandledMsg != null) { UnhandledMsg(this, conn, msg); } if (LogUnhandledMsgs) { App.Log.Write(new NFX.Log.Message { Type = Log.MessageType.TraceErl, Topic = CoreConsts.ERLANG_TOPIC, From = this.ToString(), Text = "Connection {0} couldn't find mbox for: {1}".Args(conn.Name, msg) }); } }
internal bool Deliver(ErlAtom node, ErlMsg msg) { if (node == NodeName) { return(Deliver(msg)); } var conn = Connection(node); if (conn == null) { return(false); } try { conn.Send(msg); } catch { return(false); } return(true); }
/// <summary> /// Used to break all known links to this mbox /// </summary> internal void BreakLinks(ErlAtom fromNode, IErlObject reason) { var links = m_Links.Remove(fromNode); foreach (var link in links) { if (link.HasPid) { m_Node.Deliver(ErlMsg.Exit(m_Self, link.Pid, reason)); } else { m_Node.Deliver(new ErlConnectionException(fromNode, reason)); } } foreach (var m in m_Monitors.Where(o => o.Value.Node == fromNode) .Where(m => m_Monitors.Remove(m.Key))) { Deliver(new ErlConnectionException(fromNode, reason)); } }
internal void Send(ErlMsg msg) { var ctrl = msg.ToCtrlTuple(SendCookie); sendBuf(ctrl, msg.Payload == null ? null : new ErlOutputStream(msg.Payload)); }
protected internal virtual void OnUnhandledMsg(ErlConnection conn, ErlMsg msg, [CallerFilePath] string file = null, [CallerLineNumber]int line = 0) { if (UnhandledMsg != null) UnhandledMsg(this, conn, msg); if (LogUnhandledMsgs) App.Log.Write(new NFX.Log.Message(null, file, line) { Type = Log.MessageType.TraceErl, Topic = CoreConsts.ERLANG_TOPIC, From = this.ToString(), Text = "Connection {0} couldn't find mbox for: {1}".Args(conn.Name, msg) }); }
/// <summary> /// Deliver messages to the recipient /// </summary> protected abstract void Deliver(ErlMsg msg, string file, int line);
/// <summary> /// Send an Erlang term to a Pid on a local or remote node /// </summary> public void Send(ErlPid dest, IErlObject msg) { base.Send(ErlMsg.Send(dest, msg, SendCookie)); }
/// <summary> /// Remove a link to a remote mailbox or Erlang process. This /// method removes a link created with <see cref="ErlLink"/> /// Links are idempotent; calling this method once will remove all /// links between this mailbox and the remote <see cref="ErlPid"/> /// </summary> public void Unlink(ErlPid to) { m_Links.Remove(to); m_Node.Deliver(ErlMsg.Unlink(m_Self, to)); }
/// <summary> /// Send a message to a remote <see cref="ErlPid"/>, representing /// either another <see cref="ErlMbox"/> or an Erlang process /// </summary> /// <returns>true if message was sent successfully</returns> public bool Send(ErlPid to, IErlObject msg) { return(Deliver(ErlMsg.Send(to, msg))); }
/// <summary> /// Send a message to a named mailbox created from another node /// </summary> public bool Send(ErlAtom node, ErlAtom name, IErlObject msg) { return(m_Node.Deliver(node, ErlMsg.RegSend(m_Self, name, msg))); }
internal static ErlTuple ToCtrlTuple(ErlMsg msg) { return(msg.ToCtrlTuple()); }
private void threadSpinCore() { if (!m_Connected) { Deliver(new ErlConnectionException(Name, StringConsts.ERL_CONN_NOT_CONNECTED_ERROR)); return; } int len; byte[] header = new byte[4]; byte[] tock = new byte[] { 0, 0, 0, 0 }; byte[] payloadBuf = new byte[1024 * 1024]; try { while (m_Home.Running && !this.DisposeStarted && !m_Done) { // don't return until we get a real message // or a failure of some kind (e.g. EXIT) // read length and read buffer must be atomic! do { // read 4 bytes - get length of incoming packet // socket.getInputStream().read(lbuf); int n; if ((n = ReadSock(header, header.Length, false)) < header.Length) { throw new ErlException(StringConsts.ERL_CONN_READ_TOO_SHORT_ERROR, n, header.Length); } len = header.ReadBEInt32(); // received tick? send tock! if (len == 0) { lock (this) { if (m_Transport != null) { (m_Transport.GetStream()).Write(tock, 0, tock.Length); } } } }while (len == 0); // tick_loop if (len > DEFAULT_MAX_PAYLOAD_LENGTH) { throw new ErlException( StringConsts.ERL_CONN_MSG_SIZE_TOO_LONG_ERROR, DEFAULT_MAX_PAYLOAD_LENGTH, len); } // got a real message (maybe) - read len bytes byte[] tmpbuf = new byte[len]; // len > payloadBuf.Length ? new byte[len] : payloadBuf; // i = socket.getInputStream().read(tmpbuf); int m = ReadSock(tmpbuf, len, true); if (m != len) { throw new ErlException(StringConsts.ERL_CONN_READ_TOO_SHORT_ERROR, m, len); } var ibuf = new ErlInputStream(tmpbuf, 0, len, checkVersion: false); if (ibuf.Read1() != PASS_THROUGH) { goto receive_loop_brk; } try { // decode the header var tmp = ibuf.Read(true); if (!(tmp is ErlTuple)) { goto receive_loop_brk; } var head = (ErlTuple)tmp; if (!(head[0] is ErlByte)) { goto receive_loop_brk; } // lets see what kind of message this is ErlMsg.Tag tag = (ErlMsg.Tag)head[0].ValueAsInt; switch (tag) { case ErlMsg.Tag.Send: case ErlMsg.Tag.SendTT: { // { SEND, Cookie, ToPid, TraceToken } if (!m_CookieOk) { // we only check this once, he can send us bad cookies later if he likes if (!(head[1] is ErlAtom)) { goto receive_loop_brk; } var cookie = (ErlAtom)head[1]; if (m_ShouldSendCookie) { if (!cookie.Equals(m_Cookie)) { cookieError(m_Home, cookie); } } else if (!cookie.Empty) { cookieError(m_Home, cookie); } m_CookieOk = true; } m_Home.OnTrace(ErlTraceLevel.Ctrl, Direction.Inbound, () => { long mark = ibuf.Position; var traceobj = ibuf.Read(true); ibuf.Position = mark; return("{0} {1} trace: {2}".Args( HeaderType(head), head.ToString(), traceobj == null ? "(null)" : traceobj.ToString())); }); var to = (ErlPid)head[2]; Deliver(new ErlMsg(tag, ErlPid.Null, to, paybuf: ibuf)); break; } case ErlMsg.Tag.RegSend: case ErlMsg.Tag.RegSendTT: { // { REG_SEND, FromPid, Cookie, ToName, TraceToken } if (!m_CookieOk) { // we only check this once, he can send us bad cookies later if he likes if (!(head[2] is ErlAtom)) { goto receive_loop_brk; } var cookie = (ErlAtom)head[2]; if (m_ShouldSendCookie) { if (!cookie.Equals(m_Cookie)) { cookieError(m_Home, cookie); } } else if (!cookie.Empty) { cookieError(m_Home, cookie); } m_CookieOk = true; } m_Home.OnTrace(ErlTraceLevel.Ctrl, Direction.Inbound, () => { long mark = ibuf.Position; var traceobj = ibuf.Read(true); ibuf.Position = mark; return("{0} {1} trace: {2}".Args( HeaderType(head), head.ToString(), traceobj == null ? "(null)" : traceobj.ToString())); }); var from = (ErlPid)head[1]; var toName = (ErlAtom)head[3]; Deliver(new ErlMsg(tag, from, toName, paybuf: ibuf)); break; } case ErlMsg.Tag.Exit: case ErlMsg.Tag.Exit2: { // { EXIT2, FromPid, ToPid, Reason } if (!(head[3] is ErlAtom)) { goto receive_loop_brk; } m_Home.OnTrace(ErlTraceLevel.Ctrl, Direction.Inbound, () => "{0} {1}".Args(HeaderType(head), head.ToString())); var from = (ErlPid)head[1]; var to = (ErlPid)head[2]; var reason = (ErlAtom)head[3]; Deliver(new ErlMsg(tag, from, to, reason: reason)); break; } case ErlMsg.Tag.ExitTT: case ErlMsg.Tag.Exit2TT: { // { EXIT2, FromPid, ToPid, TraceToken, Reason } // as above, but bifferent element number if (!(head[4] is ErlAtom)) { goto receive_loop_brk; } m_Home.OnTrace(ErlTraceLevel.Ctrl, Direction.Inbound, () => "{0} {1}".Args(HeaderType(head), head.ToString())); // TODO: print TraceToken var from = (ErlPid)head[1]; var to = (ErlPid)head[2]; var trace = (ErlTrace)head[3]; var reason = (ErlAtom)head[4]; Deliver(new ErlMsg(tag, from, to, reason: reason, trace: trace)); break; } case ErlMsg.Tag.Link: case ErlMsg.Tag.Unlink: { // {UNLINK, FromPid, ToPid} m_Home.OnTrace(ErlTraceLevel.Ctrl, Direction.Inbound, () => "{0} {1}".Args(HeaderType(head), head.ToString())); var from = (ErlPid)head[1]; var to = (ErlPid)head[2]; Deliver(new ErlMsg(tag, from, to)); break; } case ErlMsg.Tag.GroupLeader: case ErlMsg.Tag.NodeLink: { // No idea what to do with these, so we ignore them... // {GROUP_LEADER, FromPid, ToPid}, { NODELINK } // (just show trace) m_Home.OnTrace(ErlTraceLevel.Ctrl, Direction.Inbound, () => "{0} {1}".Args(HeaderType(head), head.ToString())); break; } case ErlMsg.Tag.MonitorP: case ErlMsg.Tag.DemonitorP: { // {MONITOR_P, FromPid, ToProc, Ref} // {DEMONITOR_P, FromPid, ToProc, Ref} m_Home.OnTrace(ErlTraceLevel.Ctrl, Direction.Inbound, () => "{0} {1}".Args(HeaderType(head), head.ToString())); var from = (ErlPid)head[1]; var to = (ErlPid)head[2]; var eref = (ErlRef)head[3]; Deliver(new ErlMsg(tag, from, to, eref)); break; } case ErlMsg.Tag.MonitorPexit: { // {MONITOR_P_EXIT, FromPid, ToProc, Ref, Reason} m_Home.OnTrace(ErlTraceLevel.Ctrl, Direction.Inbound, () => "{0} {1}".Args(HeaderType(head), head.ToString())); var from = (ErlPid)head[1]; var to = (ErlPid)head[2]; var eref = (ErlRef)head[3]; var reason = head[4]; Deliver(ErlMsg.MonitorPexit(from, to, eref, reason)); break; } default: // garbage? m_Home.OnTrace(ErlTraceLevel.Ctrl, Direction.Inbound, () => StringConsts.ERL_CONN_UNKNOWN_TAG_ERROR.Args( HeaderType(head), head.ToString())); goto receive_loop_brk; } } catch (Exception e) { // we have received garbage m_Home.OnTrace(ErlTraceLevel.Ctrl, Direction.Inbound, e.ToString()); Deliver(new ErlBadDataException( Name, /* "Remote has closed connection or sending garbage: " + */ e.Message)); } } receive_loop_brk: // end receive_loop // this section reachable only with break // connection went down or we have received garbage from peer Deliver(new ErlConnectionException(Name, StringConsts.ERL_CONN_INVALID_DATA_FROM_PEER_ERROR)); } catch (ErlAuthException e) { m_Home.OnTrace(ErlTraceLevel.Ctrl, Direction.Inbound, () => e.ToString()); Deliver(e); } catch (Exception e) { m_Home.OnTrace(ErlTraceLevel.Ctrl, Direction.Inbound, () => e.ToString()); Deliver(new ErlConnectionException( Name, /* "Remote has closed connection or sending garbage: " + */ e.Message)); } finally { m_Thread = null; Close(); m_Home.OnTrace(ErlTraceLevel.Ctrl, Direction.Inbound, () => "Exiting connection (thread {0})".Args(Thread.CurrentThread.Name)); } }
internal void UnhandledMsg(ErlConnection conn, ErlMsg msg) { if (m_OnUnhandledMsg != null) m_OnUnhandledMsg(conn, msg); }
internal bool Deliver(ErlMsg msg) { if (msg.Recipient is ErlPid) { var node = msg.RecipientPid.Node; if (node != NodeName) return Deliver(node, msg); } var mbox = msg.Type == ErlMsg.Tag.RegSend || msg.Type == ErlMsg.Tag.RegSendTT ? m_Mailboxes[msg.RecipientName] : m_Mailboxes[msg.RecipientPid]; if (mbox == null) return false; Trace(ErlTraceLevel.Send, Direction.Inbound, () => "{0}{1} got: {2}".Args( mbox.Self, mbox.Name.Empty ? string.Empty : " ({0})".Args(mbox.Name.ToString()), msg.Msg)); mbox.Deliver(msg); return true; }
internal bool Deliver(ErlAtom node, ErlMsg msg) { if (node == NodeName) return Deliver(msg); var conn = Connection(node); if (conn == null) return false; try { conn.Send(msg); } catch { return false; } return true; }
internal void Send(ErlMsg msg) { var ctrl = msg.ToCtrlTuple(SendCookie); sendBuf(ctrl, msg.Payload==null ? null : new ErlOutputStream(msg.Payload)); }
/// <summary> /// Send a message to a named mailbox on local node /// </summary> public bool Send(ErlPid from, ErlAtom toName, IErlObject msg) { return(Deliver(ErlMsg.RegSend(from, toName, msg))); }
/// <summary> /// Deliver messages to the recipient /// </summary> protected abstract void Deliver(ErlMsg msg);
protected internal virtual void OnUnhandledMsg(ErlConnection conn, ErlMsg msg, [CallerFilePath]string file = "", [CallerLineNumber]int line = 0) { if (UnhandledMsg != null) UnhandledMsg(this, conn, msg); if (LogUnhandledMsgs) Log(MessageType.TraceErl, "OnUnhandledMsg", "Connection {0} couldn't find mbox for: {1}".Args(conn.Name, msg), file:file, line:line); }
/// <summary> /// Deliver messages to the recipient /// </summary> protected override void Deliver(ErlMsg msg) { if (!m_Home.Deliver(msg)) m_Home.OnUnhandledMsg(this, msg); }
/// <summary> /// Deliver messages to the recipient /// </summary> protected override void Deliver(ErlMsg msg, [CallerFilePath]string file = "", [CallerLineNumber]int line = 0) { if (!m_Home.Deliver(msg, file, line)) m_Home.OnUnhandledMsg(this, msg, file, line); }
/// <summary> /// Called to deliver message to this mailbox /// </summary> internal void Deliver(ErlMsg m) { Debug.Assert((m.Recipient is ErlPid && m.RecipientPid == m_Self) || (m.Recipient is ErlAtom && m.RecipientName == m_RegName)); switch (m.Type) { case ErlMsg.Tag.Link: m_Links.Add(m.SenderPid); break; case ErlMsg.Tag.Unlink: m_Links.Remove(m.SenderPid); break; case ErlMsg.Tag.Exit: case ErlMsg.Tag.Exit2: m_Monitors.Remove(m.Ref); if (m_Links.Remove(m.SenderPid)) Enqueue(m); break; case ErlMsg.Tag.MonitorP: m_Monitors.Add(m.Ref, m.SenderPid); break; case ErlMsg.Tag.DemonitorP: m_Monitors.Remove(m.Ref); break; case ErlMsg.Tag.MonitorPexit: m_Links.Remove(m.SenderPid); if (m_Monitors.Remove(m.Ref)) Enqueue(new ErlDown(m.Ref, m.SenderPid, m.Reason)); break; case ErlMsg.Tag.Undefined: break; default: Enqueue(m); break; } }
/* * send to remote name * dest is recipient's registered name, the nodename is implied by * the choice of connection. */ public void Send(ErlPid from, ErlAtom dest, IErlObject msg) { // encode and send the message base.Send(ErlMsg.RegSend(from, dest, msg, SendCookie)); }