diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/ArcGISThreadContext.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/ArcGISThreadContext.cs index dee64e930..112ef1448 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/ArcGISThreadContext.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/ArcGISThreadContext.cs @@ -14,11 +14,12 @@ protected override ValueTask MainToWorkerAsync(Func> action) } else { - return QueuedTask.Run(async() => await action().BackToCurrent()).AsValueTask(); + return QueuedTask.Run(async () => await action().BackToCurrent()).AsValueTask(); } } - protected override ValueTask WorkerToMainAsync(Func> action) => QueuedTask.Run(async() => await action().BackToCurrent()).AsValueTask(); + protected override ValueTask WorkerToMainAsync(Func> action) => + QueuedTask.Run(async () => await action().BackToCurrent()).AsValueTask(); protected override ValueTask MainToWorker(Func action) { diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/ArcGisDocumentStore.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/ArcGisDocumentStore.cs index 5a39736f2..0dd2e7abe 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/ArcGisDocumentStore.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/ArcGisDocumentStore.cs @@ -82,47 +82,51 @@ private void OnMapViewChanged(ActiveMapViewChangedEventArgs args) } protected override void HostAppSaveState(string modelCardState) => - _threadContext.RunOnWorker(() => - { - Map map = MapView.Active.Map; - // Read existing metadata - To prevent messing existing metadata. 🤞 Hope other add-in developers will do same :D - var existingMetadata = map.GetMetadata(); - - // Parse existing metadata - XDocument existingXmlDocument = !string.IsNullOrEmpty(existingMetadata) - ? XDocument.Parse(existingMetadata) - : new XDocument(new XElement("metadata")); - - XElement xmlModelCards = new("SpeckleModelCards", modelCardState); - - // Check if SpeckleModelCards element already exists at root and update it - var speckleModelCardsElement = existingXmlDocument.Root?.Element("SpeckleModelCards"); - if (speckleModelCardsElement != null) + _threadContext + .RunOnWorker(() => { - speckleModelCardsElement.ReplaceWith(xmlModelCards); - } - else - { - existingXmlDocument.Root?.Add(xmlModelCards); - } - - map.SetMetadata(existingXmlDocument.ToString()); - }).Wait(); + Map map = MapView.Active.Map; + // Read existing metadata - To prevent messing existing metadata. 🤞 Hope other add-in developers will do same :D + var existingMetadata = map.GetMetadata(); + + // Parse existing metadata + XDocument existingXmlDocument = !string.IsNullOrEmpty(existingMetadata) + ? XDocument.Parse(existingMetadata) + : new XDocument(new XElement("metadata")); + + XElement xmlModelCards = new("SpeckleModelCards", modelCardState); + + // Check if SpeckleModelCards element already exists at root and update it + var speckleModelCardsElement = existingXmlDocument.Root?.Element("SpeckleModelCards"); + if (speckleModelCardsElement != null) + { + speckleModelCardsElement.ReplaceWith(xmlModelCards); + } + else + { + existingXmlDocument.Root?.Add(xmlModelCards); + } + + map.SetMetadata(existingXmlDocument.ToString()); + }) + .Wait(); protected override void LoadState() => - _threadContext.RunOnWorker(() => - { - Map map = MapView.Active.Map; - var metadata = map.GetMetadata(); - var root = XDocument.Parse(metadata).Root; - var element = root?.Element("SpeckleModelCards"); - if (element is null) + _threadContext + .RunOnWorker(() => { - ClearAndSave(); - return; - } - - string modelsString = element.Value; - LoadFromString(modelsString); - }).Wait(); + Map map = MapView.Active.Map; + var metadata = map.GetMetadata(); + var root = XDocument.Parse(metadata).Root; + var element = root?.Element("SpeckleModelCards"); + if (element is null) + { + ClearAndSave(); + return; + } + + string modelsString = element.Value; + LoadFromString(modelsString); + }) + .Wait(); } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitThreadContext.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitThreadContext.cs index b4cc76319..92dd7d521 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitThreadContext.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitThreadContext.cs @@ -7,7 +7,8 @@ public class RevitThreadContext : ThreadContext { protected override ValueTask MainToWorkerAsync(Func> action) => action(); - protected override ValueTask WorkerToMainAsync(Func> action) => RevitTask.RunAsync(async () => await action().BackToCurrent()).AsValueTask(); + protected override ValueTask WorkerToMainAsync(Func> action) => + RevitTask.RunAsync(async () => await action().BackToCurrent()).AsValueTask(); protected override ValueTask MainToWorker(Func action) => new(action()); diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Receive/RhinoHostObjectBuilder.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Receive/RhinoHostObjectBuilder.cs index b2de7b1d7..1a42a4dfb 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Receive/RhinoHostObjectBuilder.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Receive/RhinoHostObjectBuilder.cs @@ -44,7 +44,9 @@ public RhinoHostObjectBuilder( RhinoMaterialBaker materialBaker, RhinoColorBaker colorBaker, RhinoGroupBaker groupBaker, - ISdkActivityFactory activityFactory, IThreadContext threadContext) + ISdkActivityFactory activityFactory, + IThreadContext threadContext + ) { _converter = converter; _converterSettings = converterSettings; @@ -115,13 +117,15 @@ CancellationToken cancellationToken using (var _ = _activityFactory.Start("Pre baking layers")) { //TODO what is this? This is going to the UI thread - _threadContext.RunOnMain(() => - { - using var layerNoDraw = new DisableRedrawScope(_converterSettings.Current.Document.Views); - var paths = atomicObjectsWithoutInstanceComponentsWithPath.Select(t => t.path).ToList(); - paths.AddRange(instanceComponentsWithPath.Select(t => t.path)); - _layerBaker.CreateAllLayersForReceive(paths, baseLayerName); - }).Wait(); + _threadContext + .RunOnMain(() => + { + using var layerNoDraw = new DisableRedrawScope(_converterSettings.Current.Document.Views); + var paths = atomicObjectsWithoutInstanceComponentsWithPath.Select(t => t.path).ToList(); + paths.AddRange(instanceComponentsWithPath.Select(t => t.path)); + _layerBaker.CreateAllLayersForReceive(paths, baseLayerName); + }) + .Wait(); } // 5 - Convert atomic objects @@ -245,35 +249,37 @@ private void PreReceiveDeepClean(string baseLayerName) RhinoMath.UnsetIntIndex ); - _threadContext.RunOnMain(() => - { - _instanceBaker.PurgeInstances(baseLayerName); - _materialBaker.PurgeMaterials(baseLayerName); - - var doc = _converterSettings.Current.Document; - // Cleans up any previously received objects - if (rootLayerIndex != RhinoMath.UnsetIntIndex) + _threadContext + .RunOnMain(() => { - var documentLayer = doc.Layers[rootLayerIndex]; - var childLayers = documentLayer.GetChildren(); - if (childLayers != null) + _instanceBaker.PurgeInstances(baseLayerName); + _materialBaker.PurgeMaterials(baseLayerName); + + var doc = _converterSettings.Current.Document; + // Cleans up any previously received objects + if (rootLayerIndex != RhinoMath.UnsetIntIndex) { - using var layerNoDraw = new DisableRedrawScope(doc.Views); - foreach (var layer in childLayers) + var documentLayer = doc.Layers[rootLayerIndex]; + var childLayers = documentLayer.GetChildren(); + if (childLayers != null) { - var purgeSuccess = doc.Layers.Purge(layer.Index, true); - if (!purgeSuccess) + using var layerNoDraw = new DisableRedrawScope(doc.Views); + foreach (var layer in childLayers) { - Console.WriteLine($"Failed to purge layer: {layer}"); + var purgeSuccess = doc.Layers.Purge(layer.Index, true); + if (!purgeSuccess) + { + Console.WriteLine($"Failed to purge layer: {layer}"); + } } } + doc.Layers.Purge(documentLayer.Index, true); } - doc.Layers.Purge(documentLayer.Index, true); - } - // Cleans up any previously received group - _groupBaker.PurgeGroups(baseLayerName); - }).Wait(); + // Cleans up any previously received group + _groupBaker.PurgeGroups(baseLayerName); + }) + .Wait(); } /// diff --git a/DUI3/Speckle.Connectors.DUI/Bridge/BrowserBridge.cs b/DUI3/Speckle.Connectors.DUI/Bridge/BrowserBridge.cs index e46774eec..69562d9af 100644 --- a/DUI3/Speckle.Connectors.DUI/Bridge/BrowserBridge.cs +++ b/DUI3/Speckle.Connectors.DUI/Bridge/BrowserBridge.cs @@ -105,25 +105,27 @@ public string[] GetBindingsMethodNames() } public void RunMethod(string methodName, string requestId, string methodArgs) => - _threadContext.RunOnThreadAsync( - async () => - { - var task = await TopLevelExceptionHandler - .CatchUnhandledAsync(async () => + _threadContext + .RunOnThreadAsync( + async () => + { + var task = await TopLevelExceptionHandler + .CatchUnhandledAsync(async () => + { + var result = await ExecuteMethod(methodName, methodArgs).ConfigureAwait(false); + string resultJson = _jsonSerializer.Serialize(result); + await NotifyUIMethodCallResultReady(requestId, resultJson).ConfigureAwait(false); + }) + .ConfigureAwait(false); + if (task.Exception is not null) { - var result = await ExecuteMethod(methodName, methodArgs).ConfigureAwait(false); - string resultJson = _jsonSerializer.Serialize(result); + string resultJson = SerializeFormattedException(task.Exception); await NotifyUIMethodCallResultReady(requestId, resultJson).ConfigureAwait(false); - }) - .ConfigureAwait(false); - if (task.Exception is not null) - { - string resultJson = SerializeFormattedException(task.Exception); - await NotifyUIMethodCallResultReady(requestId, resultJson).ConfigureAwait(false); - } - }, - _threadOptions.RunCommandsOnMainThread - ).Wait(); + } + }, + _threadOptions.RunCommandsOnMainThread + ) + .Wait(); /// /// Used by the action block to invoke the actual method called by the UI. diff --git a/Sdk/Speckle.Connectors.Common/Threading/DefaultThreadContext.cs b/Sdk/Speckle.Connectors.Common/Threading/DefaultThreadContext.cs index 6509eb3c7..29057f544 100644 --- a/Sdk/Speckle.Connectors.Common/Threading/DefaultThreadContext.cs +++ b/Sdk/Speckle.Connectors.Common/Threading/DefaultThreadContext.cs @@ -33,7 +33,12 @@ protected override ValueTask WorkerToMainAsync(Func> action) protected override ValueTask MainToWorkerAsync(Func> action) { - Task> f = Task.Factory.StartNew(async () => await action().BackToCurrent(), default, TaskCreationOptions.LongRunning, TaskScheduler.Default); + Task> f = Task.Factory.StartNew( + async () => await action().BackToCurrent(), + default, + TaskCreationOptions.LongRunning, + TaskScheduler.Default + ); return new ValueTask(f.Unwrap()); } diff --git a/Sdk/Speckle.Connectors.Common/Threading/ThreadContext.cs b/Sdk/Speckle.Connectors.Common/Threading/ThreadContext.cs index 42c44d01c..459f0285d 100644 --- a/Sdk/Speckle.Connectors.Common/Threading/ThreadContext.cs +++ b/Sdk/Speckle.Connectors.Common/Threading/ThreadContext.cs @@ -5,8 +5,6 @@ namespace Speckle.Connectors.Common.Threading; [GenerateAutoInterface] public abstract class ThreadContext : IThreadContext { - - public static bool IsMainThread => Environment.CurrentManagedThreadId == 1 && !Thread.CurrentThread.IsBackground; public async ValueTask RunOnThread(Action action, bool useMain) @@ -88,7 +86,7 @@ public async ValueTask RunOnThreadAsync(Func action, bool useMain) { await MainToWorkerAsync(async () => { - await action().BackToCurrent(); + await action().BackToCurrent(); return new ValueTask(null); }) .BackToCurrent(); @@ -117,6 +115,7 @@ public ValueTask RunOnThreadAsync(Func> action, bool useMain) } return action(); } + protected abstract ValueTask WorkerToMainAsync(Func> action); protected abstract ValueTask MainToWorkerAsync(Func> action); diff --git a/Sdk/Speckle.Connectors.Common/Threading/Yield.cs b/Sdk/Speckle.Connectors.Common/Threading/Yield.cs index d1437dc94..e2215ad7b 100644 --- a/Sdk/Speckle.Connectors.Common/Threading/Yield.cs +++ b/Sdk/Speckle.Connectors.Common/Threading/Yield.cs @@ -4,9 +4,12 @@ namespace Speckle.Connectors.Common.Threading; public static class TaskExtensions { - public static ConfiguredValueTaskAwaitable BackToCurrent (this ValueTask valueTask) => valueTask.ConfigureAwait(true); + public static ConfiguredValueTaskAwaitable BackToCurrent(this ValueTask valueTask) => + valueTask.ConfigureAwait(true); + + public static ConfiguredValueTaskAwaitable BackToAny(this ValueTask valueTask) => + valueTask.ConfigureAwait(false); - public static ConfiguredValueTaskAwaitable BackToAny(this ValueTask valueTask) => valueTask.ConfigureAwait(false); public static ConfiguredValueTaskAwaitable BackToCurrent(this ValueTask valueTask) => valueTask.ConfigureAwait(true); public static ConfiguredValueTaskAwaitable BackToAny(this ValueTask valueTask) => valueTask.ConfigureAwait(false); @@ -18,11 +21,16 @@ public static class TaskExtensions public static ConfiguredTaskAwaitable BackToCurrent(this Task task) => task.ConfigureAwait(true); public static ConfiguredTaskAwaitable BackToAny(this Task task) => task.ConfigureAwait(false); - + public static ValueTask AsValueTask(this Task task) => new(task); + public static ValueTask AsValueTask(this Task task) => new(task); + public static void Wait(this Task task) => task.GetAwaiter().GetResult(); + public static T Wait(this Task task) => task.GetAwaiter().GetResult(); + public static void Wait(this ValueTask task) => task.GetAwaiter().GetResult(); + public static T Wait(this ValueTask task) => task.GetAwaiter().GetResult(); }