Exemple #1
0
        private GrainTimer(Func<object, Task> asyncCallback, object state, TimeSpan dueTime, TimeSpan period, string name)
        {
            var ctxt = RuntimeContext.Current.ActivationContext;
            activationData = (ActivationData) RuntimeClient.Current.CurrentActivationData;

            this.Name = name;
            this.asyncCallback = asyncCallback;
            timer = new AsyncTaskSafeTimer( 
                stateObj => TimerTick(stateObj, ctxt),
                state);
            this.dueTime = dueTime;
            timerFrequency = period;
            previousTickTime = DateTime.UtcNow;
            totalNumTicks = 0;
        }
Exemple #2
0
        /// <summary>
        /// Handle an incoming message and queue/invoke appropriate handler
        /// </summary>
        /// <param name="message"></param>
        /// <param name="targetActivation"></param>
        public void HandleIncomingRequest(Message message, ActivationData targetActivation)
        {
            lock (targetActivation)
            {
                if (targetActivation.State == ActivationState.Invalid)
                {
                    ProcessRequestToInvalidActivation(message, targetActivation.Address, targetActivation.ForwardingAddress, "HandleIncomingRequest");
                    return;
                }

                // Now we can actually scheduler processing of this request
                targetActivation.RecordRunning(message);
                var context = new SchedulingContext(targetActivation);

                MessagingProcessingStatisticsGroup.OnDispatcherMessageProcessedOk(message);
                Scheduler.QueueWorkItem(new InvokeWorkItem(targetActivation, message, context), context);
            }
        }
Exemple #3
0
 /// <summary>
 /// Determine if the activation is able to currently accept the given message
 /// - always accept responses
 /// For other messages, require that:
 /// - activation is properly initialized
 /// - the message would not cause a reentrancy conflict
 /// </summary>
 /// <param name="targetActivation"></param>
 /// <param name="incoming"></param>
 /// <returns></returns>
 private bool ActivationMayAcceptRequest(ActivationData targetActivation, Message incoming)
 {
     if (!targetActivation.State.Equals(ActivationState.Valid)) return false;
     if (!targetActivation.IsCurrentlyExecuting) return true;
     return CanInterleave(targetActivation, incoming);
 }
Exemple #4
0
        private void ReceiveResponse(Message message, ActivationData targetActivation)
        {
            lock (targetActivation)
            {
                if (targetActivation.State == ActivationState.Invalid)
                {
                    logger.Warn(ErrorCode.Dispatcher_Receive_InvalidActivation,
                        "Response received for invalid activation {0}", message);
                    MessagingProcessingStatisticsGroup.OnDispatcherMessageProcessedError(message, "Ivalid");
                    return;
                }
                MessagingProcessingStatisticsGroup.OnDispatcherMessageProcessedOk(message);
                if (Transport.TryDeliverToProxy(message)) return;

                RuntimeClient.Current.ReceiveResponse(message);
            }
        }
Exemple #5
0
        private async Task<ActivationData> CallGrainDeactivateAndCleanupStreams(ActivationData activation)
        {
            try
            {
                var grainTypeName = activation.GrainInstanceType.FullName;

                // Note: This call is being made from within Scheduler.Queue wrapper, so we are already executing on worker thread
                if (logger.IsVerbose) logger.Verbose(ErrorCode.Catalog_BeforeCallingDeactivate, "About to call {1} grain's OnDeactivateAsync() method {0}", activation, grainTypeName);

                // Call OnDeactivateAsync inline, but within try-catch wrapper to safely capture any exceptions thrown from called function
                try
                {
                    // just check in case this activation data is already Invalid or not here at all.
                    ActivationData ignore;
                    if (TryGetActivationData(activation.ActivationId, out ignore) &&
                        activation.State == ActivationState.Deactivating)
                    {
                        await activation.GrainInstance.OnDeactivateAsync();
                    }
                    if (logger.IsVerbose) logger.Verbose(ErrorCode.Catalog_AfterCallingDeactivate, "Returned from calling {1} grain's OnDeactivateAsync() method {0}", activation, grainTypeName);
                }
                catch (Exception exc)
                {
                    logger.Error(ErrorCode.Catalog_ErrorCallingDeactivate,
                        string.Format("Error calling grain's OnDeactivateAsync() method - Grain type = {1} Activation = {0}", activation, grainTypeName), exc);
                }

                if (activation.IsUsingStreams)
                {
                    try
                    {
                        await activation.DeactivateStreamResources();
                    }
                    catch (Exception exc)
                    {
                        logger.Warn(ErrorCode.Catalog_DeactivateStreamResources_Exception, String.Format("DeactivateStreamResources Grain type = {0} Activation = {1} failed.", grainTypeName, activation), exc);
                    }
                }
            }
            catch(Exception exc)
            {
                logger.Error(ErrorCode.Catalog_FinishGrainDeactivateAndCleanupStreams_Exception, String.Format("CallGrainDeactivateAndCleanupStreams Activation = {0} failed.", activation), exc);
            }
            return activation;
        }
Exemple #6
0
        private void RerouteAllQueuedMessages(ActivationData activation, ActivationAddress forwardingAddress, string failedOperation, Exception exc = null)
        {
            lock (activation)
            {
                List<Message> msgs = activation.DequeueAllWaitingMessages();
                if (msgs == null || msgs.Count <= 0) return;

                if (logger.IsVerbose) logger.Verbose(ErrorCode.Catalog_RerouteAllQueuedMessages, String.Format("RerouteAllQueuedMessages: {0} msgs from Invalid activation {1}.", msgs.Count(), activation));
                dispatcher.ProcessRequestsToInvalidActivation(msgs, activation.Address, forwardingAddress, failedOperation, exc);
            }
        }
Exemple #7
0
 /// <summary>
 /// Deletes activation immediately regardless of active transactions etc.
 /// For use by grain delete, transaction abort, etc.
 /// </summary>
 /// <param name="activation"></param>
 private void DestroyActivationVoid(ActivationData activation)
 {
     StartDestroyActivations(new List<ActivationData> { activation });
 }
