Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: update generated rpc requests to not omit ConfigureAwait(false) #260

Merged
merged 2 commits into from
Jun 4, 2024

Conversation

robcao
Copy link
Contributor

@robcao robcao commented Jun 4, 2024

What was changed

Updates the code generated rpc calls to just return the Task<Resp> instead of awaiting.

Why?

The current code is being awaited without a ConfigureAwait(false), which can cause deadlocks when being invoked inside synchronization contexts such as WindowsFormsSynchronizationContext.

Checklist

  1. Closes
    No issue created, discussed with Chad on Slack, could create an issue if desired.

  2. How was this tested:

I ran the existing tests locally and made sure they passed.

I could add a windows-only test for this if wanted, but this is very edge case and there are a few msbuild property changes needed to get WinForms into .net6 that would have to be windows-only.

Or I guess I could write a custom SynchronizationContext that does essentially the same thing, and use it for a test?

The deadlock can be simulated by running this console application on a Windows machine:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WinFormsRepro
{
	public class Program
	{
		public static async Task Main(string[] args)
		{
			SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());

			Console.Out.WriteLine("About to call the workflow service!");

			await SimulateWorkflowServiceRpcAsync().ConfigureAwait(false);

			Console.Out.WriteLine("Finished calling the workflow service.");
		}


		public static async Task SimulateWorkflowServiceRpcAsync() => await SimulateTemporalRpcCallAsync();

		public static async Task SimulateTemporalRpcCallAsync()
		{
			var completion = new TaskCompletionSource<string>();

			Task task = Task.Run(() =>
			{
				Thread.Sleep(100);
				completion.SetResult("complete");
			});

			await task.ConfigureAwait(false);

		}
	}

}

with the following .csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0-windows</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>
  </PropertyGroup>

</Project>

I tested that changing SimulateWorkflowServiceRpcAsync to the following does not deadlock.

public static Task SimulateWorkflowServiceRpcAsync() => SimulateTemporalRpcCallAsync();
  1. Any docs updates needed?

No

Copy link
Member

@cretz cretz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, will merge if/when CI passes (I am merging main into your branch first).

@cretz cretz merged commit 43d0f86 into temporalio:main Jun 4, 2024
7 checks passed
@robcao robcao deleted the async-rpc branch June 20, 2024 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants