/// <summary>
 /// Reply to an ask
 /// </summary>
 /// <remarks>
 /// This should be used from within a process' message loop only
 /// </remarks>
 public static Unit reply <T>(T message) =>
 (message is IReturn) && !((IReturn)message).HasValue
         ? unit
         : InMessageLoop
             ? ActorContext.Request.CurrentRequest == null
                 ? failwith <Unit>("You can't reply to this message.  It wasn't an 'ask'.  Use isAsk to confirm whether something is an 'ask' or a 'tell'")
                 : ActorContext.System(default(SystemName)).Tell(
     ActorContext.Request.CurrentRequest.ReplyTo,
     new ActorResponse(
         message,
         ActorContext.Request.CurrentRequest.ReplyTo,
         ActorContext.Request.Self.Actor.Id,
         ActorContext.Request.CurrentRequest.RequestId,
         typeof(T).AssemblyQualifiedName
         ),
     ActorContext.Request.Self.Actor.Id
     )
             : raiseUseInMsgLoopOnlyException <Unit>(nameof(reply));
        public static object Invoke(ProcessId pid, string method, Type rettyp, params object[] args)
        {
            if (args.Filter(a => a == null).Any())
            {
                throw new ArgumentNullException();
            }

            return(ActorContext.System(pid).Ask(pid,
                                                new ProxyMsg
            {
                Method = method,
                Args = args.Map(JsonConvert.SerializeObject).ToArray(),
                ArgTypes = args.Map(a => a.GetType().GetTypeInfo().AssemblyQualifiedName).ToArray(),
                ReturnType = rettyp.GetTypeInfo().AssemblyQualifiedName
            },
                                                rettyp,
                                                Process.Self
                                                ));
        }
        internal static IDisposable safedelay(Action f, TimeSpan delayFor)
        {
            var savedContext = ActorContext.Request;
            var savedSession = ActorContext.SessionId;
            var stackTrace   = new System.Diagnostics.StackTrace(true);

            return(Observable.Timer(delayFor).Do(_ =>
            {
                if (savedContext == null)
                {
                    f();
                }
                else
                {
                    ActorSystem system;
                    try
                    {
                        system = ActorContext.System(savedContext.Self.Actor.Id);
                    }
                    catch (Exception e)
                    {
                        throw new ProcessSystemException(e, stackTrace);
                    }

                    system.WithContext(
                        savedContext.Self,
                        savedContext.Parent,
                        savedContext.Sender,
                        savedContext.CurrentRequest,
                        savedContext.CurrentMsg,
                        savedSession,
                        () =>
                    {
                        f();

                        // Run the operations that affect the settings and sending of tells
                        // in the order which they occured in the actor
                        ActorContext.Request?.Ops?.Run();
                    });
                }
            }).Subscribe(onNext: _ => { }, onCompleted: () => { }, onError: logErr));
        }
Exemple #4
0
        public static State Inbox(State state, Msg msg)
        {
            try
            {
                state = ActorContext.System(Self).Cluster.Fold(state, (s, cluster) =>
                {
                    if (s.Scheduled.IsNone)
                    {
                        // Lazily load any items in the persistent store, once
                        s = new State(GetScheduled(cluster));
                    }

                    switch (msg)
                    {
                    case Msg.CheckMsg m: return(Check(s, cluster));

                    case Msg.AddToScheduleMsg m: return(AddToSchedule(s, m, cluster));

                    case Msg.RescheduleMsg m: return(Reschedule(s, m, cluster));

                    case Msg.RemoveFromScheduleMsg m: return(RemoveFromSchedule(s, m, cluster));

                    default: return(s);
                    }
                });
            }
            catch (Exception e)
            {
                try
                {
                    logErr(e);
                }
                catch { }
            }

            if (msg is Msg.CheckMsg)
            {
                tellSelf(Msg.Check, schedule);
            }
            return(state);
        }
Exemple #5
0
        /// <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);
            }
        }
