private void CheckRemoteInbox(string key) { var inbox = this; var count = cluster.QueueLength(key); while (count > 0) { ActorInboxCommon.GetNextMessage(cluster, actor.Id, key).IfSome( x => iter(x, (dto, msg) => { try { switch (msg.MessageType) { case Message.Type.System: ActorInboxCommon.SystemMessageInbox(actor, inbox, (SystemMessage)msg, parent); break; case Message.Type.User: ActorInboxCommon.UserMessageInbox(actor, inbox, (UserControlMessage)msg, parent); break; case Message.Type.UserControl: ActorInboxCommon.UserMessageInbox(actor, inbox, (UserControlMessage)msg, parent); break; } } catch (Exception e) { ActorContext.WithContext(new ActorItem(actor, inbox, actor.Flags), parent, dto.Sender, msg as ActorRequest, msg, () => replyErrorIfAsked(e)); tell(ActorContext.DeadLetters, DeadLetter.create(dto.Sender, actor.Id, e, "Remote message inbox.", msg)); logSysErr(e); } finally { cluster.Dequeue <RemoteMessageDTO>(key); } })); count--; } }
public Option <T> PreProcessMessageContent(object message) { if (message == null) { tell(ActorContext.DeadLetters, DeadLetter.create(Sender, Self, "Message is null for tell (expected " + typeof(T) + ")", message)); return(None); } if (typeof(T) != typeof(string) && message is string) { try { // This allows for messages to arrive from JS and be dealt with at the endpoint // (where the type is known) rather than the gateway (where it isn't) return(Some(JsonConvert.DeserializeObject <T>((string)message))); } catch { tell(ActorContext.DeadLetters, DeadLetter.create(Sender, Self, "Invalid message type for tell (expected " + typeof(T) + ")", message)); return(None); } } if (!typeof(T).IsAssignableFrom(message.GetType())) { tell(ActorContext.DeadLetters, DeadLetter.create(Sender, Self, "Invalid message type for tell (expected " + typeof(T) + ")", message)); return(None); } return(Some((T)message)); }
InboxDirective RunMessageDirective( ProcessId pid, ProcessId sender, StrategyDecision decision, Exception e, object message ) { var directive = decision.MessageDirective; switch (directive.Type) { case MessageDirectiveType.ForwardToParent: tell(pid.Parent(), message, sender); return(InboxDirective.Default); case MessageDirectiveType.ForwardToSelf: tell(pid, message, sender); return(InboxDirective.Default); case MessageDirectiveType.ForwardToProcess: tell((directive as ForwardToProcess).ProcessId, message, sender); return(InboxDirective.Default); case MessageDirectiveType.StayInQueue: return(InboxDirective.PushToFrontOfQueue); default: tell(ActorContext.DeadLetters, DeadLetter.create(sender, pid, e, "Process error: ", message)); return(InboxDirective.Default); } }
/// <summary> /// Forward the current message to dead-letters (and wrap it in a contextual dead-letter /// structre) /// </summary> /// <param name="ex">Exception that caused the dead-letter</param> public static Unit dead(Exception ex, SystemName system = default(SystemName)) => tell( ActorContext.System(system).DeadLetters, DeadLetter.create( Sender, Self, ex, ActorContext.Request.CurrentMsg ));
/// <summary> /// Forward the current message to dead-letters (and wrap it in a contextual dead-letter /// structre) /// </summary> /// <param name="reason">Reason for the dead-letter</param> public static Unit dead(string reason, SystemName system = default(SystemName)) => tell( ActorContext.System(system).DeadLetters, DeadLetter.create( Sender, Self, reason, ActorContext.Request.CurrentMsg ));
/// <summary> /// Forward a message to dead-letters (and wrap it in a contextual dead-letter /// structre) /// </summary> /// <param name="message">Dead letter message</param> /// <param name="ex">Exception that caused the dead-letter</param> public static Unit dead(object message, Exception ex, SystemName system = default(SystemName)) => tell( ActorContext.System(system).DeadLetters, DeadLetter.create( Sender, Self, ex, message ));
/// <summary> /// Forward the current message to dead-letters (and wrap it in a contextual dead-letter /// structre) /// </summary> /// <param name="ex">Exception that caused the dead-letter</param> public static Unit dead(Exception ex) => tell( ActorContext.DeadLetters, DeadLetter.create( Sender, Self, ex, ActorContext.CurrentMsg ));
/// <summary> /// Forward the current message to dead-letters (and wrap it in a contextual dead-letter /// structre) /// </summary> /// <param name="reason">Reason for the dead-letter</param> public static Unit dead(string reason) => tell( ActorContext.DeadLetters, DeadLetter.create( Sender, Self, reason, ActorContext.CurrentMsg ));
/// <summary> /// Forward a message to dead-letters (and wrap it in a contextual dead-letter /// structre) /// </summary> /// <param name="message">Dead letter message</param> /// <param name="ex">Exception that caused the dead-letter</param> public static Unit dead(object message, Exception ex) => tell( ActorContext.DeadLetters, DeadLetter.create( Sender, Self, ex, message ));
/// <summary> /// Forward a message to dead-letters (and wrap it in a contextual dead-letter /// structre) /// </summary> /// <param name="message">Dead letter message</param> /// <param name="reason">Reason for the dead-letter</param> public static Unit dead(object message, string reason) => tell( ActorContext.DeadLetters, DeadLetter.create( Sender, Self, reason, message ));
/// <summary> /// Forward a message to dead-letters (and wrap it in a contextual dead-letter /// structre) /// </summary> /// <param name="message">Dead letter message</param> /// <param name="reason">Reason for the dead-letter</param> public static Unit dead(object message, string reason, SystemName system = default(SystemName)) => tell( ActorContext.System(system).DeadLetters, DeadLetter.create( Sender, Self, reason, message ));
void CheckRemoteInbox(string key, bool pausable) { var inbox = this; var count = cluster?.QueueLength(key) ?? 0; while (count > 0 && (!pausable || !IsPaused)) { var directive = InboxDirective.Default; ActorInboxCommon.GetNextMessage(cluster, actor.Id, key).IfSome( x => iter(x, (dto, msg) => { try { switch (msg.MessageType) { case Message.Type.User: directive = ActorInboxCommon.UserMessageInbox(actor, inbox, (UserControlMessage)msg, parent); break; case Message.Type.UserControl: directive = ActorInboxCommon.UserMessageInbox(actor, inbox, (UserControlMessage)msg, parent); break; } } catch (Exception e) { var session = msg.SessionId == null ? None : Some(new SessionId(msg.SessionId)); ActorContext.System(actor.Id).WithContext(new ActorItem(actor, inbox, actor.Flags), parent, dto.Sender, msg as ActorRequest, msg, session, () => replyErrorIfAsked(e)); tell(ActorContext.System(actor.Id).DeadLetters, DeadLetter.create(dto.Sender, actor.Id, e, "Remote message inbox.", msg)); logSysErr(e); } finally { if ((directive & InboxDirective.Pause) != 0) { IsPaused = true; directive = directive & (~InboxDirective.Pause); } if (directive == InboxDirective.Default) { cluster?.Dequeue <RemoteMessageDTO>(key); } } })); if (directive == InboxDirective.Default) { count--; } } }
public static Option <Tuple <RemoteMessageDTO, Message> > GetNextMessage(ICluster cluster, ProcessId self, string key) { if (cluster == null) { return(None); } Message msg = null; RemoteMessageDTO dto = null; dto = null; do { dto = cluster.Peek <RemoteMessageDTO>(key); if (dto == null) { // Queue is empty return(None); } if (dto.Tag == 0 && dto.Type == 0) { // Message is bad cluster.Dequeue <RemoteMessageDTO>(key); tell(ActorContext.System(self).DeadLetters, DeadLetter.create(dto.Sender, self, null, "Failed to deserialise message: ", dto)); if (cluster.QueueLength(key) == 0) { return(None); } } }while (dto == null || dto.Tag == 0 || dto.Type == 0); try { msg = MessageSerialiser.DeserialiseMsg(dto, self); msg.SessionId = dto.SessionId; } catch (Exception e) { // Message can't be deserialised cluster.Dequeue <RemoteMessageDTO>(key); tell(ActorContext.System(self).DeadLetters, DeadLetter.create(dto.Sender, self, e, "Failed to deserialise message: ", msg)); return(None); } return(Some(Tuple(dto, msg))); }
private FSharpMailboxProcessor <TMsg> StartMailbox <TMsg>(Actor <S, T> actor, string key, CancellationToken cancelToken, Action <Actor <S, T>, IActorInbox, TMsg, ActorItem> handler) where TMsg : Message => ActorInboxCommon.Mailbox <TMsg>(cancelToken, msg => { try { handler(actor, this, msg, parent); } catch (Exception e) { replyErrorIfAsked(e); tell(ActorContext.DeadLetters, DeadLetter.create(ActorContext.Sender, actor.Id, e, "Remote message inbox.", msg)); logSysErr(e); } finally { // Remove from queue, then see if there are any more to process. CheckRemoteInbox(key, cluster, actor.Id, sysInbox, userInbox); } });
FSharpMailboxProcessor <TMsg> StartMailbox <TMsg>(Actor <S, T> actor, string key, CancellationToken cancelToken, Func <Actor <S, T>, IActorInbox, TMsg, ActorItem, InboxDirective> handler, bool pausable) where TMsg : Message => ActorInboxCommon.Mailbox <TMsg>(cancelToken, msg => { try { var directive = handler(actor, this, msg, parent); // TODO: Push msg to front of queue if directive requests it } catch (Exception e) { replyErrorIfAsked(e); tell(ActorContext.System(actor.Id).DeadLetters, DeadLetter.create(ActorContext.Request.Sender, actor.Id, e, "Remote message inbox.", msg)); logSysErr(e); } finally { // Remove from queue, then see if there are any more to process. CheckRemoteInbox(key, cluster, actor.Id, sysInbox, userInbox, pausable); } });
/// <summary> /// TODO: This is a combination of code in ActorCommon.GetNextMessage and /// CheckRemoteInbox. Some factoring is needed. /// </summary> void SysInbox(RemoteMessageDTO dto) { try { if (dto == null) { // Failed to deserialise properly return; } if (dto.Tag == 0 && dto.Type == 0) { // Message is bad tell(ActorContext.System(actor.Id).DeadLetters, DeadLetter.create(dto.Sender, actor.Id, null, "Failed to deserialise message: ", dto)); return; } var msg = MessageSerialiser.DeserialiseMsg(dto, actor.Id); try { lock (sync) { ActorInboxCommon.SystemMessageInbox(actor, this, (SystemMessage)msg, parent); } } catch (Exception e) { var session = msg.SessionId == null ? None : Some(new SessionId(msg.SessionId)); ActorContext.System(actor.Id).WithContext(new ActorItem(actor, this, actor.Flags), parent, dto.Sender, msg as ActorRequest, msg, session, () => replyErrorIfAsked(e)); tell(ActorContext.System(actor.Id).DeadLetters, DeadLetter.create(dto.Sender, actor.Id, e, "Remote message inbox.", msg)); logSysErr(e); } } catch (Exception e) { logSysErr(e); } }
public static Option <UserControlMessage> PreProcessMessage <T>(ProcessId sender, ProcessId self, object message) { if (message == null) { var emsg = $"Message is null for tell (expected {typeof(T)})"; tell(ActorContext.DeadLetters, DeadLetter.create(sender, self, emsg, message)); return(None); } if (message is ActorRequest) { var req = (ActorRequest)message; if (!(req.Message is T) && !(req.Message is Message)) { var emsg = $"Invalid message type for ask (expected {typeof(T)})"; tell(ActorContext.DeadLetters, DeadLetter.create(sender, self, emsg, message)); ActorContext.Tell( sender, new ActorResponse(new Exception($"Invalid message type for ask (expected {typeof(T)})"), typeof(Exception).AssemblyQualifiedName, sender, self, req.RequestId, true ), self ); return(None); } return(Optional((UserControlMessage)message)); } return(new UserMessage(message, sender, sender)); }
/// <summary> /// Process an inbox message /// </summary> /// <param name="message"></param> /// <returns></returns> public Unit ProcessMessage(object message) { var savedReq = ActorContext.CurrentRequest; var savedFlags = ActorContext.ProcessFlags; var savedMsg = ActorContext.CurrentMsg; try { ActorContext.CurrentRequest = null; ActorContext.ProcessFlags = flags; ActorContext.CurrentMsg = message; if (typeof(T) != typeof(string) && message is string) { state = PreProcessMessageContent(message).Match( Some: tmsg => { var stateIn = GetState(); var stateOut = actorFn(stateIn, tmsg); try { if (stateOut != null && !stateOut.Equals(stateIn)) { stateSubject.OnNext(state); } } catch (Exception ue) { logErr(ue); } return(stateOut); }, None: () => state ); } else if (message is T) { var s = actorFn(GetState(), (T)message); state = s; try { stateSubject.OnNext(state); } catch (Exception ue) { logErr(ue); } } else if (message is Message) { ProcessSystemMessage((Message)message); } else { logErr("ProcessMessage request.Message is not T " + message); } } catch (SystemKillActorException) { logInfo("Process message - system kill " + Id); kill(Id); } catch (Exception e) { // TODO: Add extra strategy behaviours here Restart(); tell(ActorContext.Errors, e); tell(ActorContext.DeadLetters, DeadLetter.create(Sender, Self, e, "Process error (tell): ", message)); } finally { ActorContext.CurrentRequest = savedReq; ActorContext.ProcessFlags = savedFlags; ActorContext.CurrentMsg = savedMsg; } return(unit); }
public Unit ProcessAsk(ActorRequest request) { var savedMsg = ActorContext.CurrentMsg; var savedFlags = ActorContext.ProcessFlags; var savedReq = ActorContext.CurrentRequest; try { ActorContext.CurrentRequest = request; ActorContext.ProcessFlags = flags; ActorContext.CurrentMsg = request.Message; if (typeof(T) != typeof(string) && request.Message is string) { state = PreProcessMessageContent(request.Message).Match( Some: tmsg => { var stateIn = GetState(); var stateOut = actorFn(stateIn, tmsg); try { if (stateOut != null && !stateOut.Equals(stateIn)) { stateSubject.OnNext(stateOut); } } catch (Exception ue) { logErr(ue); } return(stateOut); }, None: () => state ); } else if (request.Message is T) { T msg = (T)request.Message; var stateIn = GetState(); var stateOut = actorFn(stateIn, msg); try { if (stateOut != null && !stateOut.Equals(stateIn)) { stateSubject.OnNext(stateOut); } } catch (Exception ue) { logErr(ue); } state = stateOut; } else if (request.Message is Message) { ProcessSystemMessage((Message)request.Message); } else { logErr("ProcessAsk request.Message is not T " + request.Message); } } catch (SystemKillActorException) { kill(Id); } catch (Exception e) { // TODO: Add extra strategy behaviours here Restart(); replyError(e); tell(ActorContext.Errors, e); tell(ActorContext.DeadLetters, DeadLetter.create(request.ReplyTo, request.To, e, "Process error (ask): ", request.Message)); } finally { ActorContext.CurrentMsg = savedMsg; ActorContext.ProcessFlags = savedFlags; ActorContext.CurrentRequest = savedReq; } return(unit); }