/** * When we get a reply ack, we can remove the item from our cache, * we know the other guy got our reply */ protected void HandleReplyAck(ReqrepType rt, int idnum, MemBlock err_data, ISender ret_path) { RequestKey rk = new RequestKey(idnum, ret_path); lock ( _sync ) { /** * This is not completely safe, but probably fine. Consider the * case where: * A -(req)-> B * A timeout but B does get the req * A <-(rep)- B * A -(req)-> B (these cross in flight) * A -(repack)-> B * * but then the repack passes the req retransmission (out of order * delivery) * * This is unlikely, but we could improve it. * @todo improve the reply caching algorithm */ ReplyState rs = (ReplyState)_reply_cache[rk]; if (rs != null) { ReleaseReplyState(rs); } } }
/** Forget all state associated with this ReplyState * Not synchronized! You have to hold the lock! */ protected void ReleaseReplyState(ReplyState rs) { _reply_cache.Remove(rs.RequestKey); ReplyState tmp_rs; _reply_id_table.TryTake(rs.LocalID, out tmp_rs); }
// Methods ///// /** Create a ReplyState for a new Request * Note, this is not synchronized, you must hold the lock when calling! */ protected ReplyState GenerateReplyState(PType prefix, RequestKey rk) { var rs = new ReplyState(_prefix, rk); _reply_cache[rk] = rs; rs.LocalID = _reply_id_table.GenerateID(rs); return(rs); }
public Reply(Topic topic, String subject, String content, ReplyState state) : base(subject, content, null) { if (topic == null) { throw new ArgumentNullException(nameof(topic)); } this.TopicId = topic.Id; this.State = state; }
public Reply(Reply reply, String subject, String content, ReplyState state) : base(subject, content, null) { if (reply == null) { throw new ArgumentNullException(nameof(reply)); } this.ReplyToId = reply.Id; this.TopicId = reply.TopicId; this.State = state; }
protected void HandleRequest(ReqrepType rt, int idnum, MemBlock rest, ISender retpath) { /** * Lets see if we have been asked this question before */ ReplyState rs = null; bool resend = false; #if REQREP_DEBUG Console.Error.WriteLine("[ReqrepManager: {0}] Receiving request id: {1}, from: {2}", _info, idnum, retpath); #endif RequestKey rk = new RequestKey(idnum, retpath); lock ( _sync ) { rs = (ReplyState)_reply_cache[rk]; if (rs == null) { rs = GenerateReplyState(_prefix, rk); } else { resend = true; } } if (resend) { //This is an old request: rs.Resend(); } else { //This is a new request: try { _sub.Handle(rest, rs); } catch { lock ( _sync ) { ReleaseReplyState(rs); } //This didn't work out: try { MemBlock err_data = MemBlock.Reference( new byte[] { (byte)ReqrepError.HandlerFailure }); ICopyable reply = MakeRequest(ReqrepType.Error, idnum, err_data); retpath.Send(reply); } catch { //If this fails, we may think about logging. //The return path could fail, that's the only obvious exception ///@todo log exception } } } }
// Methods ///// /** Create a ReplyState for a new Request * Note, this is not synchronized, you must hold the lock when calling! */ protected ReplyState GenerateReplyState(PType prefix, RequestKey rk) { var rs = new ReplyState(_prefix, rk); _reply_cache[rk] = rs; rs.LocalID = _reply_id_table.GenerateID(rs); return rs; }
protected void HandleRequest(ReqrepType rt, int idnum, MemBlock rest, ISender retpath) { /** * Lets see if we have been asked this question before */ ReplyState rs = null; bool resend = false; #if REQREP_DEBUG Console.Error.WriteLine("[ReqrepManager: {0}] Receiving request id: {1}, from: {2}", _info, idnum, retpath); #endif RequestKey rk = new RequestKey(idnum, retpath); lock( _sync ) { rs = (ReplyState)_reply_cache[rk]; if( rs == null ) { rs = new ReplyState(rk); //Add the new reply state before we drop the lock _reply_cache[rk] = rs; } else { resend = true; } } if( resend ) { //This is an old request: rs.Resend(); } else { //This is a new request: try { _sub.Handle(rest, rs); } catch { lock( _sync ) { _reply_cache.Remove( rs.RequestKey ); } //This didn't work out: try { MemBlock err_data = MemBlock.Reference( new byte[]{ (byte) ReqrepError.HandlerFailure } ); ICopyable reply = MakeRequest(ReqrepType.Error, idnum, err_data); retpath.Send(reply); } catch { //If this fails, we may think about logging. //The return path could fail, that's the only obvious exception ///@todo log exception } } } }
/** * This method listens for the HeartBeatEvent from the * node and checks for timeouts. */ public void TimeoutChecker(object o, EventArgs args) { DateTime now = DateTime.UtcNow; TimeSpan interval = now - _last_check; ArrayList timeout_hands = null; ArrayList to_resend = null; ArrayList to_ack = null; ArrayList reps_to_resend = null; if (interval > _edge_reqtimeout || interval > _nonedge_reqtimeout) { //Here is a list of all the handlers for the requests that timed out lock ( _sync ) { _last_check = now; foreach (RequestState reqs in _req_state_table) { if (reqs.IsTimeToAct(now)) { //We need to act: if (reqs.IsTimedOut) { //We have timed out. if (timeout_hands == null) { timeout_hands = new ArrayList(); } timeout_hands.Add(reqs); } else if (reqs.NeedToResend) { ///@todo improve the logic of resending to be less wasteful if (to_resend == null) { to_resend = new ArrayList(); } to_resend.Add(reqs); } } } //Clean up the req_state_table: if (timeout_hands != null) { RequestState tmprs; foreach (RequestState reqs in timeout_hands) { _req_state_table.TryTake(reqs.RequestID, out tmprs); } } //Look for any Replies it might be time to clean: foreach (DictionaryEntry de in _reply_cache) { ReplyState reps = (ReplyState)de.Value; TimeSpan reptimeout; if (reps.ReturnPath is Edge) { reptimeout = _edge_reqtimeout; } else { reptimeout = _nonedge_reqtimeout; } if (reps.HaveSent) { /* * See if we need to resend our reply */ if (now - reps.RepDate > reptimeout) { //We have already sent and we've kept it for a while... if (reps.IncrementRepTimeouts() <= _MAX_RESENDS) { if (reps.HaveSentAck) { /* * If we sent an ack, we must keep sending the reply, until * we get a reply ack */ if (reps_to_resend == null) { reps_to_resend = new ArrayList(); } reps_to_resend.Add(reps); } } else { /* * This reply has timed out: */ ReleaseReplyState(reps); } } } else if (false == reps.HaveSentAck) { //This one is taking a long time to answer, just ack it: if (to_ack == null) { to_ack = new ArrayList(); } to_ack.Add(reps); } } } } else { //At each heartbeat check to see if we need send request acks: lock (_sync) { foreach (DictionaryEntry de in _reply_cache) { ReplyState reps = (ReplyState)de.Value; if ((false == reps.HaveSentAck) && (false == reps.HaveSent)) { //This one is taking a long time to answer, just ack it: if (to_ack == null) { to_ack = new ArrayList(); } to_ack.Add(reps); } } } } /* * It is important not to hold the lock while we call * functions that could result in this object being * accessed. * * We have released the lock, now we can send the packets: */ if (to_resend != null) { foreach (RequestState req in to_resend) { try { req.Send(); } catch (SendException sx) { if (sx.IsTransient) { /* * Just ignore it and wait until later */ //This send didn't work, but maybe it will next time, who knows... req.ReplyHandler.HandleError(this, req.RequestID, ReqrepError.Send, null, req.UserState); } else { /* * This is a permanent failure, we don't have a way to denote * permanent failures currently, so let's just pass it on as a * Timeout (which is a kind of permanent failure). */ StopRequest(req.RequestID, req.ReplyHandler); req.ReplyHandler.HandleError(this, req.RequestID, ReqrepError.Timeout, null, req.UserState); } } catch { //This send didn't work, but maybe it will next time, who knows... req.ReplyHandler.HandleError(this, req.RequestID, ReqrepError.Send, null, req.UserState); } } } /* * Once we have released the lock, tell the handlers * about the timeout that have occured */ if (timeout_hands != null) { foreach (RequestState reqs in timeout_hands) { reqs.ReplyHandler.HandleError(this, reqs.RequestID, ReqrepError.Timeout, null, reqs.UserState); } } /* * Send acks for those that have been waiting for a long time */ if (to_ack != null) { foreach (ReplyState reps in to_ack) { try { reps.SendAck(); } catch (Exception) { ///@todo, log this exception. } } } /* * Resend replies for unacked replies */ if (reps_to_resend != null) { foreach (ReplyState reps in reps_to_resend) { reps.Resend(); } } }
public RpcReplyMessage(byte[] recieved) { Type = MessageType.REPLY; int pos = sizeof(int); state = (ReplyState)NetUtils.ToIntFromBigEndian(recieved, sizeof(int)); pos += sizeof(int); switch (state) { case ReplyState.MSG_ACCEPTED: #region Accepted ServerVerifier = new Authentication(recieved, pos); pos += ServerVerifier.Size; if (ServerVerifier.Flavor == AuthFlavor.AUTH_NONE) { acceptState = (ReplyAcceptState)NetUtils.ToIntFromBigEndian(recieved, pos); pos += sizeof(int); switch (acceptState) { case ReplyAcceptState.SUCCESS: Result = new byte[recieved.Length - pos]; Buffer.BlockCopy(recieved, pos, Result, 0, recieved.Length - pos); break; case ReplyAcceptState.PROG_MISMATCH: PROGvLow = (uint)NetUtils.ToIntFromBigEndian(recieved, pos); pos += sizeof(int); PROGvHigh = (uint)NetUtils.ToIntFromBigEndian(recieved, pos); break; case ReplyAcceptState.PROG_UNAVAIL: case ReplyAcceptState.PROC_UNAVAIL: case ReplyAcceptState.GARBAGE_ARGS: case ReplyAcceptState.SYSTEM_ERR: break; default: throw new ArgumentException("RpcReplyMessage. Wrong Accept State."); } } else { throw new ArgumentException("RPCv2. Library not support authentication."); } #endregion break; case ReplyState.MSG_DENIED: #region Denied rejectState = (ReplyRejectState)NetUtils.ToIntFromBigEndian(recieved, pos); pos += sizeof(int); switch (rejectState) { case ReplyRejectState.RPC_MISMATCH: RPCvLow = (uint)NetUtils.ToIntFromBigEndian(recieved, pos); pos += sizeof(int); RPCvHigh = (uint)NetUtils.ToIntFromBigEndian(recieved, pos); break; case ReplyRejectState.AUTH_ERROR: authState = (AuthenticationState)NetUtils.ToIntFromBigEndian(recieved, pos); break; default: throw new ArgumentException("RpcReplyMessage. Wrong Reject State."); } #endregion break; default: throw new ArgumentException("RpcReplyMessage. Wrong Message Status."); } }