Exemple #8
0
        internal Silo(string name, SiloType siloType, ClusterConfiguration config, ILocalDataStore keyStore)
        {
            SystemStatus.Current = SystemStatus.Creating;

            CurrentSilo = this;

            var startTime = DateTime.UtcNow;

            this.siloType = siloType;
            Name          = name;

            siloTerminatedEvent = new ManualResetEvent(false);

            OrleansConfig = config;
            globalConfig  = config.Globals;
            config.OnConfigChange("Defaults", () => nodeConfig = config.GetConfigurationForNode(name));

            if (!TraceLogger.IsInitialized)
            {
                TraceLogger.Initialize(nodeConfig);
            }

            config.OnConfigChange("Defaults/Tracing", () => TraceLogger.Initialize(nodeConfig, true), false);
            LimitManager.Initialize(nodeConfig);
            ActivationData.Init(config);
            StatisticsCollector.Initialize(nodeConfig);
            SerializationManager.Initialize(globalConfig.UseStandardSerializer);
            initTimeout = globalConfig.MaxJoinAttemptTime;
            if (Debugger.IsAttached)
            {
                initTimeout = StandardExtensions.Max(TimeSpan.FromMinutes(10), globalConfig.MaxJoinAttemptTime);
                stopTimeout = initTimeout;
            }

            IPEndPoint here       = nodeConfig.Endpoint;
            int        generation = nodeConfig.Generation;

            if (generation == 0)
            {
                generation            = SiloAddress.AllocateNewGeneration();
                nodeConfig.Generation = generation;
            }
            TraceLogger.MyIPEndPoint = here;
            logger = TraceLogger.GetLogger("Silo", TraceLogger.LoggerType.Runtime);
            logger.Info(ErrorCode.SiloInitializing, "-------------- Initializing {0} silo on {1} at {2}, gen {3} --------------", siloType, nodeConfig.DNSHostName, here, generation);
            logger.Info(ErrorCode.SiloInitConfig, "Starting silo {0} with runtime Version='{1}' Config= " + Environment.NewLine + "{2}", name, RuntimeVersion.Current, config.ToString(name));

            if (keyStore != null)
            {
                // Re-establish reference to shared local key store in this app domain
                LocalDataStoreInstance.LocalDataStore = keyStore;
            }
            healthCheckParticipants = new List <IHealthCheckParticipant>();

            BufferPool.InitGlobalBufferPool(globalConfig);
            PlacementStrategy.Initialize(globalConfig);

            UnobservedExceptionsHandlerClass.SetUnobservedExceptionHandler(UnobservedExceptionHandler);
            AppDomain.CurrentDomain.UnhandledException +=
                (obj, ev) => DomainUnobservedExceptionHandler(obj, (Exception)ev.ExceptionObject);

            typeManager = new GrainTypeManager(here.Address.Equals(IPAddress.Loopback));

            // Performance metrics
            siloStatistics = new SiloStatisticsManager(globalConfig, nodeConfig);
            config.OnConfigChange("Defaults/LoadShedding", () => siloStatistics.MetricsTable.NodeConfig = nodeConfig, false);

            // The scheduler
            scheduler = new OrleansTaskScheduler(globalConfig, nodeConfig);
            healthCheckParticipants.Add(scheduler);

            // Initialize the message center
            var mc = new MessageCenter(here, generation, globalConfig, siloStatistics.MetricsTable);

            if (nodeConfig.IsGatewayNode)
            {
                mc.InstallGateway(nodeConfig.ProxyGatewayEndpoint);
            }

            messageCenter = mc;

            // Now the router/directory service
            // This has to come after the message center //; note that it then gets injected back into the message center.;
            localGrainDirectory = new LocalGrainDirectory(this);

            // Now the activation directory.
            // This needs to know which router to use so that it can keep the global directory in synch with the local one.
            activationDirectory = new ActivationDirectory();

            // Now the consistent ring provider
            RingProvider = GlobalConfig.UseVirtualBucketsConsistentRing ?
                           (IConsistentRingProvider) new VirtualBucketsRingProvider(SiloAddress, GlobalConfig.NumVirtualBucketsConsistentRing)
                : new ConsistentRingProvider(SiloAddress);

            Action <Dispatcher> setDispatcher;

            catalog = new Catalog(Constants.CatalogId, SiloAddress, Name, LocalGrainDirectory, typeManager, scheduler, activationDirectory, config, out setDispatcher);
            var dispatcher = new Dispatcher(scheduler, messageCenter, catalog, config);

            setDispatcher(dispatcher);

            RuntimeClient.Current = new InsideRuntimeClient(
                dispatcher,
                catalog,
                LocalGrainDirectory,
                SiloAddress,
                config,
                RingProvider,
                typeManager);
            messageCenter.RerouteHandler       = InsideRuntimeClient.Current.RerouteMessage;
            messageCenter.SniffIncomingMessage = InsideRuntimeClient.Current.SniffIncomingMessage;

            siloStatistics.MetricsTable.Scheduler           = scheduler;
            siloStatistics.MetricsTable.ActivationDirectory = activationDirectory;
            siloStatistics.MetricsTable.ActivationCollector = catalog.ActivationCollector;
            siloStatistics.MetricsTable.MessageCenter       = messageCenter;

            DeploymentLoadPublisher.CreateDeploymentLoadPublisher(this, globalConfig);
            PlacementDirectorsManager.CreatePlacementDirectorsManager(globalConfig);

            // Now the incoming message agents
            incomingSystemAgent = new IncomingMessageAgent(Message.Categories.System, messageCenter, activationDirectory, scheduler, dispatcher);
            incomingPingAgent   = new IncomingMessageAgent(Message.Categories.Ping, messageCenter, activationDirectory, scheduler, dispatcher);
            incomingAgent       = new IncomingMessageAgent(Message.Categories.Application, messageCenter, activationDirectory, scheduler, dispatcher);

            membershipFactory = new MembershipFactory();
            reminderFactory   = new LocalReminderServiceFactory();

            SystemStatus.Current = SystemStatus.Created;

            StringValueStatistic.FindOrCreate(StatisticNames.SILO_START_TIME,
                                              () => TraceLogger.PrintDate(startTime)); // this will help troubleshoot production deployment when looking at MDS logs.

            TestHookup = new TestHookups(this);

            logger.Info(ErrorCode.SiloInitializingFinished, "-------------- Started silo {0}, ConsistentHashCode {1:X} --------------", SiloAddress.ToLongString(), SiloAddress.GetConsistentHashCode());
        }
Exemple #9
0
        private async Task RegisterActivationInGrainDirectoryAndValidate(ActivationData activation)
        {
            ActivationAddress address = activation.Address;
            bool singleActivationMode = !activation.IsStatelessWorker;

            if (singleActivationMode)
            {
                ActivationAddress returnedAddress = await scheduler.RunOrQueueTask(() => directory.RegisterSingleActivationAsync(address), this.SchedulingContext);
                if (address.Equals(returnedAddress)) return;

                SiloAddress primaryDirectoryForGrain = directory.GetPrimaryForGrain(address.Grain);
                var dae = new DuplicateActivationException
                {
                    ActivationToUse = returnedAddress,
                    PrimaryDirectoryForGrain = primaryDirectoryForGrain
                };

                throw dae;
            }
            else
            {
                StatelessWorkerPlacement stPlacement = activation.PlacedUsing as StatelessWorkerPlacement;
                int maxNumLocalActivations = stPlacement.MaxLocal;
                lock (activations)
                {
                    List<ActivationData> local;
                    if (!LocalLookup(address.Grain, out local) || local.Count <= maxNumLocalActivations)
                        return;

                    var id = StatelessWorkerDirector.PickRandom(local).Address;
                    var dae = new DuplicateActivationException
                    {
                        ActivationToUse = id,
                    };
                    throw dae;
                }
            }
            // We currently don't have any other case for multiple activations except for StatelessWorker.
            //await scheduler.RunOrQueueTask(() => directory.RegisterAsync(address), this.SchedulingContext);
        }
Exemple #10
0
        private void DeactivateActivationImpl(ActivationData data, StatisticName statisticName)
        {
            bool promptly = false;
            bool alreadBeingDestroyed = false;
            lock (data)
            {
                if (data.State == ActivationState.Valid)
                {
                    // Change the ActivationData state here, since we're about to give up the lock.
                    data.PrepareForDeactivation(); // Don't accept any new messages
                    ActivationCollector.TryCancelCollection(data);
                    if (!data.IsCurrentlyExecuting)
                    {
                        promptly = true;
                    }
                    else // busy, so destroy later.
                    {
                        data.AddOnInactive(() => DestroyActivationVoid(data));
                    }
                }
                else if (data.State == ActivationState.Create)
                {
                    throw new InvalidOperationException(String.Format(
                        "Activation {0} has called DeactivateOnIdle from within a constructor, which is not allowed.",
                            data.ToString()));
                }
                else if (data.State == ActivationState.Activating)
                {
                    throw new InvalidOperationException(String.Format(
                        "Activation {0} has called DeactivateOnIdle from within OnActivateAsync, which is not allowed.",
                            data.ToString()));
                }
                else
                {
                    alreadBeingDestroyed = true;
                }
            }
            logger.Info(ErrorCode.Catalog_ShutdownActivations_2,
                "DeactivateActivationOnIdle: {0} {1}.", data.ToString(), promptly ? "promptly" : (alreadBeingDestroyed ? "already being destroyed or invalid" : "later when become idle"));

            CounterStatistic.FindOrCreate(statisticName).Increment();
            if (promptly)
            {
                DestroyActivationVoid(data); // Don't await or Ignore, since we are in this activation context and it may have alraedy been destroyed!
            }
        }
Exemple #11
0
 // To be called fro within Activation context.
 // To be used only if an activation is stuck for a long time, since it can lead to a duplicate activation
 internal void DeactivateStuckActivation(ActivationData activationData)
 {
     DeactivateActivationImpl(activationData, StatisticNames.CATALOG_ACTIVATION_SHUTDOWN_VIA_DEACTIVATE_STUCK_ACTIVATION);
     // The unregistration is normally done in the regular deactivation process, but since this activation seems
     // stuck (it might never run the deactivation process), we remove it from the directory directly
     scheduler.RunOrQueueTask(
         () => directory.UnregisterAsync(activationData.Address, UnregistrationCause.Force),
         SchedulingContext)
         .Ignore();
 }
Exemple #12
0
 // this is a compatibility method for portions of the code base that don't use
 // async/await yet, which is almost everything. there's no liability to discarding the
 // Task returned by AsyncSendMessage()
 internal void SendMessage(Message message, ActivationData sendingActivation = null)
 {
     AsyncSendMessage(message, sendingActivation).Ignore();
 }
Exemple #13
0
        /// <summary>
        /// Receive a new message:
        /// - validate order constraints, queue (or possibly redirect) if out of order
        /// - validate transactions constraints
        /// - invoke handler if ready, otherwise enqueue for later invocation
        /// </summary>
        /// <param name="message"></param>
        public void ReceiveMessage(Message message)
        {
            EventSourceUtils.EmitEvent(message, OrleansDispatcherEvent.ReceiveMessageAction);
            MessagingProcessingStatisticsGroup.OnDispatcherMessageReceive(message);
            // Don't process messages that have already timed out
            if (message.IsExpired)
            {
                logger.Warn(ErrorCode.Dispatcher_DroppingExpiredMessage, "Dropping an expired message: {0}", message);
                MessagingProcessingStatisticsGroup.OnDispatcherMessageProcessedError(message, "Expired");
                message.DropExpiredMessage(MessagingStatisticsGroup.Phase.Dispatch);
                return;
            }

            // check if its targeted at a new activation
            if (message.TargetGrain.IsSystemTarget)
            {
                MessagingProcessingStatisticsGroup.OnDispatcherMessageProcessedError(message, "ReceiveMessage on system target.");
                throw new InvalidOperationException("Dispatcher was called ReceiveMessage on system target for " + message);
            }

            try
            {
                Task           ignore;
                ActivationData target = catalog.GetOrCreateActivation(
                    message.TargetAddress,
                    message.IsNewPlacement,
                    message.NewGrainType,
                    String.IsNullOrEmpty(message.GenericGrainType) ? null : message.GenericGrainType,
                    message.RequestContextData,
                    out ignore);

                if (ignore != null)
                {
                    ignore.Ignore();
                }

                if (message.Direction == Message.Directions.Response)
                {
                    ReceiveResponse(message, target);
                }
                else // Request or OneWay
                {
                    if (target.State == ActivationState.Valid)
                    {
                        this.activationCollector.TryRescheduleCollection(target);
                    }
                    // Silo is always capable to accept a new request. It's up to the activation to handle its internal state.
                    // If activation is shutting down, it will queue and later forward this request.
                    ReceiveRequest(message, target);
                }
            }
            catch (Exception ex)
            {
                try
                {
                    MessagingProcessingStatisticsGroup.OnDispatcherMessageProcessedError(message, "Non-existent activation");

                    var nea = ex as Catalog.NonExistentActivationException;
                    if (nea == null)
                    {
                        var str = $"Error creating activation for {message.NewGrainType}. Message {message}";
                        logger.Error(ErrorCode.Dispatcher_ErrorCreatingActivation, str, ex);
                        throw new OrleansException(str, ex);
                    }

                    if (nea.IsStatelessWorker)
                    {
                        if (logger.IsEnabled(LogLevel.Debug))
                        {
                            logger.Debug(ErrorCode.Dispatcher_Intermediate_GetOrCreateActivation,
                                         $"Intermediate StatelessWorker NonExistentActivation for message {message}, Exception {ex}");
                        }
                    }
                    else
                    {
                        logger.Info(ErrorCode.Dispatcher_Intermediate_GetOrCreateActivation,
                                    $"Intermediate NonExistentActivation for message {message}, with Exception {ex}");
                    }

                    ActivationAddress nonExistentActivation = nea.NonExistentActivation;

                    if (message.Direction != Message.Directions.Response)
                    {
                        // Un-register the target activation so we don't keep getting spurious messages.
                        // The time delay (one minute, as of this writing) is to handle the unlikely but possible race where
                        // this request snuck ahead of another request, with new placement requested, for the same activation.
                        // If the activation registration request from the new placement somehow sneaks ahead of this un-registration,
                        // we want to make sure that we don't un-register the activation we just created.
                        // We would add a counter here, except that there's already a counter for this in the Catalog.
                        // Note that this has to run in a non-null scheduler context, so we always queue it to the catalog's context
                        var origin = message.SendingSilo;
                        scheduler.QueueWorkItem(new ClosureWorkItem(
                                                    // don't use message.TargetAddress, cause it may have been removed from the headers by this time!
                                                    async() =>
                        {
                            try
                            {
                                await this.localGrainDirectory.UnregisterAfterNonexistingActivation(
                                    nonExistentActivation, origin);
                            }
                            catch (Exception exc)
                            {
                                logger.Warn(ErrorCode.Dispatcher_FailedToUnregisterNonExistingAct,
                                            $"Failed to un-register NonExistentActivation {nonExistentActivation}", exc);
                            }
                        },
                                                    "LocalGrainDirectory.UnregisterAfterNonexistingActivation"),
                                                catalog.SchedulingContext);

                        ProcessRequestToInvalidActivation(message, nonExistentActivation, null, "Non-existent activation");
                    }
                    else
                    {
                        logger.Warn(
                            ErrorCode.Dispatcher_NoTargetActivation,
                            nonExistentActivation.Silo.IsClient
                                ? "No target client {0} for response message: {1}. It's likely that the client recently disconnected."
                                : "No target activation {0} for response message: {1}",
                            nonExistentActivation,
                            message);

                        this.localGrainDirectory.InvalidateCacheEntry(nonExistentActivation);
                    }
                }
                catch (Exception exc)
                {
                    // Unable to create activation for this request - reject message
                    RejectMessage(message, Message.RejectionTypes.Transient, exc);
                }
            }
        }
        private void SendRequestMessage(
            GrainReference target,
            Message message,
            TaskCompletionSource <object> context,
            Action <Message, TaskCompletionSource <object> > callback,
            string debugContext,
            InvokeMethodOptions options,
            string genericArguments = null)
        {
            // fill in sender
            if (message.SendingSilo == null)
            {
                message.SendingSilo = MySilo;
            }
            if (!String.IsNullOrEmpty(genericArguments))
            {
                message.GenericGrainType = genericArguments;
            }

            SchedulingContext schedulingContext = RuntimeContext.Current != null ?
                                                  RuntimeContext.Current.ActivationContext as SchedulingContext : null;

            ActivationData sendingActivation = null;

            if (schedulingContext == null)
            {
                throw new InvalidExpressionException(
                          String.Format("Trying to send a message {0} on a silo not from within grain and not from within system target (RuntimeContext is not set to SchedulingContext) "
                                        + "RuntimeContext.Current={1} TaskScheduler.Current={2}",
                                        message,
                                        RuntimeContext.Current == null ? "null" : RuntimeContext.Current.ToString(),
                                        TaskScheduler.Current));
            }
            switch (schedulingContext.ContextType)
            {
            case SchedulingContextType.SystemThread:
                throw new ArgumentException(
                          String.Format("Trying to send a message {0} on a silo not from within grain and not from within system target (RuntimeContext is of SchedulingContextType.SystemThread type)", message), "context");

            case SchedulingContextType.Activation:
                message.SendingActivation = schedulingContext.Activation.ActivationId;
                message.SendingGrain      = schedulingContext.Activation.Grain;
                sendingActivation         = schedulingContext.Activation;
                break;

            case SchedulingContextType.SystemTarget:
                message.SendingActivation = schedulingContext.SystemTarget.ActivationId;
                message.SendingGrain      = schedulingContext.SystemTarget.GrainId;
                break;
            }

            // fill in destination
            var targetGrainId = target.GrainId;

            message.TargetGrain = targetGrainId;
            if (targetGrainId.IsSystemTarget)
            {
                SiloAddress targetSilo = (target.SystemTargetSilo ?? MySilo);
                message.TargetSilo       = targetSilo;
                message.TargetActivation = ActivationId.GetSystemActivation(targetGrainId, targetSilo);
                message.Category         = targetGrainId.Equals(Constants.MembershipOracleId) ?
                                           Message.Categories.Ping : Message.Categories.System;
            }
            if (target.IsObserverReference)
            {
                message.TargetObserverId = target.ObserverId;
            }

            if (debugContext != null)
            {
                message.DebugContext = debugContext;
            }

            var oneWay = (options & InvokeMethodOptions.OneWay) != 0;

            if (context == null && !oneWay)
            {
                logger.Warn(ErrorCode.IGC_SendRequest_NullContext, "Null context {0}: {1}", message, new StackTrace());
            }

            if (message.IsExpirableMessage(Config.Globals))
            {
                message.Expiration = DateTime.UtcNow + ResponseTimeout + Constants.MAXIMUM_CLOCK_SKEW;
            }

            if (!oneWay)
            {
                var callbackData = new CallbackData(
                    callback,
                    TryResendMessage,
                    context,
                    message,
                    () => UnRegisterCallback(message.Id),
                    Config.Globals);
                callbacks.TryAdd(message.Id, callbackData);
                callbackData.StartTimer(ResponseTimeout);
            }

            if (targetGrainId.IsSystemTarget)
            {
                // Messages to system targets bypass the task system and get sent "in-line"
                dispatcher.TransportMessage(message);
            }
            else
            {
                dispatcher.SendMessage(message, sendingActivation);
            }
        }
Exemple #15
0
 /// <summary>
 /// Send an outgoing message
 /// - may buffer for transaction completion / commit if it ends a transaction
 /// - choose target placement address, maintaining send order
 /// - add ordering info & maintain send order
 /// 
 /// </summary>
 /// <param name="message"></param>
 /// <param name="sendingActivation"></param>
 public async Task AsyncSendMessage(Message message, ActivationData sendingActivation = null)
 {
     try
     {
         await AddressMessage(message);
         TransportMessage(message);
     }
     catch (Exception ex)
     {
         if (!(ex.GetBaseException() is KeyNotFoundException))
         {
             logger.Error(ErrorCode.Dispatcher_SelectTarget_Failed,
                 String.Format("SelectTarget failed with {0}", ex.Message),
                 ex);
         }
         MessagingProcessingStatisticsGroup.OnDispatcherMessageProcessedError(message, "SelectTarget failed");
         RejectMessage(message, Message.RejectionTypes.Unrecoverable, ex);
     }
 }
Exemple #16
0
        /// <summary>
        /// Invoked when an activation has finished a transaction and may be ready for additional transactions
        /// </summary>
        /// <param name="activation">The activation that has just completed processing this message</param>
        /// <param name="message">The message that has just completed processing. 
        /// This will be <c>null</c> for the case of completion of Activate/Deactivate calls.</param>
        internal void OnActivationCompletedRequest(ActivationData activation, Message message)
        {
            lock (activation)
            {
#if DEBUG
                // This is a hot code path, so using #if to remove diags from Release version
                if (logger.IsVerbose2)
                {
                    logger.Verbose2(ErrorCode.Dispatcher_OnActivationCompletedRequest_Waiting,
                        "OnActivationCompletedRequest {0}: Activation={1}", activation.ActivationId, activation.DumpStatus());
                }
#endif
                activation.ResetRunning(message);

                // ensure inactive callbacks get run even with transactions disabled
                if (!activation.IsCurrentlyExecuting)
                    activation.RunOnInactive();

                // Run message pump to see if there is a new request arrived to be processed
                RunMessagePump(activation);
            }
        }
