/// <summary>
        /// Creates a workers log metric counter
        /// </summary>
        /// <param name="pipelineId"></param>
        public ActiveWorkersLogWriter(string pipelineId)
        {
            _pipelineId = pipelineId;

            _storage        = new Lazy <IStorage>(() => GlobalConfiguration.Configuration.Resolve <IStorage>());
            _dispatcherLock = new DispatcherLock();
        }
Example #2
0
        /// <summary>
        /// Creates a new instance of the eventbus
        /// </summary>
        /// <param name="pipelineFactory"></param>
        /// <param name="pipelineId"></param>
        public EventBus(IPipelineFactory pipelineFactory, string pipelineId)
        {
            _pipelineFactory = pipelineFactory;
            PipelineId       = pipelineId;

            var storage = GlobalConfiguration.Configuration.Resolve <IStorage>();

            _metricService = new MetricService(storage, pipelineId);

            _queue  = new EventQueue();
            _logger = LoggerConfiguration.Setup
                      (
                s =>
            {
                s.AddWriter(new ProcessedEventMetricCounter(_metricService, pipelineId));
                s.AddWriter(new ActiveWorkersLogWriter(pipelineId));
                s.AddWriter(new LogEventStatisticWriter(pipelineId));
            }
                      );

            _metricService.SetMetric(new Metric(MetricType.ThreadCount, "Active Workers", _processors.Count));
            _metricService.SetMetric(new Metric(MetricType.QueueSize, "Events in Queue", _queue.Count));
            _metricService.SetPipelineHeartbeat();

            var options = _pipelineFactory.Options.Merge(GlobalConfiguration.Configuration.GetOptions());

            _minProcessors = options.MinProcessors;

            _cleanupLock = new DispatcherLock();
            _dispatcher  = new EventBusPorcessDispatcher(_processors, _queue, () => new EventProcessor(new EventPipelineWorker(_queue, () => _pipelineFactory.Setup(), _logger, _metricService), CleanupProcessors, _logger), _logger, _metricService, options);
            RunDispatcher();
        }
Example #3
0
        /// <summary>
        /// Creates a new instance of EventProcessor
        /// </summary>
        /// <param name="worker"></param>
        /// <param name="continuation">Task to continue with after the worker is done</param>
        /// <param name="logger"></param>
        public EventProcessor(IWorker worker, Action continuation, ILogger logger)
        {
            logger.Write($"Created new WorkerThread with Id {_id}", Category.Log, LogLevel.Debug, "EventBus");

            _lock         = new DispatcherLock();
            _worker       = worker ?? throw new ArgumentNullException(nameof(worker));
            _continuation = continuation ?? throw new ArgumentNullException(nameof(continuation));
            _logger       = logger;
        }
Example #4
0
        public void ActiveWorkersLogCleanupTask_Execute_Unlock()
        {
            var locker = new DispatcherLock();

            locker.Lock();
            var task = new ActiveWorkersLogCleanupTask(locker, new StorageKey("test"));

            task.Execute(new StorageContext(new Mock <IStorage>().Object));

            Assert.IsFalse(locker.IsLocked());
        }
Example #5
0
        private async Task Initialize([NotNull] TAssetPart assetSideInstance)
        {
            var partId           = new AbsoluteId(Owner.Id.AssetId, assetSideInstance.Id);
            var gameSideInstance = await Editor.Controller.InvokeAsync(() => Editor.Controller.FindGameSidePart(partId));

            if (gameSideInstance == null)
            {
                throw new InvalidOperationException("Unable to reach the game-side instance");
            }

            using (await DispatcherLock.Lock(true, Editor.Controller, Editor.Dispatcher))
            {
                Editor.Controller.Logger.Debug($"Initial linking for part {assetSideInstance.Id}");
                var assetNode = (IAssetNode)Editor.NodeContainer.GetNode(assetSideInstance);
                var gameNode  = Editor.Controller.GameSideNodeContainer.GetOrCreateNode(gameSideInstance);
                gameObjectLinker.LinkGraph(assetNode, gameNode);
            }
        }
