Skip to content

Commit

Permalink
#19176 fix Task.Result directly blocks thread (#19231)
Browse files Browse the repository at this point in the history
- async should never be blocked on the ApiClient
 To fix we invert the logic and use Task as base and keep consistency, we expect and wait result only on synchronous calls.
  • Loading branch information
filipe-silva authored Aug 4, 2024
1 parent 2c2d690 commit af94bde
Show file tree
Hide file tree
Showing 12 changed files with 208 additions and 268 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -449,10 +449,9 @@ namespace {{packageName}}.Client
/// <param name="configuration">A per-request configuration object.
/// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
private async Task<ApiResponse<T>> ExecClientAsync<T>(Func<RestClient, Task<RestResponse<T>>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl;
var clientOptions = new RestClientOptions(baseUrl)
{
ClientCertificates = configuration.ClientCertificates,
Expand Down Expand Up @@ -486,7 +485,7 @@ namespace {{packageName}}.Client
{
InterceptRequest(request);
RestResponse<T> response = getResponse(client);
RestResponse<T> response = await getResponse(client);
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
Expand Down Expand Up @@ -582,21 +581,21 @@ namespace {{packageName}}.Client
clientOptions.CookieContainer = cookies;
};
Func<RestClient, RestResponse<T>> getResponse = (client) =>
Func<RestClient, Task<RestResponse<T>>> getResponse = (client) =>
{
if (RetryConfiguration.RetryPolicy != null)
{
var policy = RetryConfiguration.RetryPolicy;
var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
return Task.FromResult(DeserializeRestResponseFromPolicy<T>(client, request, policyResult));
}
else
{
return client.Execute<T>(request);
return Task.FromResult(client.Execute<T>(request));
}
};
return ExecClient(getResponse, setOptions, request, options, configuration);
return ExecClientAsync(getResponse, setOptions, request, options, configuration).GetAwaiter().GetResult();
}
{{#supportsAsync}}
Expand All @@ -607,29 +606,25 @@ namespace {{packageName}}.Client
//no extra options
};
Func<RestClient, RestResponse<T>> getResponse = (client) =>
Func<RestClient, Task<RestResponse<T>>> getResponse = async (client) =>
{
Func<Task<RestResponse<T>>> action = async () =>
{{#supportsRetry}}
if (RetryConfiguration.AsyncRetryPolicy != null)
{
{{#supportsRetry}}
if (RetryConfiguration.AsyncRetryPolicy != null)
{
var policy = RetryConfiguration.AsyncRetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
}
else
{
{{/supportsRetry}}
return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
{{#supportsRetry}}
}
{{/supportsRetry}}
};
return action().Result;
var policy = RetryConfiguration.AsyncRetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
}
else
{
{{/supportsRetry}}
return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
{{#supportsRetry}}
}
{{/supportsRetry}}
};
return Task.FromResult<ApiResponse<T>>(ExecClient(getResponse, setOptions, request, options, configuration));
return ExecClientAsync(getResponse, setOptions, request, options, configuration);
}
#region IAsynchronousClient
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -449,10 +449,9 @@ private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response)
/// <param name="configuration">A per-request configuration object.
/// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
private async Task<ApiResponse<T>> ExecClientAsync<T>(Func<RestClient, Task<RestResponse<T>>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl;

var clientOptions = new RestClientOptions(baseUrl)
{
ClientCertificates = configuration.ClientCertificates,
Expand All @@ -469,7 +468,7 @@ private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getRespon
{
InterceptRequest(request);

RestResponse<T> response = getResponse(client);
RestResponse<T> response = await getResponse(client);

// if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
Expand Down Expand Up @@ -565,21 +564,21 @@ private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IRea
clientOptions.CookieContainer = cookies;
};

Func<RestClient, RestResponse<T>> getResponse = (client) =>
Func<RestClient, Task<RestResponse<T>>> getResponse = (client) =>
{
if (RetryConfiguration.RetryPolicy != null)
{
var policy = RetryConfiguration.RetryPolicy;
var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
return Task.FromResult(DeserializeRestResponseFromPolicy<T>(client, request, policyResult));
}
else
{
return client.Execute<T>(request);
return Task.FromResult(client.Execute<T>(request));
}
};

return ExecClient(getResponse, setOptions, request, options, configuration);
return ExecClientAsync(getResponse, setOptions, request, options, configuration).GetAwaiter().GetResult();
}

private Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, CancellationToken cancellationToken = default(CancellationToken))
Expand All @@ -589,25 +588,21 @@ private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IRea
//no extra options
};

Func<RestClient, RestResponse<T>> getResponse = (client) =>
Func<RestClient, Task<RestResponse<T>>> getResponse = async (client) =>
{
Func<Task<RestResponse<T>>> action = async () =>
if (RetryConfiguration.AsyncRetryPolicy != null)
{
if (RetryConfiguration.AsyncRetryPolicy != null)
{
var policy = RetryConfiguration.AsyncRetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
}
else
{
return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
}
};
return action().Result;
var policy = RetryConfiguration.AsyncRetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
}
else
{
return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
}
};

return Task.FromResult<ApiResponse<T>>(ExecClient(getResponse, setOptions, request, options, configuration));
return ExecClientAsync(getResponse, setOptions, request, options, configuration);
}

#region IAsynchronousClient
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -449,10 +449,9 @@ private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response)
/// <param name="configuration">A per-request configuration object.
/// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
private async Task<ApiResponse<T>> ExecClientAsync<T>(Func<RestClient, Task<RestResponse<T>>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl;

var clientOptions = new RestClientOptions(baseUrl)
{
ClientCertificates = configuration.ClientCertificates,
Expand All @@ -469,7 +468,7 @@ private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getRespon
{
InterceptRequest(request);

RestResponse<T> response = getResponse(client);
RestResponse<T> response = await getResponse(client);

// if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
Expand Down Expand Up @@ -565,21 +564,21 @@ private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IRea
clientOptions.CookieContainer = cookies;
};

Func<RestClient, RestResponse<T>> getResponse = (client) =>
Func<RestClient, Task<RestResponse<T>>> getResponse = (client) =>
{
if (RetryConfiguration.RetryPolicy != null)
{
var policy = RetryConfiguration.RetryPolicy;
var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
return Task.FromResult(DeserializeRestResponseFromPolicy<T>(client, request, policyResult));
}
else
{
return client.Execute<T>(request);
return Task.FromResult(client.Execute<T>(request));
}
};

return ExecClient(getResponse, setOptions, request, options, configuration);
return ExecClientAsync(getResponse, setOptions, request, options, configuration).GetAwaiter().GetResult();
}

private Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, CancellationToken cancellationToken = default(CancellationToken))
Expand All @@ -589,25 +588,21 @@ private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IRea
//no extra options
};

Func<RestClient, RestResponse<T>> getResponse = (client) =>
Func<RestClient, Task<RestResponse<T>>> getResponse = async (client) =>
{
Func<Task<RestResponse<T>>> action = async () =>
if (RetryConfiguration.AsyncRetryPolicy != null)
{
if (RetryConfiguration.AsyncRetryPolicy != null)
{
var policy = RetryConfiguration.AsyncRetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
}
else
{
return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
}
};
return action().Result;
var policy = RetryConfiguration.AsyncRetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
}
else
{
return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
}
};

return Task.FromResult<ApiResponse<T>>(ExecClient(getResponse, setOptions, request, options, configuration));
return ExecClientAsync(getResponse, setOptions, request, options, configuration);
}

#region IAsynchronousClient
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -450,10 +450,9 @@ private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response)
/// <param name="configuration">A per-request configuration object.
/// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
private async Task<ApiResponse<T>> ExecClientAsync<T>(Func<RestClient, Task<RestResponse<T>>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl;

var clientOptions = new RestClientOptions(baseUrl)
{
ClientCertificates = configuration.ClientCertificates,
Expand Down Expand Up @@ -485,7 +484,7 @@ private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getRespon
{
InterceptRequest(request);

RestResponse<T> response = getResponse(client);
RestResponse<T> response = await getResponse(client);

// if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
Expand Down Expand Up @@ -581,21 +580,21 @@ private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IRea
clientOptions.CookieContainer = cookies;
};

Func<RestClient, RestResponse<T>> getResponse = (client) =>
Func<RestClient, Task<RestResponse<T>>> getResponse = (client) =>
{
if (RetryConfiguration.RetryPolicy != null)
{
var policy = RetryConfiguration.RetryPolicy;
var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
return Task.FromResult(DeserializeRestResponseFromPolicy<T>(client, request, policyResult));
}
else
{
return client.Execute<T>(request);
return Task.FromResult(client.Execute<T>(request));
}
};

return ExecClient(getResponse, setOptions, request, options, configuration);
return ExecClientAsync(getResponse, setOptions, request, options, configuration).GetAwaiter().GetResult();
}

private Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, CancellationToken cancellationToken = default(CancellationToken))
Expand All @@ -605,25 +604,21 @@ private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IRea
//no extra options
};

Func<RestClient, RestResponse<T>> getResponse = (client) =>
Func<RestClient, Task<RestResponse<T>>> getResponse = async (client) =>
{
Func<Task<RestResponse<T>>> action = async () =>
if (RetryConfiguration.AsyncRetryPolicy != null)
{
if (RetryConfiguration.AsyncRetryPolicy != null)
{
var policy = RetryConfiguration.AsyncRetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
}
else
{
return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
}
};
return action().Result;
var policy = RetryConfiguration.AsyncRetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
}
else
{
return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
}
};

return Task.FromResult<ApiResponse<T>>(ExecClient(getResponse, setOptions, request, options, configuration));
return ExecClientAsync(getResponse, setOptions, request, options, configuration);
}

#region IAsynchronousClient
Expand Down
Loading

0 comments on commit af94bde

Please sign in to comment.