Exemple #17
0
 /// <summary>
 /// Invoke the activate method on a newly created activation
 /// </summary>
 /// <param name="activation"></param>
 /// <returns></returns>
 private Task InvokeActivate(ActivationData activation)
 {
     // NOTE: This should only be called with the correct schedulering context for the activation to be invoked.
     lock (activation)
     {
         activation.SetState(ActivationState.Activating);
     }
     return scheduler.QueueTask(() => CallGrainActivate(activation), new SchedulingContext(activation)); // Target grain's scheduler context);
     // ActivationData will transition out of ActivationState.Activating via Dispatcher.OnActivationCompletedRequest
 }
Exemple #18
0
        // To be called fro within Activation context.
        // Cannot be awaitable, since after DestroyActivation is done the activation is in Invalid state and cannot await any Task.
        internal void DeactivateActivationOnIdle(ActivationData data)
        {
            bool promptly = false;
            bool alreadBeingDestroyed = false;
            lock (data)
            {
                if (data.State == ActivationState.Valid)
                {
                    // Change the ActivationData state here, since we're about to give up the lock.
                    data.PrepareForDeactivation(); // Don't accept any new messages
                    if (!data.IsCurrentlyExecuting)
                    {
                        promptly = true;
                    }
                    else // busy, so destroy later.
                    {
                        data.AddOnInactive(() => DestroyActivationVoid(data));
                    }
                }
                else
                {
                    alreadBeingDestroyed = true;
                }
            }
            logger.Info(ErrorCode.Catalog_ShutdownActivations_2,
                "DeactivateActivationOnIdle: 1 {0}.", promptly ? "promptly" : (alreadBeingDestroyed ? "already being destroyed or invalid" : "later when become idle"));

            CounterStatistic.FindOrCreate(StatisticNames.CATALOG_ACTIVATION_SHUTDOWN_VIA_DEACTIVATE_ON_IDLE).Increment();
            if (promptly)
            {
                DestroyActivationVoid(data); // Don't await or Ignore, since we are in this activation context and it may have alraedy been destroyed!
            }
        }
Exemple #19
0
 /// <summary>
 /// Register a new object to which messages can be delivered with the local lookup table and scheduler.
 /// </summary>
 /// <param name="activation"></param>
 public void RegisterMessageTarget(ActivationData activation)
 {
     var context = new SchedulingContext(activation);
     scheduler.RegisterWorkContext(context);
     activations.RecordNewTarget(activation);
     activationsCreated.Increment();
 }
Exemple #20
0
 private void DestroyActivationAsync(ActivationData activation, MultiTaskCompletionSource tcs)
 {
     StartDestroyActivations(new List<ActivationData> { activation }, tcs);
 }
Exemple #21
0
        /// <summary>
        /// Unregister message target and stop delivering messages to it
        /// </summary>
        /// <param name="activation"></param>
        public void UnregisterMessageTarget(ActivationData activation)
        {
            activations.RemoveTarget(activation);

            // this should be removed once we've refactored the deactivation code path. For now safe to keep.
            ActivationCollector.TryCancelCollection(activation);
            activationsDestroyed.Increment();

            scheduler.UnregisterWorkContext(new SchedulingContext(activation));

            if (activation.GrainInstance == null) return;

            var grainTypeName = TypeUtils.GetFullName(activation.GrainInstanceType);
            activations.DecrementGrainCounter(grainTypeName);
            activation.SetGrainInstance(null);
        }
Exemple #22
0
        private async Task CallGrainActivate(ActivationData activation)
        {
            var grainTypeName = activation.GrainInstanceType.FullName;

            // Note: This call is being made from within Scheduler.Queue wrapper, so we are already executing on worker thread
            if (logger.IsVerbose) logger.Verbose(ErrorCode.Catalog_BeforeCallingActivate, "About to call {1} grain's OnActivateAsync() method {0}", activation, grainTypeName);

            // Call OnActivateAsync inline, but within try-catch wrapper to safely capture any exceptions thrown from called function
            try
            {
                await activation.GrainInstance.OnActivateAsync();

                if (logger.IsVerbose) logger.Verbose(ErrorCode.Catalog_AfterCallingActivate, "Returned from calling {1} grain's OnActivateAsync() method {0}", activation, grainTypeName);

                lock (activation)
                {
                    if (activation.State == ActivationState.Activating)
                    {
                        activation.SetState(ActivationState.Valid); // Activate calls on this activation are finished
                    }
                    if (!activation.IsCurrentlyExecuting)
                    {
                        activation.RunOnInactive();
                    }
                    // Run message pump to see if there is a new request is queued to be processed
                    dispatcher.RunMessagePump(activation);
                }
            }
            catch (Exception exc)
            {
                logger.Error(ErrorCode.Catalog_ErrorCallingActivate,
                    string.Format("Error calling grain's AsyncActivate method - Grain type = {1} Activation = {0}", activation, grainTypeName), exc);

                activation.SetState(ActivationState.Invalid); // Mark this activation as unusable

                activationsFailedToActivate.Increment();

                throw;
            }
        }
Exemple #23
0
        /// <summary>
        /// If activation already exists, use it
        /// Otherwise, create an activation of an existing grain by reading its state.
        /// Return immediately using a dummy that will queue messages.
        /// Concurrently start creating and initializing the real activation and replace it when it is ready.
        /// </summary>
        /// <param name="address">Grain's activation address</param>
        /// <param name="newPlacement">Creation of new activation was requested by the placement director.</param>
        /// <param name="grainType">The type of grain to be activated or created</param>
        /// <param name="genericArguments">Specific generic type of grain to be activated or created</param>
        /// <param name="activatedPromise"></param>
        /// <returns></returns>
        public ActivationData GetOrCreateActivation(
            ActivationAddress address,
            bool newPlacement,
            string grainType,
            string genericArguments,
            out Task activatedPromise)
        {
            ActivationData result;
            activatedPromise = TaskDone.Done;
            lock (activations)
            {
                if (TryGetActivationData(address.Activation, out result))
                {
                    ActivationCollector.TryRescheduleCollection(result);
                    return result;
                }
                
                if (newPlacement && !SiloStatusOracle.CurrentStatus.IsTerminating())
                {
                    // create a dummy activation that will queue up messages until the real data arrives
                    PlacementStrategy placement;
                    int typeCode = address.Grain.GetTypeCode();
                    string actualGrainType = null;

                    if (typeCode != 0) // special case for Membership grain.
                        GetGrainTypeInfo(typeCode, out actualGrainType, out placement);
                    else
                        placement = SystemPlacement.Singleton;

                    if (string.IsNullOrEmpty(grainType))
                    {
                        grainType = actualGrainType;
                    }

                    // We want to do this (RegisterMessageTarget) under the same lock that we tested TryGetActivationData. They both access ActivationDirectory.
                    result = new ActivationData(
                        address, 
                        genericArguments, 
                        placement, 
                        ActivationCollector, 
                        config.Application.GetCollectionAgeLimit(grainType));
                    RegisterMessageTarget(result);
                }
            } // End lock

            // Did not find and did not start placing new
            if (result == null)
            {
                var msg = String.Format("Non-existent activation: {0}, grain type: {1}.",
                                           address.ToFullString(), grainType);
                if (logger.IsVerbose) logger.Verbose(ErrorCode.CatalogNonExistingActivation2, msg);
                CounterStatistic.FindOrCreate(StatisticNames.CATALOG_ACTIVATION_NON_EXISTENT_ACTIVATIONS).Increment();
                throw new NonExistentActivationException(msg) { NonExistentActivation = address };
            }
   
            SetupActivationInstance(result, grainType, genericArguments);
            activatedPromise = InitActivation(result, grainType, genericArguments);
            return result;
        }
