Skip to content

Commit 76286ae

Browse files
committed
Convert the methods to async to avoid deadlocks
1 parent ecdf133 commit 76286ae

File tree

7 files changed

+112
-215
lines changed

7 files changed

+112
-215
lines changed

src/Microsoft.OpenApi.Hidi/OpenApiService.cs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public static async Task TransformOpenApiDocumentAsync(HidiOptions options, ILog
9797
}
9898

9999
// Load OpenAPI document
100-
var format = OpenApiModelFactory.GetFormat(options.OpenApi);
100+
var format = await OpenApiModelFactory.GetFormatAsync(options.OpenApi).ConfigureAwait(false);
101101
var document = await GetOpenApiAsync(options, format, logger, options.MetadataVersion, cancellationToken).ConfigureAwait(false);
102102

103103
if (options.FilterOptions != null)
@@ -396,8 +396,7 @@ private static async Task<ReadResult> ParseOpenApiAsync(string openApiFile, bool
396396
new Uri("file://" + new FileInfo(openApiFile).DirectoryName + Path.DirectorySeparatorChar)
397397
};
398398

399-
var format = OpenApiModelFactory.GetFormat(openApiFile);
400-
result = await OpenApiDocument.LoadAsync(stream, format, settings, cancellationToken).ConfigureAwait(false);
399+
result = await OpenApiDocument.LoadAsync(stream, settings, cancellationToken).ConfigureAwait(false);
401400

402401
logger.LogTrace("{Timestamp}ms: Completed parsing.", stopwatch.ElapsedMilliseconds);
403402

@@ -421,7 +420,7 @@ public static async Task<OpenApiDocument> ConvertCsdlToOpenApiAsync(Stream csdl,
421420
settings ??= SettingsUtilities.GetConfiguration();
422421

423422
var document = edmModel.ConvertToOpenApi(SettingsUtilities.GetOpenApiConvertSettings(settings, metadataVersion));
424-
document = FixReferences(document, format);
423+
document = await FixReferencesAsync(document, format).ConfigureAwait(false);
425424

426425
return document;
427426
}
@@ -431,17 +430,17 @@ public static async Task<OpenApiDocument> ConvertCsdlToOpenApiAsync(Stream csdl,
431430
/// </summary>
432431
/// <param name="document"> The converted OpenApiDocument.</param>
433432
/// <returns> A valid OpenApiDocument instance.</returns>
434-
public static OpenApiDocument FixReferences(OpenApiDocument document, string format)
433+
public static async Task<OpenApiDocument> FixReferencesAsync(OpenApiDocument document, string format)
435434
{
436435
// This method is only needed because the output of ConvertToOpenApi isn't quite a valid OpenApiDocument instance.
437436
// So we write it out, and read it back in again to fix it up.
438437

439438
var sb = new StringBuilder();
440439
document.SerializeAsV3(new OpenApiYamlWriter(new StringWriter(sb)));
441440

442-
var doc = OpenApiDocument.Parse(sb.ToString(), format).OpenApiDocument;
441+
var result = await OpenApiDocument.ParseAsync(sb.ToString()).ConfigureAwait(false);
443442

444-
return doc;
443+
return result.OpenApiDocument;
445444
}
446445

447446
/// <summary>
@@ -587,7 +586,7 @@ private static string GetInputPathExtension(string? openapi = null, string? csdl
587586
throw new ArgumentException("Please input a file path or URL");
588587
}
589588

590-
var format = OpenApiModelFactory.GetFormat(options.OpenApi);
589+
var format = await OpenApiModelFactory.GetFormatAsync(options.OpenApi).ConfigureAwait(false);
591590
var document = await GetOpenApiAsync(options, format, logger, null, cancellationToken).ConfigureAwait(false);
592591

593592
using (logger.BeginScope("Creating diagram"))
@@ -749,10 +748,10 @@ internal static async Task PluginManifestAsync(HidiOptions options, ILogger logg
749748
}
750749

751750
// Load OpenAPI document
752-
var format = OpenApiModelFactory.GetFormat(options.OpenApi);
751+
var format = await OpenApiModelFactory.GetFormatAsync(options.OpenApi).ConfigureAwait(false);
753752
var document = await GetOpenApiAsync(options, format, logger, options.MetadataVersion, cancellationToken).ConfigureAwait(false);
754753

755-
cancellationToken.ThrowIfCancellationRequested();
754+
cancellationToken.ThrowIfCancellationRequested();
756755

757756
if (options.FilterOptions != null)
758757
{

src/Microsoft.OpenApi.Readers/OpenApiYamlReader.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,9 @@ public async Task<ReadResult> ReadAsync(TextReader input,
4646
}
4747

4848
/// <inheritdoc/>
49-
public T ReadFragment<T>(TextReader input,
50-
OpenApiSpecVersion version,
51-
out OpenApiDiagnostic diagnostic,
52-
OpenApiReaderSettings settings = null) where T : IOpenApiElement
49+
public async Task<ReadFragmentResult> ReadFragmentAsync<T>(TextReader input,
50+
OpenApiSpecVersion version,
51+
OpenApiReaderSettings settings = null) where T : IOpenApiElement
5352
{
5453
JsonNode jsonNode;
5554

@@ -60,12 +59,12 @@ public T ReadFragment<T>(TextReader input,
6059
}
6160
catch (JsonException ex)
6261
{
63-
diagnostic = new();
62+
var diagnostic = new OpenApiDiagnostic();
6463
diagnostic.Errors.Add(new($"#line={ex.LineNumber}", ex.Message));
6564
return default;
6665
}
6766

68-
return ReadFragment<T>(jsonNode, version, out diagnostic);
67+
return await ReadFragmentAsync<T>(jsonNode, version, settings);
6968
}
7069

7170
/// <summary>
@@ -88,9 +87,9 @@ public async Task<ReadResult> ReadAsync(JsonNode jsonNode, OpenApiReaderSettings
8887
}
8988

9089
/// <inheritdoc/>
91-
public T ReadFragment<T>(JsonNode input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null) where T : IOpenApiElement
90+
public async Task<ReadFragmentResult> ReadFragmentAsync<T>(JsonNode input, OpenApiSpecVersion version, OpenApiReaderSettings settings = null) where T : IOpenApiElement
9291
{
93-
return OpenApiReaderRegistry.DefaultReader.ReadFragment<T>(input, version, out diagnostic);
92+
return await OpenApiReaderRegistry.DefaultReader.ReadFragmentAsync<T>(input, version, settings);
9493
}
9594
}
9695
}

