/// <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(); }
/// <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(); }
/// <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; }
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()); }
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); } }
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; }