Exemple #24
0
        private void SetupActivationInstance(ActivationData result, string grainType, string genericInterface)
        {
            var genericArguments = String.IsNullOrEmpty(genericInterface) ? null
                : TypeUtils.GenericTypeArgsString(genericInterface);

            lock (result)
            {
                if (result.GrainInstance == null)
                {
                    CreateGrainInstance(grainType, result, genericArguments);
                }
            }
        }
Exemple #25
0
        private async Task InitActivation(ActivationData activation, string grainType, string genericInterface)
        {
            // We've created a dummy activation, which we'll eventually return, but in the meantime we'll queue up (or perform promptly)
            // the operations required to turn the "dummy" activation into a real activation
            ActivationAddress address = activation.Address;

            int initStage = 0;
            // A chain of promises that will have to complete in order to complete the activation
            // Register with the grain directory, register with the store if necessary and call the Activate method on the new activation.
            try
            {
                initStage = 1;
                await RegisterActivationInGrainDirectory(address, !activation.IsMultiActivationGrain);

                initStage = 2;
                await SetupActivationState(activation, grainType);                

                initStage = 3;
                await InvokeActivate(activation);

                ActivationCollector.ScheduleCollection(activation);

                // Success!! Log the result, and start processing messages
                if (logger.IsVerbose) logger.Verbose("InitActivation is done: {0}", address);
            }
            catch (Exception ex)
            {
                lock (activation)
                {
                    activation.SetState(ActivationState.Invalid);
                    try
                    {
                        UnregisterMessageTarget(activation);
                    }
                    catch (Exception exc)
                    {
                        logger.Warn(ErrorCode.Catalog_UnregisterMessageTarget4, String.Format("UnregisterMessageTarget failed on {0}.", activation), exc);
                    }

                    switch (initStage)
                    {
                        case 1: // failed to RegisterActivationInGrainDirectory
                            
                            ActivationAddress target = null;
                            Exception dupExc;
                            // Failure!! Could it be that this grain uses single activation placement, and there already was an activation?
                            if (Utils.TryFindException(ex, typeof (DuplicateActivationException), out dupExc))
                            {
                                target = ((DuplicateActivationException) dupExc).ActivationToUse;
                                CounterStatistic.FindOrCreate(StatisticNames.CATALOG_ACTIVATION_DUPLICATE_ACTIVATIONS)
                                    .Increment();
                            }
                            activation.ForwardingAddress = target;
                            if (target != null)
                            {
                                // If this was a duplicate, it's not an error, just a race.
                                // Forward on all of the pending messages, and then forget about this activation.
                                logger.Info(ErrorCode.Catalog_DuplicateActivation,
                                    "Tried to create a duplicate activation {0}, but we'll use {1} instead. " +
                                    "GrainInstanceType is {2}. " +
                                    "Primary Directory partition for this grain is {3}, " +
                                    "full activation address is {4}. We have {5} messages to forward.",
                                    address,
                                    target,
                                    activation.GrainInstanceType,
                                    ((DuplicateActivationException) dupExc).PrimaryDirectoryForGrain,
                                    address.ToFullString(),
                                    activation.WaitingCount);

                                RerouteAllQueuedMessages(activation, target, "Duplicate activation", ex);
                            }
                            else
                            {
                                logger.Warn(ErrorCode.Runtime_Error_100064,
                                    String.Format("Failed to RegisterActivationInGrainDirectory for {0}.",
                                        activation), ex);
                                // Need to undo the registration we just did earlier
                                scheduler.RunOrQueueTask(() => directory.UnregisterAsync(address),
                                    SchedulingContext).Ignore();

                                RerouteAllQueuedMessages(activation, null,
                                    "Failed RegisterActivationInGrainDirectory", ex);
                            }
                            break;

                        case 2: // failed to setup persistent state
                            
                            logger.Warn(ErrorCode.Catalog_Failed_SetupActivationState,
                                String.Format("Failed to SetupActivationState for {0}.", activation), ex);
                            // Need to undo the registration we just did earlier
                            scheduler.RunOrQueueTask(() => directory.UnregisterAsync(address),
                                SchedulingContext).Ignore();

                            RerouteAllQueuedMessages(activation, null, "Failed SetupActivationState", ex);
                            break;

                        case 3: // failed to InvokeActivate
                            
                            logger.Warn(ErrorCode.Catalog_Failed_InvokeActivate,
                                String.Format("Failed to InvokeActivate for {0}.", activation), ex);
                            // Need to undo the registration we just did earlier
                            scheduler.RunOrQueueTask(() => directory.UnregisterAsync(address),
                                SchedulingContext).Ignore();

                            RerouteAllQueuedMessages(activation, null, "Failed InvokeActivate", ex);
                            break;
                    }
                }
                throw;
            }
        }
Exemple #26
0
 /// <summary>
 /// Check if we can locally accept this message.
 /// Redirects if it can't be accepted.
 /// </summary>
 /// <param name="message"></param>
 /// <param name="targetActivation"></param>
 private void ReceiveRequest(Message message, ActivationData targetActivation)
 {
     lock (targetActivation)
     {
         if (targetActivation.State == ActivationState.Invalid)
         {
             ProcessRequestToInvalidActivation(
                 message,
                 targetActivation.Address,
                 targetActivation.ForwardingAddress,
                 "ReceiveRequest");
         }
         else if (!ActivationMayAcceptRequest(targetActivation, message))
         {
             // Check for deadlock before Enqueueing.
             if (config.Globals.PerformDeadlockDetection && !message.TargetGrain.IsSystemTarget)
             {
                 try
                 {
                     CheckDeadlock(message);
                 }
                 catch (DeadlockException exc)
                 {
                     // Record that this message is no longer flowing through the system
                     MessagingProcessingStatisticsGroup.OnDispatcherMessageProcessedError(message, "Deadlock");
                     logger.Warn(ErrorCode.Dispatcher_DetectedDeadlock, 
                         "Detected Application Deadlock: {0}", exc.Message);
                     // We want to send DeadlockException back as an application exception, rather than as a system rejection.
                     SendResponse(message, Response.ExceptionResponse(exc));
                     return;
                 }
             }
             EnqueueRequest(message, targetActivation);
         }
         else
         {
             HandleIncomingRequest(message, targetActivation);
         }
     }
 }