src/Microsoft.OpenApi/Interfaces/IOpenApiReader.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,17 @@ public interface IOpenApiReader
3838
/// </summary>
3939
/// <param name="input">TextReader containing OpenAPI description to parse.</param>
4040
/// <param name="version">Version of the OpenAPI specification that the fragment conforms to.</param>
41-
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing.</param>
4241
/// <param name="settings">The OpenApiReader settings.</param>
4342
/// <returns>Instance of newly created IOpenApiElement.</returns>
44-
T ReadFragment<T>(TextReader input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null) where T : IOpenApiElement;
43+
Task<ReadFragmentResult> ReadFragmentAsync<T>(TextReader input, OpenApiSpecVersion version, OpenApiReaderSettings settings = null) where T: IOpenApiElement;
4544

4645
/// <summary>
4746
/// Reads the JsonNode input and parses the fragment of an OpenAPI description into an Open API Element.
4847
/// </summary>
4948
/// <param name="input">TextReader containing OpenAPI description to parse.</param>
5049
/// <param name="version">Version of the OpenAPI specification that the fragment conforms to.</param>
51-
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing.</param>
5250
/// <param name="settings">The OpenApiReader settings.</param>
5351
/// <returns>Instance of newly created IOpenApiElement.</returns>
54-
T ReadFragment<T>(JsonNode input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null) where T : IOpenApiElement;
52+
Task<ReadFragmentResult> ReadFragmentAsync<T>(JsonNode input, OpenApiSpecVersion version, OpenApiReaderSettings settings = null) where T: IOpenApiElement;
5553
}
5654
}

src/Microsoft.OpenApi/Models/OpenApiDocument.cs

Lines changed: 7 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -535,45 +535,6 @@ private static string ConvertByteArrayToString(byte[] hash)
535535
return Workspace?.ResolveReference<IOpenApiReferenceable>(uriLocation);
536536
}
537537