Exemple #6
0
        /// <summary>
        /// Spawn a router using the settings in the config
        /// </summary>
        /// <example>
        /// <para>
        ///     router broadcast1:
        ///         pid:			/root/user/broadcast1
        ///         route:	        broadcast
        ///         worker-count:	10
        /// </para>
        /// <para>
        ///     router broadcast2:
        ///         pid:			/root/user/broadcast2
        ///         route:	        broadcast
        ///         workers:		[hello, world]
        /// </para>
        /// <para>
        ///     router least:
        ///         pid:			/role/user/least
        ///         route:	        least-busy
        ///         workers:		[one, two, three]
        /// </para>
        /// </example>
        /// <param name="name">Name of the child process that will be the router</param>
        /// <returns>ProcessId of the router</returns>
        public static ProcessId fromConfig <T>(ProcessName name)
        {
            var id      = Self[name];
            var type    = ActorContext.System(id).Settings.GetRouterDispatch(id);
            var workers = ActorContext.System(id).Settings.GetRouterWorkers(id)
                          .Map(p => p.ProcessId.IfNone(ProcessId.None))
                          .Filter(pid => pid != ProcessId.None);

            var flags = ActorContext.System(id).Settings.GetProcessFlags(id);
            var mbs   = ActorContext.System(id).Settings.GetProcessMailboxSize(id);

            return(type.Map(t =>
            {
                // TODO: Consider the best approach to generalise this, so that bespoke router
                //       types can make use of the config system too.
                switch (t)
                {
                case "broadcast":
                    return broadcast <T>(name, workers, RouterOption.Default, flags, mbs);

                case "hash":
                    return hash <T>(name, workers, null, RouterOption.Default, flags, mbs);

                case "least-busy":
                    return leastBusy <T>(name, workers, RouterOption.Default, flags, mbs);

                case "random":
                    return random <T>(name, workers, RouterOption.Default, flags, mbs);

                case "round-robin":
                    return roundRobin <T>(name, workers, RouterOption.Default, flags, mbs);

                default:
                    throw new Exception($"Unsupported router type (for config system setup): {t} ");
                }
            })
                   .IfNone(() => failwith <ProcessId>($"'route' not specified for {id}")));
        }
Exemple #7
0
        internal static IDisposable safedelay(Action f, TimeSpan delayFor)
        {
            var savedContext = ActorContext.Request;
            var savedSession = ActorContext.SessionId;

            return((IDisposable)Task.Delay(delayFor).ContinueWith(_ =>
            {
                try
                {
                    if (savedContext == null)
                    {
                        f();
                    }
                    else
                    {
                        ActorContext.System(savedContext.Self.Actor.Id).WithContext(
                            savedContext.Self,
                            savedContext.Parent,
                            savedContext.Sender,
                            savedContext.CurrentRequest,
                            savedContext.CurrentMsg,
                            savedSession,
                            () =>
                        {
                            f();

                            // Run the operations that affect the settings and sending of tells
                            // in the order which they occured in the actor
                            ActorContext.Request?.Ops?.Run();
                        });
                    }
                }
                catch (Exception e)
                {
                    logErr(e);
                }
            }));
        }
Exemple #8
0
        /// <summary>
        /// Spawn a router using the settings in the config
        /// </summary>
        /// <example>
        /// <para>
        ///     router broadcast1:
        ///         pid:			/root/user/broadcast1
        ///         route:	        broadcast
        ///         worker-count:	10
        /// </para>
        /// <para>
        ///     router broadcast2:
        ///         pid:			/root/user/broadcast2
        ///         route:	        broadcast
        ///         workers:		[hello, world]
        /// </para>
        /// <para>
        ///     router least:
        ///         pid:			/role/user/least
        ///         route:	        least-busy
        ///         workers:		[one, two, three]
        /// </para>
        /// </example>
        /// <typeparam name="T"></typeparam>
        /// <param name="name">Name of the child process that will be the router</param>
        /// <returns>ProcessId of the router</returns>
        public static ProcessId fromConfig <S, T>(ProcessName name, Func <S> Setup, Func <S, T, S> Inbox)
        {
            var id       = Self[name];
            var type     = ActorContext.System(id).Settings.GetRouterDispatch(id);
            var workers  = ActorContext.System(id).Settings.GetRouterWorkerCount(id);
            var flags    = ActorContext.System(id).Settings.GetProcessFlags(id);
            var mbs      = ActorContext.System(id).Settings.GetProcessMailboxSize(id);
            var strategy = ActorContext.System(id).Settings.GetProcessStrategy(id);
            var wrkrName = ActorContext.System(id).Settings.GetRouterWorkerName(id);

            return(type.Map(t =>
            {
                // TODO: Consider the best approach to generalise this, so that bespoke router
                //       types can make use of the config system too.
                switch (t)
                {
                case "broadcast":
                    return broadcast(name, workers.IfNone(2), Setup, Inbox, flags, strategy, mbs, wrkrName);

                case "hash":
                    return hash(name, workers.IfNone(2), Setup, Inbox, null, flags, strategy, mbs, wrkrName);

                case "least-busy":
                    return leastBusy(name, workers.IfNone(2), Setup, Inbox, flags, strategy, mbs, wrkrName);

                case "random":
                    return random(name, workers.IfNone(2), Setup, Inbox, flags, strategy, mbs, wrkrName);

                case "round-robin":
                    return roundRobin(name, workers.IfNone(2), Setup, Inbox, flags, strategy, mbs, wrkrName);

                default:
                    throw new Exception($"Unsupported router type (for config system setup): {t} ");
                }
            })
                   .IfNone(() => failwith <ProcessId>($"'route' not specified for {id}")));
        }