Exemple #27
0
        /// <summary>
        /// Perform just the prompt, local part of creating an activation object
        /// Caller is responsible for registering locally, registering with store and calling its activate routine
        /// </summary>
        /// <param name="grainTypeName"></param>
        /// <param name="data"></param>
        /// <param name="genericArguments"></param>
        /// <returns></returns>
        private void CreateGrainInstance(string grainTypeName, ActivationData data, string genericArguments)
        {
            string grainClassName;
            var interfaceToClassMap = GrainTypeManager.GetGrainInterfaceToClassMap();
            if (!interfaceToClassMap.TryGetValue(grainTypeName, out grainClassName))
            {
                // Lookup from grain type code
                var typeCode = data.Grain.GetTypeCode();
                if (typeCode != 0)
                {
                    PlacementStrategy unused;
                    GetGrainTypeInfo(typeCode, out grainClassName, out unused, genericArguments);
                }
                else
                {
                    grainClassName = grainTypeName;
                }
            }

            GrainTypeData grainTypeData = GrainTypeManager[grainClassName];

            Type grainType = grainTypeData.Type;
            Type stateObjectType = grainTypeData.StateObjectType;
            lock (data)
            {
                var grain = (Grain) Activator.CreateInstance(grainType);
                grain.Identity = data.Identity;
                grain.Runtime = grainRuntime;
                data.SetGrainInstance(grain);

                if (stateObjectType != null)
                {
                    SetupStorageProvider(data);

                    var state = (GrainState)Activator.CreateInstance(stateObjectType);
                    state.InitState(null);
                    data.GrainInstance.GrainState = state;
                    data.GrainInstance.Storage = new GrainStateStorageBridge(data.GrainTypeName, data.GrainInstance, data.StorageProvider);
                }
            }

            activations.IncrementGrainCounter(grainClassName);

            data.GrainInstance.Data = data;

            if (logger.IsVerbose) logger.Verbose("CreateGrainInstance {0}{1}", data.Grain, data.ActivationId);
        }
Exemple #28
0
        /// <summary>
        /// Whether an incoming message can interleave 
        /// </summary>
        /// <param name="targetActivation"></param>
        /// <param name="incoming"></param>
        /// <returns></returns>
        public bool CanInterleave(ActivationData targetActivation, Message incoming)
        {
            bool canInterleave = 
                   catalog.IsReentrantGrain(targetActivation.ActivationId)
                || incoming.IsAlwaysInterleave
                || targetActivation.Running == null
                || (targetActivation.Running.IsReadOnly && incoming.IsReadOnly);

            return canInterleave;
        }
Exemple #29
0
        private void SetupStorageProvider(ActivationData data)
        {
            var grainTypeName = data.GrainInstanceType.FullName;

            // Get the storage provider name, using the default if not specified.
            var attrs = data.GrainInstanceType.GetCustomAttributes(typeof(StorageProviderAttribute), true);
            var attr = attrs.FirstOrDefault() as StorageProviderAttribute;
            var storageProviderName = attr != null ? attr.ProviderName : Constants.DEFAULT_STORAGE_PROVIDER_NAME;

            IStorageProvider provider;
            if (storageProviderManager == null || storageProviderManager.GetNumLoadedProviders() == 0)
            {
                var errMsg = string.Format("No storage providers found loading grain type {0}", grainTypeName);
                logger.Error(ErrorCode.Provider_CatalogNoStorageProvider_1, errMsg);
                throw new BadProviderConfigException(errMsg);
            }
            if (string.IsNullOrWhiteSpace(storageProviderName))
            {
                // Use default storage provider
                provider = storageProviderManager.GetDefaultProvider();
            }
            else
            {
                // Look for MemoryStore provider as special case name
                bool caseInsensitive = Constants.MEMORY_STORAGE_PROVIDER_NAME.Equals(storageProviderName, StringComparison.OrdinalIgnoreCase);
                storageProviderManager.TryGetProvider(storageProviderName, out provider, caseInsensitive);
                if (provider == null)
                {
                    var errMsg = string.Format(
                        "Cannot find storage provider with Name={0} for grain type {1}", storageProviderName,
                        grainTypeName);
                    logger.Error(ErrorCode.Provider_CatalogNoStorageProvider_2, errMsg);
                    throw new BadProviderConfigException(errMsg);
                }
            }
            data.StorageProvider = provider;

            if (logger.IsVerbose2)
            {
                string msg = string.Format("Assigned storage provider with Name={0} to grain type {1}",
                    storageProviderName, grainTypeName);
                logger.Verbose2(ErrorCode.Provider_CatalogStorageProviderAllocated, msg);
            }
        }
Exemple #30
0
        /// <summary>
        /// Enqueue message for local handling after transaction completes
        /// </summary>
        /// <param name="message"></param>
        /// <param name="targetActivation"></param>
        private void EnqueueRequest(Message message, ActivationData targetActivation)
        {
            var overloadException = targetActivation.CheckOverloaded(logger);
            if (overloadException != null)
            {
                MessagingProcessingStatisticsGroup.OnDispatcherMessageProcessedError(message, "Overload2");
                RejectMessage(message, Message.RejectionTypes.Overloaded, overloadException, "Target activation is overloaded " + targetActivation);
                return;
            }
            
            bool enqueuedOk = targetActivation.EnqueueMessage(message);
            if (!enqueuedOk)
            {
                ProcessRequestToInvalidActivation(message, targetActivation.Address, targetActivation.ForwardingAddress, "EnqueueRequest");
            }

            // Dont count this as end of processing. The message will come back after queueing via HandleIncomingRequest.

#if DEBUG
            // This is a hot code path, so using #if to remove diags from Release version
            // Note: Caller already holds lock on activation
            if (logger.IsVerbose2) logger.Verbose2(ErrorCode.Dispatcher_EnqueueMessage,
                "EnqueueMessage for {0}: targetActivation={1}", message.TargetActivation, targetActivation.DumpStatus());
#endif
        }
Exemple #31
0
        private async Task SetupActivationState(ActivationData result, string grainType)
        {
            var state = result.GrainInstance.GrainState;

            if (result.StorageProvider != null && state != null)
            {
                var sw = Stopwatch.StartNew();
                // Populate state data
                try
                {
                    var grainRef = result.GrainReference;

                    await scheduler.RunOrQueueTask(() =>
                        result.StorageProvider.ReadStateAsync(grainType, grainRef, state),
                        new SchedulingContext(result));

                    sw.Stop();
                    StorageStatisticsGroup.OnStorageActivate(result.StorageProvider, grainType, result.GrainReference, sw.Elapsed);
                    result.GrainInstance.GrainState = state;
                }
                catch (Exception ex)
                {
                    StorageStatisticsGroup.OnStorageActivateError(result.StorageProvider, grainType, result.GrainReference);
                    sw.Stop();
                    if (!(ex.GetBaseException() is KeyNotFoundException))
                        throw;

                    result.GrainInstance.GrainState = state; // Just keep original empty state object
                }
            }
        }
Exemple #32
0
 // this is a compatibility method for portions of the code base that don't use
 // async/await yet, which is almost everything. there's no liability to discarding the
 // Task returned by AsyncSendMessage()
 internal void SendMessage(Message message, ActivationData sendingActivation = null)
 {
     AsyncSendMessage(message, sendingActivation).Ignore();
 }
