public ProcessId ActorCreate <S, T>( ActorItem parent, ProcessName name, Func <S, T, S> actorFn, Func <IActor, S> setupFn, Func <S, ProcessId, S> termFn, State <StrategyContext, Unit> strategy, ProcessFlags flags, int maxMailboxSize, bool lazy) { var actor = new Actor <S, T>(cluster, parent, name, actorFn, setupFn, termFn, strategy, flags, ActorContext.System(parent.Actor.Id).Settings, this); IActorInbox inbox = null; if ((actor.Flags & ProcessFlags.ListenRemoteAndLocal) == ProcessFlags.ListenRemoteAndLocal && cluster.IsSome) { inbox = new ActorInboxDual <S, T>(); } else if ((actor.Flags & ProcessFlags.PersistInbox) == ProcessFlags.PersistInbox && cluster.IsSome) { inbox = new ActorInboxRemote <S, T>(); } else { inbox = new ActorInboxLocal <S, T>(); } var item = new ActorItem(actor, inbox, actor.Flags); parent.Actor.LinkChild(item); // Auto register if there are config settings and we // have the variable name it was assigned to. ActorContext.System(actor.Id).Settings.GetProcessRegisteredName(actor.Id).Iter(regName => { // Also check if 'dispatch' is specified in the config, if so we will // register the Process as a role dispatcher PID instead of just its // PID. ActorContext.System(actor.Id).Settings.GetProcessDispatch(actor.Id) .Match( Some: disp => Process.register(regName, Disp[$"role-{disp}"][Role.Current].Append(actor.Id.Skip(1))), None: () => Process.register(regName, actor.Id) ); }); try { inbox.Startup(actor, parent, cluster, maxMailboxSize); if (!lazy) { TellSystem(actor.Id, SystemMessage.StartupProcess); } } catch { item?.Actor?.ShutdownProcess(false); throw; } return(item.Actor.Id); }
/// <summary> /// Send a message to a process /// </summary> /// <param name="pid">Process ID to send to</param> /// <param name="message">Message to send</param> /// <param name="sender">Optional sender override. The sender is handled automatically if you do not provide one.</param> internal static Unit tellSystem <T>(ProcessId pid, T message, ProcessId sender = default(ProcessId)) => ActorContext.System(pid).TellSystem(pid, message as SystemMessage);
public static InboxDirective SystemMessageInbox <S, T>(Actor <S, T> actor, IActorInbox inbox, SystemMessage msg, ActorItem parent) { var session = msg.SessionId == null ? None : Some(new SessionId(msg.SessionId)); return(ActorContext.System(actor.Id).WithContext(new ActorItem(actor, inbox, actor.Flags), parent, ProcessId.NoSender, null, msg, session, () => { switch (msg.Tag) { case Message.TagSpec.Restart: if (inbox.IsPaused) { inbox.Unpause(); } actor.Restart(); break; case Message.TagSpec.LinkChild: var lc = msg as SystemLinkChildMessage; actor.LinkChild(lc.Child); break; case Message.TagSpec.UnlinkChild: var ulc = (msg as SystemUnLinkChildMessage).SetSystem(actor.Id.System); actor.UnlinkChild(ulc.Child); break; case Message.TagSpec.ChildFaulted: var cf = (msg as SystemChildFaultedMessage).SetSystem(actor.Id.System); return actor.ChildFaulted(cf.Child, cf.Sender, cf.Exception, cf.Message); case Message.TagSpec.StartupProcess: actor.Startup(); break; case Message.TagSpec.ShutdownProcess: var sp = msg as ShutdownProcessMessage; actor.ShutdownProcess(sp.MaintainState); break; case Message.TagSpec.Unpause: inbox.Unpause(); break; case Message.TagSpec.Pause: inbox.Pause(); return InboxDirective.Pause; case Message.TagSpec.Watch: var awm = msg as SystemAddWatcherMessage; actor.AddWatcher(awm.Id); break; case Message.TagSpec.UnWatch: var rwm = msg as SystemRemoveWatcherMessage; actor.RemoveWatcher(rwm.Id); break; case Message.TagSpec.DispatchWatch: var dwm = msg as SystemDispatchWatchMessage; actor.DispatchWatch(dwm.Id); break; case Message.TagSpec.DispatchUnWatch: var duwm = msg as SystemDispatchUnWatchMessage; actor.DispatchUnWatch(duwm.Id); break; } return InboxDirective.Default; })); }
public Unit DispatchUnWatch(ProcessId pid) { sys.GetDispatcher(pid).UnWatch(Id); return(ActorContext.System(Id).RemoveWatcher(pid, Id)); }
/// <summary> /// Send a message to a process /// </summary> /// <param name="pid">Process ID to send to</param> /// <param name="message">Message to send</param> /// <param name="sender">Optional sender override. The sender is handled automatically if you do not provide one.</param> public static Unit tell <T>(ProcessId pid, T message, ProcessId sender = default(ProcessId)) => message is UserControlMessage ? ActorContext.System(pid).TellUserControl(pid, message as UserControlMessage) : ActorContext.System(pid).Tell(pid, message, sender);
public Unit DispatchWatch(ProcessId pid) { sys.GetDispatcher(pid).Watch(Id); return(ActorContext.System(Id).AddWatcher(pid, Id)); }
private IEnumerable <IActorDispatch> GetWorkers() => group.Map(pid => ActorContext.System(pid).GetDispatcher(pid));
/// <summary> /// Access a setting /// If in a Process message loop, then this accesses the configuration settings /// for the Process from the the configuration file, or stored in the cluster. /// If not in a Process message loop, then this accesses 'global' configuration /// settings. /// </summary> /// <param name="name">Name of the setting</param> /// <param name="prop">If the setting is a complex value (like a map or record), then /// this selects the property of the setting to access</param> /// <returns>Optional configuration setting value</returns> public static T read <T>(string name, string prop, T defaultValue, SystemName system = default(SystemName)) => InMessageLoop ? ActorContext.Request.Ops.Read(name, prop, ActorContext.Request.ProcessFlags, defaultValue) : ActorContext.System(system).Settings.GetRoleSetting(name, prop, defaultValue);
private static void StartFromConfig(ProcessSystemConfig config) { lock (sync) { config.Cluster.Match( Some: _ => { // Extract cluster settings var provider = config.GetClusterSetting("provider", "value", "redis"); var role = config.GetClusterSetting("role", "value", name => clusterSettingMissing <string>(name)); var clusterConn = config.GetClusterSetting("connection", "value", "localhost"); var clusterDb = config.GetClusterSetting("database", "value", "0"); var env = config.SystemName; var userEnv = config.GetClusterSetting <string>("user-env", "value"); var appProfile = new AppProfile( config.NodeName, role, clusterConn, clusterDb, env, userEnv ); // Look for an existing actor-system with the same system name var current = ActorContext.Systems.Filter(c => c.Value == env).HeadOrNone(); // Work out if something significant has changed that will cause us to restart var restart = current.Map(ActorContext.System) .Map(c => c.AppProfile.NodeName != appProfile.NodeName || c.AppProfile.Role != appProfile.Role || c.AppProfile.ClusterConn != appProfile.ClusterConn || c.AppProfile.ClusterDb != appProfile.ClusterDb); // Either restart / update settings / or start new restart.Match( Some: r => { if (r) { // Restart try { ActorContext.StopSystem(env); } catch (Exception e) { logErr(e); } StartFromConfig(config); } else { // Update settings ActorContext.System(env).UpdateSettings(config, appProfile); var cluster = from systm in current.Map(ActorContext.System) from clstr in systm.Cluster select clstr; } }, None: () => { // Start new ICluster cluster = Cluster.connect( provider, config.NodeName, clusterConn, clusterDb, role ); ActorContext.StartSystem(env, Optional(cluster), appProfile, config); config.PostConnect(); }); }, None: () => { ActorContext.StartSystem(new SystemName(""), None, AppProfile.NonClustered, config); }); } }
public static Tuple <long, Dictionary <long, AskActorReq> > Inbox(Tuple <long, Dictionary <long, AskActorReq> > state, object msg) { var reqId = state.Item1; var dict = state.Item2; if (msg is AskActorReq) { reqId++; var req = (AskActorReq)msg; ActorContext.System(req.To).Ask(req.To, new ActorRequest(req.Message, req.To, Self, reqId), Self); dict.Add(reqId, req); } else { var res = (ActorResponse)msg; if (dict.ContainsKey(res.RequestId)) { var req = dict[res.RequestId]; try { if (res.IsFaulted) { Exception ex = null; // Let's be reeeally safe here and do everything we can to get some valid information out of // the response to report to the process doing the 'ask'. try { var msgtype = Type.GetType(res.ResponseMessageType); if (msgtype == res.Message.GetType() && typeof(Exception).GetTypeInfo().IsAssignableFrom(msgtype.GetTypeInfo())) { // Type is fine, just return it as an error ex = (Exception)res.Message; } else { if (res.Message is string) { ex = (Exception)Deserialise.Object(res.Message.ToString(), msgtype); } else { ex = (Exception)Deserialise.Object(JsonConvert.SerializeObject(res.Message), msgtype); } } } catch { ex = new Exception(res.Message == null ? $"An unknown error was thrown by {req.To}" : res.Message.ToString()); } req.Complete(new AskActorRes(new ProcessException($"Process issue: {ex.Message}", req.To.Path, req.ReplyTo.Path, ex))); } else { req.Complete(new AskActorRes(res.Message)); } } catch (Exception e) { req.Complete(new AskActorRes(new ProcessException($"Process issue: {e.Message}", req.To.Path, req.ReplyTo.Path, e))); logSysErr(e); } finally { dict.Remove(res.RequestId); } } else { logWarn($"Request ID doesn't exist: {res.RequestId}"); } } return(new Tuple <long, Dictionary <long, AskActorReq> >(reqId, dict)); }