Exemple #9
0
        public Unit TellUserControl(UserControlMessage msg, Option <SessionId> sessionId)
        {
            if (msg == null)
            {
                throw new ArgumentNullException(nameof(msg));
            }

            if (userInbox != null)
            {
                msg.SessionId = msg.SessionId ?? sessionId.Map(s => s.Value).IfNoneUnsafe(msg.SessionId);
                if (IsPaused)
                {
                    // We're paused, so send to our persistent queue
                    new ActorDispatchRemote(
                        ActorContext.System(actor.Id).Ping,
                        actor.Id,
                        cluster,
                        msg.SessionId == null
                            ? None
                            : Some(new SessionId(msg.SessionId)),
                        false)
                    .TellUserControl(msg, ProcessId.None);
                }
                else
                {
                    try
                    {
                        userInbox?.Post(msg);
                    }
                    catch (QueueFullException)
                    {
                        throw new ProcessInboxFullException(actor.Id, MailboxSize, "user");
                    }
                }
            }
            return(unit);
        }
Exemple #10
0
        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.System(self).DeadLetters, DeadLetter.create(sender, self, emsg, message));
                return(None);
            }

            if (message is ActorRequest req)
            {
                if (!(req.Message is T) && !(req.Message is Message))
                {
                    var emsg = $"Invalid message type for ask (expected {typeof(T)})";
                    tell(ActorContext.System(self).DeadLetters, DeadLetter.create(sender, self, emsg, message));

                    ActorContext.System(self).Tell(
                        sender,
                        new ActorResponse(new Exception($"Invalid message type for ask (expected {typeof(T)})"),
                                          sender,
                                          self,
                                          req.RequestId,
                                          typeof(Exception).AssemblyQualifiedName,
                                          true
                                          ),
                        Schedule.Immediate,
                        self
                        );

                    return(None);
                }
                return(Optional((UserControlMessage)message));
            }

            return(new UserMessage(message, sender, sender));
        }
Exemple #11
0
 /// <summary>
 /// Find out if a process exists and is alive
 ///
 ///     Rules:
 ///         * Local processes   - the process must actually be running
 ///         * Remote processes  - the process must actually be running
 ///         * Dispatchers/roles - at least one process in the collection must be running
 ///         * JS processes      - not current supported
 /// </summary>
 /// <param name="pid">Process ID to check</param>
 /// <returns>True if exists</returns>
 public static bool ping(ProcessId pid) =>
 ActorContext.System(pid).GetDispatcher(pid).Ping();
Exemple #12
0
 /// <summary>
 /// Find out if a process exists
 ///
 ///     Rules:
 ///         * Local processes   - the process must actually be alive and in-memory
 ///         * Remote processes  - the process must have an inbox to receive messages
 ///                               and may be active, but it's not required.
 ///         * Dispatchers/roles - at least one process in the collection must exist(pid)
 ///         * JS processes      - not current supported
 /// </summary>
 /// <param name="pid">Process ID to check</param>
 /// <returns>True if exists</returns>
 public static bool exists(ProcessId pid) =>
 ActorContext.System(pid).GetDispatcher(pid).Exists;
Exemple #13
0
 /// <summary>
 /// Un-pauses a paused process.  Messages that have built-up in the inbox whilst
 /// the Process was paused will be Processed immediately.
 /// </summary>
 /// <param name="pid">Process to un-pause</param>
 public static Unit unpause(ProcessId pid) =>
 ActorContext.System(pid).TellSystem(pid, SystemMessage.Unpause);
Exemple #14
0
 /// <summary>
 /// Forces a running process to restart.  This will reset its state and drop
 /// any subscribers, or any of its subscriptions.
 /// </summary>
 public static Unit restart(ProcessId pid) =>
 ActorContext.System(pid).TellSystem(pid, SystemMessage.Restart);
Exemple #15
0
 /// <summary>
 /// Shutdown all processes on all process-systems
 /// </summary>
 public static Unit shutdownAll() =>
 ActorContext.StopAllSystems();
Exemple #16
0
 /// <summary>
 /// Shutdown all processes on the specified process-system
 /// </summary>
 public static Unit shutdownSystem(SystemName system) =>
 ActorContext.StopSystem(system);
 private IEnumerable <IActorDispatch> GetWorkers() =>
 group.Map(pid => ActorContext.System(pid).GetDispatcher(pid));