Exemple #33
0
        /// <summary>
        /// Try to get runtime data for an activation
        /// </summary>
        /// <param name="activationId"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool TryGetActivationData(ActivationId activationId, out ActivationData data)
        {
            data = null;
            if (activationId.IsSystem) return false;

            data = activations.FindTarget(activationId);
            return data != null;
        }
Exemple #34
0
        internal void RunMessagePump(ActivationData activation)
        {
            // Note: this method must be called while holding lock (activation)
#if DEBUG
            // This is a hot code path, so using #if to remove diags from Release version
            // Note: Caller already holds lock on activation
            if (logger.IsVerbose2)
            {
                logger.Verbose2(ErrorCode.Dispatcher_ActivationEndedTurn_Waiting,
                    "RunMessagePump {0}: Activation={1}", activation.ActivationId, activation.DumpStatus());
            }
#endif
            // don't run any messages if activation is not ready or deactivating
            if (!activation.State.Equals(ActivationState.Valid)) return;

            bool runLoop;
            do
            {
                runLoop = false;
                var nextMessage = activation.PeekNextWaitingMessage();
                if (nextMessage == null) continue;
                if (!ActivationMayAcceptRequest(activation, nextMessage)) continue;
                
                activation.DequeueNextWaitingMessage();
                // we might be over-writing an already running read only request.
                HandleIncomingRequest(nextMessage, activation);
                runLoop = true;
            }
            while (runLoop);
        }
Exemple #35
0
        /// <summary>
        /// Receive a new message:
        /// - validate order constraints, queue (or possibly redirect) if out of order
        /// - validate transactions constraints
        /// - invoke handler if ready, otherwise enqueue for later invocation
        /// </summary>
        /// <param name="message"></param>
        public void ReceiveMessage(Message message)
        {
            MessagingProcessingStatisticsGroup.OnDispatcherMessageReceive(message);
            // Don't process messages that have already timed out
            if (message.IsExpired)
            {
                logger.Warn(ErrorCode.Dispatcher_DroppingExpiredMessage, "Dropping an expired message: {0}", message);
                MessagingProcessingStatisticsGroup.OnDispatcherMessageProcessedError(message, "Expired");
                message.DropExpiredMessage(MessagingStatisticsGroup.Phase.Dispatch);
                return;
            }

            // check if its targeted at a new activation
            if (message.TargetGrain.IsSystemTarget)
            {
                MessagingProcessingStatisticsGroup.OnDispatcherMessageProcessedError(message, "ReceiveMessage on system target.");
                throw new InvalidOperationException("Dispatcher was called ReceiveMessage on system target for " + message);
            }

            if (errorInjection && ShouldInjectError(message))
            {
                if (logger.IsVerbose)
                {
                    logger.Verbose(ErrorCode.Dispatcher_InjectingRejection, "Injecting a rejection");
                }
                MessagingProcessingStatisticsGroup.OnDispatcherMessageProcessedError(message, "ErrorInjection");
                RejectMessage(message, Message.RejectionTypes.Unrecoverable, null, "Injected rejection");
                return;
            }

            try
            {
                Task           ignore;
                ActivationData target = catalog.GetOrCreateActivation(
                    message.TargetAddress,
                    message.IsNewPlacement,
                    message.NewGrainType,
                    message.GenericGrainType,
                    message.RequestContextData,
                    out ignore);

                if (ignore != null)
                {
                    ignore.Ignore();
                }

                if (message.Direction == Message.Directions.Response)
                {
                    ReceiveResponse(message, target);
                }
                else // Request or OneWay
                {
                    // Silo is always capable to accept a new request. It's up to the activation to handle its internal state.
                    // If activation is shutting down, it will queue and later forward this request.
                    ReceiveRequest(message, target);
                }
            }
            catch (Exception ex)
            {
                try
                {
                    MessagingProcessingStatisticsGroup.OnDispatcherMessageProcessedError(message, "Non-existent activation");

                    var nea = ex as Catalog.NonExistentActivationException;
                    if (nea == null)
                    {
                        var str = String.Format("Error creating activation for {0}. Message {1}", message.NewGrainType, message);
                        logger.Error(ErrorCode.Dispatcher_ErrorCreatingActivation, str, ex);
                        throw new OrleansException(str, ex);
                    }

                    if (nea.IsStatelessWorker)
                    {
                        if (logger.IsVerbose)
                        {
                            logger.Verbose(ErrorCode.Dispatcher_Intermediate_GetOrCreateActivation,
                                           String.Format("Intermediate StatelessWorker NonExistentActivation for message {0}", message), ex);
                        }
                    }
                    else
                    {
                        logger.Info(ErrorCode.Dispatcher_Intermediate_GetOrCreateActivation,
                                    String.Format("Intermediate NonExistentActivation for message {0}", message), ex);
                    }

                    ActivationAddress nonExistentActivation = nea.NonExistentActivation;

                    if (message.Direction != Message.Directions.Response)
                    {
                        // Un-register the target activation so we don't keep getting spurious messages.
                        // The time delay (one minute, as of this writing) is to handle the unlikely but possible race where
                        // this request snuck ahead of another request, with new placement requested, for the same activation.
                        // If the activation registration request from the new placement somehow sneaks ahead of this un-registration,
                        // we want to make sure that we don't un-register the activation we just created.
                        // We would add a counter here, except that there's already a counter for this in the Catalog.
                        // Note that this has to run in a non-null scheduler context, so we always queue it to the catalog's context
                        if (config.Globals.DirectoryLazyDeregistrationDelay > TimeSpan.Zero)
                        {
                            Scheduler.QueueWorkItem(new ClosureWorkItem(
                                                        // don't use message.TargetAddress, cause it may have been removed from the headers by this time!
                                                        async() =>
                            {
                                try
                                {
                                    await Silo.CurrentSilo.LocalGrainDirectory.UnregisterConditionallyAsync(
                                        nonExistentActivation);
                                }
                                catch (Exception exc)
                                {
                                    logger.Warn(ErrorCode.Dispatcher_FailedToUnregisterNonExistingAct,
                                                String.Format("Failed to un-register NonExistentActivation {0}",
                                                              nonExistentActivation), exc);
                                }
                            },
                                                        () => "LocalGrainDirectory.UnregisterConditionallyAsync"),
                                                    catalog.SchedulingContext);
                        }
                        ProcessRequestToInvalidActivation(message, nonExistentActivation, null, "Non-existent activation");
                    }
                    else
                    {
                        logger.Warn(ErrorCode.Dispatcher_NoTargetActivation,
                                    "No target activation {0} for response message: {1}", nonExistentActivation, message);
                        Silo.CurrentSilo.LocalGrainDirectory.InvalidateCacheEntry(nonExistentActivation);
                    }
                }
                catch (Exception exc)
                {
                    // Unable to create activation for this request - reject message
                    RejectMessage(message, Message.RejectionTypes.Transient, exc);
                }
            }
        }