Example #6
0
        private async Task PropagatorTask(TAssetPart assetSidePart)
        {
            // Ensure we are ready game-side before doing any operation
            await gameSideReady.Task;

            await Initialize(assetSidePart);

            var contentReferenceCollector = new ContentReferenceCollector(Owner.Asset.PropertyGraph.Definition);
            await Editor.Controller.InvokeTask(() =>
            {
                Editor.Controller.Logger.Debug($"Registering reference to: {Owner.Id}");
                return(Editor.Controller.Loader.Manager.RegisterReferencer(Owner.Id));
            });

            var rootNode = Editor.NodeContainer.GetOrCreateNode(assetSidePart);

            contentReferenceCollector.Visit(rootNode);

            await Editor.Controller.InvokeTask(async() =>
            {
                using (await Editor.Controller.Loader.LockDatabaseAsynchronously())
                {
                    foreach (var contentReference in contentReferenceCollector.ContentReferences)
                    {
                        // Ok to await inside the loop, this call should never yield because we already own the lock.
                        await Editor.Controller.Loader.Manager.PushContentReference(Owner.Id, contentReference.ContentId, contentReference.Node, contentReference.Index);
                    }
                }
            });

            initialized.SetResult(0);

            while (running)
            {
                var e = await propagatorQueue.ReceiveAsync();

                // This allow to post null when disposing so we evaluate running again to exit this task
                if (e == null)
                {
                    continue;
                }

                var assetNode    = (IAssetNode)e.Node;
                var gameSideNode = assetNode.GetContent(GameSideContentKey);
                if (gameSideNode == null)
                {
                    throw new InvalidOperationException("Unable to retrieve the game-side node");
                }

                var index = (e as ItemChangeEventArgs)?.Index ?? NodeIndex.Empty;
                if (!AssetRegistry.IsContentType(e.Node.Descriptor.GetInnerCollectionType()))
                {
                    if (e.Node.Type.IsValueType)
                    {
                        // No need to retrieve and/or duplicate the value for value type, we just propagate the change
                        await Editor.Controller.InvokeAsync(() => UpdateGameSideContent(gameSideNode, e.NewValue, e.ChangeType, index));
                    }
                    else
                    {
                        var value       = RetrieveValue(assetNode, e.NewValue, e.ChangeType, index);
                        var isReference = await PropagatePartReference(gameSideNode, value, e);

                        if (!isReference)
                        {
                            await Editor.Controller.InvokeAsync(() =>
                            {
                                var gameSideValue = CloneObjectForGameSide(value, assetNode, gameSideNode);
                                UpdateGameSideContent(gameSideNode, gameSideValue, e.ChangeType, index);
                            });
                        }
                    }
                }
                else
                {
                    await UpdateGameSideReference(Editor, gameSideNode, e.ChangeType, e.OldValue, e.NewValue, index);
                }

                // Critical section, both Asset thread and Game thread must be locked
                using (await DispatcherLock.Lock(true, Editor.Controller, Editor.Dispatcher))
                {
                    // Link nodes of the asset-side objects with the game-side objects.
                    gameObjectLinker.LinkGraph(assetNode, gameSideNode);
                    // Also collect content reference (this requires only lock on the Asset thread)
                    contentReferenceCollector.Reset();
                    // If the change is a remove, the content reference should have been cleared by UpdateGameSideReference and there's nothing to collect
                    if (e.ChangeType != ContentChangeType.CollectionRemove)
                    {
                        contentReferenceCollector.Visit(assetNode, index);
                    }
                }

                // Update the content reference list on the game-side now that we have push the change.
                await Editor.Controller.InvokeTask(async() =>
                {
                    using (await Editor.Controller.Loader.LockDatabaseAsynchronously())
                    {
                        foreach (var contentReference in contentReferenceCollector.ContentReferences)
                        {
                            // Ok to await inside the loop, this call should never yield because we already own the lock.
                            await Editor.Controller.Loader.Manager.PushContentReference(Owner.Id, contentReference.ContentId, contentReference.Node, contentReference.Index);
                        }
                    }

                    Editor.Controller.TriggerActiveRenderStageReevaluation();
                });
            }

            await Editor.Controller.InvokeTask(() =>
            {
                Editor.Controller.Logger.Debug($"Unregistering reference to: {Owner.Id}");
                return(Editor.Controller.Loader.Manager.RemoveReferencer(Owner.Id));
            });
        }
 /// <summary>
 /// Creates a new instance of a BackgroundTaskDispatcher
 /// </summary>
 /// <param name="dispatcherLock"></param>
 /// <param name="key"></param>
 public ActiveWorkersLogCleanupTask(DispatcherLock dispatcherLock, StorageKey key)
 {
     _dispatcherLock = dispatcherLock ?? throw new ArgumentNullException(nameof(dispatcherLock));
     _key            = key;
 }