538-
/// <summary>
539-
/// Parses a local file path or Url into an Open API document.
540-
/// </summary>
541-
/// <param name="url"> The path to the OpenAPI file.</param>
542-
/// <param name="settings"></param>
543-
/// <returns></returns>
544-
public static ReadResult Load(string url, OpenApiReaderSettings? settings = null)
545-
{
546-
return OpenApiModelFactory.Load(url, settings);
547-
}
548-
549-
/// <summary>
550-
/// Reads the stream input and parses it into an Open API document.
551-
/// </summary>
552-
/// <param name="stream">Stream containing OpenAPI description to parse.</param>
553-
/// <param name="format">The OpenAPI format to use during parsing.</param>
554-
/// <param name="settings">The OpenApi reader settings.</param>
555-
/// <returns></returns>
556-
public static ReadResult Load(Stream stream,
557-
string format,
558-
OpenApiReaderSettings? settings = null)
559-
{
560-
return OpenApiModelFactory.Load(stream, format, settings);
561-
}
562-
563-
/// <summary>
564-
/// Reads the text reader content and parses it into an Open API document.
565-
/// </summary>
566-
/// <param name="input">TextReader containing OpenAPI description to parse.</param>
567-
/// <param name="format"> The OpenAPI format to use during parsing.</param>
568-
/// <param name="settings">The OpenApi reader settings.</param>
569-
/// <returns></returns>
570-
public static ReadResult Load(TextReader input,
571-
string format,
572-
OpenApiReaderSettings? settings = null)
573-
{
574-
return OpenApiModelFactory.Load(input, format, settings);
575-
}
576-
577538
/// <summary>
578539
/// Parses a local file path or Url into an Open API document.
579540
/// </summary>
@@ -589,39 +550,35 @@ public static async Task<ReadResult> LoadAsync(string url, OpenApiReaderSettings
589550
/// Reads the stream input and parses it into an Open API document.
590551
/// </summary>
591552
/// <param name="stream">Stream containing OpenAPI description to parse.</param>
592-
/// <param name="format">The OpenAPI format to use during parsing.</param>
593553
/// <param name="settings">The OpenApi reader settings.</param>
594554
/// <param name="cancellationToken">Propagates information about operation cancelling.</param>
595555
/// <returns></returns>
596-
public static async Task<ReadResult> LoadAsync(Stream stream, string format, OpenApiReaderSettings? settings = null, CancellationToken cancellationToken = default)
556+
public static async Task<ReadResult> LoadAsync(Stream stream, OpenApiReaderSettings? settings = null, CancellationToken cancellationToken = default)
597557
{
598-
return await OpenApiModelFactory.LoadAsync(stream, format, settings, cancellationToken);
558+
return await OpenApiModelFactory.LoadAsync(stream, settings: settings, cancellationToken: cancellationToken);
599559
}
600560

601561
/// <summary>
602562
/// Reads the text reader content and parses it into an Open API document.
603563
/// </summary>
604564
/// <param name="input">TextReader containing OpenAPI description to parse.</param>
605-
/// <param name="format"> The OpenAPI format to use during parsing.</param>
606565
/// <param name="settings">The OpenApi reader settings.</param>
607566
/// <returns></returns>
608-
public static async Task<ReadResult> LoadAsync(TextReader input, string format, OpenApiReaderSettings? settings = null)
567+
public static async Task<ReadResult> LoadAsync(TextReader input, OpenApiReaderSettings? settings = null)
609568
{
610-
return await OpenApiModelFactory.LoadAsync(input, format, settings);
569+
return await OpenApiModelFactory.LoadAsync(input, settings: settings);
611570
}
612571

613572
/// <summary>
614573
/// Parses a string into a <see cref="OpenApiDocument"/> object.
615574
/// </summary>
616575
/// <param name="input"> The string input.</param>
617-
/// <param name="format"></param>
618576
/// <param name="settings"></param>
619577
/// <returns></returns>
620-
public static ReadResult Parse(string input,
621-
string? format = null,
622-
OpenApiReaderSettings? settings = null)
578+
public static async Task<ReadResult> ParseAsync(string input,
579+
OpenApiReaderSettings? settings = null)
623580
{
624-
return OpenApiModelFactory.Parse(input, format, settings);
581+
return await OpenApiModelFactory.ParseAsync(input, settings);
625582
}
626583
}
627584

