예제 #1
0
        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);
        }
예제 #2
0
 /// <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);
예제 #3
0
        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;
            }));
        }
예제 #4
0
 public Unit DispatchUnWatch(ProcessId pid)
 {
     sys.GetDispatcher(pid).UnWatch(Id);
     return(ActorContext.System(Id).RemoveWatcher(pid, Id));
 }
예제 #5
0
 /// <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);
예제 #6
0
 public Unit DispatchWatch(ProcessId pid)
 {
     sys.GetDispatcher(pid).Watch(Id);
     return(ActorContext.System(Id).AddWatcher(pid, Id));
 }
예제 #7
0
 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);
                });
            }
        }
예제 #10
0
        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));
        }