Hi all,
My plugin has a SignalR Core client connected to the server. When a user runs a command I want to retrieve some options from the server. The problem is that SingalR Core library allows only async calls to the server and I need to make it from the perfectly synchronous RunCommand method. So I do some bad practice hacking along these lines:
protected override Result RunCommand(RhinoDoc doc, RunMode mode)
var options = HubConnection.InvokeAsync<List<string>>("GetProjectNames").Result;
var go = new GetOption();
I would get away with this on .Net Core but .Net Framework makes Rhino freeze for a while and then windows show some nasty alert message. I believe this is what smart people on the internet call the deadlock.
Does anyone know a safe way to make such async calls from Rhino?
Dear @archimax - just a (really) quick guess, not knowing SignalR Core Library:
shouldn’t you wait (sleep) until the Task / Operation (InvokeAsync…returns something like this i guess) is completed - before you try to get the Result.
dummy code similar to:
var operation = HubConnection.InvokeAsync<List<string>>("GetProjectNames");
Rhino.RhinoApp.WriteLine("Waiting for ProjectNames");
// check and sleep... pseudocode
while (!operation.IsComplete)
Thread.Sleep(100);
// handle fails pseudocode
if (operation.failed) return Result.Failure;
var options = operation.Result;
var go = new GetOption();
but sorry if i am totally wrong…kind regards,tom
This is what I tried so far:
var projects = Task.Run(async () => await HubConnection.InvokeAsync<List<string>>("GetProjectNames")).Result;
var projects = Task.Run(async () => await HubConnection.InvokeAsync<List<string>>("GetProjectNames")).GetAwaiter().GetResult();
var projects = HubConnection.InvokeAsync<List<string>>("GetProjectNames").GetAwaiter().GetResult();
// 4 with ConfigureAwait(false)
var projectsTask = HubConnection.InvokeAsync<List<string>>("GetProjectNames").ConfigureAwait(false);
var projects = projectsTask.GetAwaiter().GetResult();
// 5 with and without TaskContinuationOptions
var projectsTask = HubConnection.InvokeAsync<List<string>>("GetProjectNames");
projectsTask.ContinueWith(x =>
projects = x.GetAwaiter().GetResult();
}, TaskContinuationOptions.ExecuteSynchronously).GetAwaiter().GetResult();
// 6 forcing to run on UIThread
var projectsTask = HubConnection.InvokeAsync<List<string>>("GetProjectNames");
var uiContext = TaskScheduler.FromCurrentSynchronizationContext();
projectsTask.ContinueWith(x =>
projects = x.Result;
}, uiContext).Wait();
All of them cause the deadlock.
I wonder how it is done inside Grasshopper Hops component? I assume it also runs some kind of async tasks from a synchronous grasshopper code, does it?
Have you tried
Task<List<string>> hubTask = HubConnection.InvokeAsync<List<string>>("GetProjectNames"));
List<string> results = hubTask.Result;
The working solution:
List<string> results = Task.Run(() => HubConnection.InvokeAsync<List<string>>("GetProjectNames")).GetAwaiter().GetResult();
Task.Run() runs the async method on a new thread not blocking Rhino UI thread. But it is important that every async call from RhinoCommands has to be done like this.
My problem was that I used hubConnection.StartAsync().Result to start a connection without wrapping it with Task.Run(). It worked pretty well on async void methods like hubConnection.SendAsync(...) so I didn’t notice anything wrong. But when I tried to call a return value method hubConnection.InvokeAsync(...) it blocked the UI. Seems like something inside hubConnection object “stuck” to the UI thread and didn’t allow to start the process on a new thread even when I used Task.Run() for the following methods.
usefull links:
https://docs.microsoft.com/en-us/archive/msdn-magazine/2015/july/async-programming-brownfield-async-development#transform-synchronous-to-asynchronous-code