Exemple #18
0
 /// <summary>
 /// Get the types of messages that the provided ProcessId accepts.  Returns
 /// an empty list if it can't be resolved for whatever reason (process doesn't
 /// exist/JS process/etc.).
 /// </summary>
 /// <param name="pid">Process ID to query</param>
 /// <returns>List of types</returns>
 public static IEnumerable <Type> validMessageTypes(ProcessId pid) =>
 ActorContext.System(pid).GetDispatcher(pid).GetValidMessageTypes();
Exemple #19
0
 /// <summary>
 /// Get a list of cluster nodes that are online
 /// </summary>
 public static Map <ProcessName, ClusterNode> ClusterNodes(SystemName system = default(SystemName)) =>
 ActorContext.System(system).ClusterState?.Members ?? Map <ProcessName, ClusterNode>();
Exemple #20
0
 /// <summary>
 /// Errors process
 /// Subscribe to it to monitor the errors thrown
 /// </summary>
 public static ProcessId Errors(SystemName system = default(SystemName)) =>
 ActorContext.System(system).Errors;
Exemple #21
0
 /// <summary>
 /// Dead letters process
 /// Subscribe to it to monitor the failed messages (<see cref="subscribe(ProcessId)"/>)
 /// </summary>
 public static ProcessId DeadLetters(SystemName system = default(SystemName)) =>
 ActorContext.System(system).DeadLetters;
Exemple #22
0
 /// <summary>
 /// User process ID
 /// The User process is the default entry process, your first process spawned
 /// will be a child of this process.
 /// </summary>
 public static ProcessId User(SystemName system = default(SystemName)) =>
 ActorContext.System(system).User;
Exemple #23
0
 /// <summary>
 /// Root process ID
 /// The Root process is the parent of all processes
 /// </summary>
 public static ProcessId Root(SystemName system = default(SystemName)) =>
 ActorContext.System(system).Root;
Exemple #24
0
 /// <summary>
 /// Stop watching for the death of the watching process
 /// </summary>
 /// <param name="watcher">Watcher</param>
 /// <param name="watching">Watched</param>
 public static Unit unwatch(ProcessId watcher, ProcessId watching) =>
 ActorContext.System(watcher).GetDispatcher(watcher).DispatchUnWatch(watching);
Exemple #25
0
 /// <summary>
 /// Find the number of items in the Process inbox
 /// </summary>
 /// <param name="pid">Process</param>
 /// <returns>Number of items in the Process inbox</returns>
 public static int inboxCount(ProcessId pid) =>
 ActorContext.System(pid).GetDispatcher(pid).GetInboxCount();
 /// <summary>
 /// Forward a message
 /// </summary>
 /// <param name="pid">Process ID to send to</param>
 public static Unit fwd(ProcessId pid) =>
 ActorContext.Request.CurrentRequest == null
         ? tell(pid, ActorContext.Request.CurrentMsg, Sender)
         : tell(pid, ActorContext.Request.CurrentRequest, ActorContext.System(pid).AskId);
Exemple #27
0
 /// <summary>
 /// Resolves a ProcessId into the absolute ProcessIds that it represents
 /// This allows live resolution of role-based ProcessIds to their real node
 /// ProcessIds.
 /// </summary>
 /// <remarks>Mostly useful for debugging, but could be useful for layering
 /// additional logic to any message dispatch.
 /// </remarks>
 /// <param name="pid"></param>
 /// <returns>Enumerable of resolved ProcessIds - could be zero length</returns>
 public static IEnumerable <ProcessId> resolve(ProcessId pid) =>
 ActorContext.System(pid).ResolveProcessIdSelection(pid);
Exemple #28
0
 /// <summary>
 /// Kill a specified running process.
 /// Forces the specified Process to shutdown.  The kill message jumps
 /// ahead of any messages already in the process's queue.  Any Process
 /// that has a persistent inbox or state will also have its persistent
 /// data wiped.  If the Process is registered it will also its
 /// registration revoked.
 /// If you wish for the data to be maintained for future
 /// spawns then call Process.shutdown(pid);
 /// </summary>
 public static Unit kill(ProcessId pid) =>
 ActorContext.System(pid).Kill(pid, false);
Exemple #29
0
 /// <summary>
 /// Get the child processes of the process ID provided
 /// </summary>
 public static Map <string, ProcessId> children(ProcessId pid) =>
 ActorContext.System(pid).GetChildren(pid);
Exemple #30
0
 /// <summary>
 /// Shutdown a specified running process.
 /// Forces the specified Process to shutdown.  The shutdown message jumps
 /// ahead of any messages already in the process's queue.  Any Process
 /// that has a persistent inbox or state will have its state maintained
 /// for future spawns.  If you wish for the data to be dropped then call
 /// Process.kill(pid)
 /// </summary>
 public static Unit shutdown(ProcessId pid) =>
 ActorContext.System(pid).Kill(pid, true);