src/Microsoft.OpenApi/Reader/OpenApiJsonReader.cs

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
using Microsoft.OpenApi.Services;
1515
using Microsoft.OpenApi.Interfaces;
1616
using Microsoft.OpenApi.Reader.Services;
17-
using System.Collections.Generic;
1817
using System;
1918

2019
namespace Microsoft.OpenApi.Reader
@@ -42,7 +41,7 @@ public async Task<ReadResult> ReadAsync(TextReader input,
4241
// Parse the JSON text in the TextReader into JsonNodes
4342
try
4443
{
45-
jsonNode = LoadJsonNodes(input);
44+
jsonNode = await LoadJsonNodesAsync(input);
4645
}
4746
catch (JsonException ex)
4847
{
@@ -124,35 +123,33 @@ public async Task<ReadResult> ReadAsync(JsonNode jsonNode,
124123
}
125124

126125
/// <inheritdoc/>
127-
public T ReadFragment<T>(TextReader input,
128-
OpenApiSpecVersion version,
129-
out OpenApiDiagnostic diagnostic,
130-
OpenApiReaderSettings settings = null) where T : IOpenApiElement
126+
public async Task<ReadFragmentResult> ReadFragmentAsync<T>(TextReader input,
127+
OpenApiSpecVersion version,
128+
OpenApiReaderSettings settings = null) where T: IOpenApiElement
131129
{
132130
JsonNode jsonNode;
133131

134132
// Parse the JSON
135133
try
136134
{
137-
jsonNode = LoadJsonNodes(input);
135+
jsonNode = await LoadJsonNodesAsync(input);
138136
}
139137
catch (JsonException ex)
140138
{
141-
diagnostic = new();
139+
var diagnostic = new OpenApiDiagnostic();
142140
diagnostic.Errors.Add(new($"#line={ex.LineNumber}", ex.Message));
143141
return default;
144142
}
145143

146-
return ReadFragment<T>(jsonNode, version, out diagnostic);
144+
return await ReadFragmentAsync<T>(jsonNode, version, settings);
147145
}
148146

149147
/// <inheritdoc/>
150-
public T ReadFragment<T>(JsonNode input,
151-
OpenApiSpecVersion version,
152-
out OpenApiDiagnostic diagnostic,
153-
OpenApiReaderSettings settings = null) where T : IOpenApiElement
148+
public async Task<ReadFragmentResult> ReadFragmentAsync<T>(JsonNode input,
149+
OpenApiSpecVersion version,
150+
OpenApiReaderSettings settings = null) where T : IOpenApiElement
154151
{
155-
diagnostic = new();
152+
var diagnostic = new OpenApiDiagnostic();
156153
settings ??= new OpenApiReaderSettings();
157154
var context = new ParsingContext(diagnostic)
158155
{
@@ -162,8 +159,8 @@ public T ReadFragment<T>(JsonNode input,
162159
IOpenApiElement element = null;
163160
try
164161
{
165-
// Parse the OpenAPI element
166-
element = context.ParseFragment<T>(input, version);
162+
// Parse the OpenAPI element asynchronously
163+
element = await Task.Run(() => context.ParseFragment<T>(input, version));
167164
}
168165
catch (OpenApiException ex)
169166
{
@@ -180,13 +177,17 @@ public T ReadFragment<T>(JsonNode input,
180177
}
181178
}
182179

183-
return (T)element;
180+
return new ReadFragmentResult
181+
{
182+
Element = element,
183+
OpenApiDiagnostic = diagnostic
184+
};
184185
}
185186

186-
private JsonNode LoadJsonNodes(TextReader input)
187+
private async Task<JsonNode> LoadJsonNodesAsync(TextReader input)
187188
{
188-
var nodes = JsonNode.Parse(input.ReadToEnd());
189-
return nodes;
189+
var content = await input.ReadToEndAsync();
190+
return JsonNode.Parse(content);
190191
}
191192

192193
private async Task<OpenApiDiagnostic> LoadExternalRefsAsync(OpenApiDocument document, CancellationToken cancellationToken, OpenApiReaderSettings settings, string format = null)

0 commit comments

Comments
 (0)