diff --git a/Src/Newtonsoft.Json/JsonTextReader.Async.cs b/Src/Newtonsoft.Json/JsonTextReader.Async.cs index 51370fd6..e2c67e9e 100644 --- a/Src/Newtonsoft.Json/JsonTextReader.Async.cs +++ b/Src/Newtonsoft.Json/JsonTextReader.Async.cs @@ -78,7 +78,7 @@ namespace Newtonsoft.Json return ParseObjectAsync(cancellationToken); case State.PostValue: Task task = ParsePostValueAsync(false, cancellationToken); - if (task.Status == TaskStatus.RanToCompletion) + if (task.IsCompletedSucessfully()) { if (task.Result) { @@ -531,7 +531,7 @@ namespace Newtonsoft.Json _charPos++; Task task = EnsureCharsAsync(1, append, cancellationToken); - if (task.Status == TaskStatus.RanToCompletion) + if (task.IsCompletedSucessfully()) { SetNewLine(task.Result); return AsyncUtils.CompletedTask; diff --git a/Src/Newtonsoft.Json/JsonTextWriter.Async.cs b/Src/Newtonsoft.Json/JsonTextWriter.Async.cs index b7414f46..41ea7980 100644 --- a/Src/Newtonsoft.Json/JsonTextWriter.Async.cs +++ b/Src/Newtonsoft.Json/JsonTextWriter.Async.cs @@ -186,7 +186,7 @@ namespace Newtonsoft.Json private Task WriteValueInternalAsync(JsonToken token, string value, CancellationToken cancellationToken) { Task task = InternalWriteValueAsync(token, cancellationToken); - if (task.Status == TaskStatus.RanToCompletion) + if (task.IsCompletedSucessfully()) { return _writer.WriteAsync(value, cancellationToken); } @@ -266,7 +266,7 @@ namespace Newtonsoft.Json private Task WriteIntegerValueAsync(ulong uvalue, bool negative, CancellationToken cancellationToken) { Task task = InternalWriteValueAsync(JsonToken.Integer, cancellationToken); - if (task.Status == TaskStatus.RanToCompletion) + if (task.IsCompletedSucessfully()) { return WriteDigitsAsync(uvalue, negative, cancellationToken); } @@ -317,13 +317,13 @@ namespace Newtonsoft.Json internal Task DoWritePropertyNameAsync(string name, CancellationToken cancellationToken) { Task task = InternalWritePropertyNameAsync(name, cancellationToken); - if (task.Status != TaskStatus.RanToCompletion) + if (!task.IsCompletedSucessfully()) { return DoWritePropertyNameAsync(task, name, cancellationToken); } task = WriteEscapedStringAsync(name, _quoteName, cancellationToken); - if (task.Status == TaskStatus.RanToCompletion) + if (task.IsCompletedSucessfully()) { return _writer.WriteAsync(':', cancellationToken); } @@ -395,7 +395,7 @@ namespace Newtonsoft.Json internal Task DoWriteStartArrayAsync(CancellationToken cancellationToken) { Task task = InternalWriteStartAsync(JsonToken.StartArray, JsonContainerType.Array, cancellationToken); - if (task.Status == TaskStatus.RanToCompletion) + if (task.IsCompletedSucessfully()) { return _writer.WriteAsync('[', cancellationToken); } @@ -425,7 +425,7 @@ namespace Newtonsoft.Json internal Task DoWriteStartObjectAsync(CancellationToken cancellationToken) { Task task = InternalWriteStartAsync(JsonToken.StartObject, JsonContainerType.Object, cancellationToken); - if (task.Status == TaskStatus.RanToCompletion) + if (task.IsCompletedSucessfully()) { return _writer.WriteAsync('{', cancellationToken); } @@ -477,7 +477,7 @@ namespace Newtonsoft.Json internal Task DoWriteUndefinedAsync(CancellationToken cancellationToken) { Task task = InternalWriteValueAsync(JsonToken.Undefined, cancellationToken); - if (task.Status == TaskStatus.RanToCompletion) + if (task.IsCompletedSucessfully()) { return _writer.WriteAsync(JsonConvert.Undefined, cancellationToken); } @@ -1054,7 +1054,7 @@ namespace Newtonsoft.Json internal Task DoWriteValueAsync(string value, CancellationToken cancellationToken) { Task task = InternalWriteValueAsync(JsonToken.String, cancellationToken); - if (task.Status == TaskStatus.RanToCompletion) + if (task.IsCompletedSucessfully()) { return value == null ? _writer.WriteAsync(JsonConvert.Null, cancellationToken) : WriteEscapedStringAsync(value, true, cancellationToken); } @@ -1189,7 +1189,7 @@ namespace Newtonsoft.Json internal Task WriteValueNotNullAsync(Uri value, CancellationToken cancellationToken) { Task task = InternalWriteValueAsync(JsonToken.String, cancellationToken); - if (task.Status == TaskStatus.RanToCompletion) + if (task.IsCompletedSucessfully()) { return WriteEscapedStringAsync(value.OriginalString, true, cancellationToken); } @@ -1310,7 +1310,7 @@ namespace Newtonsoft.Json { UpdateScopeWithFinishedValue(); Task task = AutoCompleteAsync(JsonToken.Undefined, cancellationToken); - if (task.Status == TaskStatus.RanToCompletion) + if (task.IsCompletedSucessfully()) { return WriteRawAsync(json, cancellationToken); } diff --git a/Src/Newtonsoft.Json/JsonWriter.Async.cs b/Src/Newtonsoft.Json/JsonWriter.Async.cs index 89d2ad4b..2f93e68a 100644 --- a/Src/Newtonsoft.Json/JsonWriter.Async.cs +++ b/Src/Newtonsoft.Json/JsonWriter.Async.cs @@ -275,32 +275,120 @@ namespace Newtonsoft.Json } } - internal async Task InternalWriteEndAsync(JsonContainerType type, CancellationToken cancellationToken) + internal Task InternalWriteEndAsync(JsonContainerType type, CancellationToken cancellationToken) { - cancellationToken.ThrowIfCancellationRequested(); - int levelsToComplete = CalculateLevelsToComplete(type); + if (cancellationToken.IsCancellationRequested) + { + return cancellationToken.FromCanceled(); + } + int levelsToComplete = CalculateLevelsToComplete(type); + Task t; while (levelsToComplete-- > 0) { JsonToken token = GetCloseTokenForType(Pop()); if (_currentState == State.Property) { - await WriteNullAsync(cancellationToken).ConfigureAwait(false); + t = WriteNullAsync(cancellationToken); + if (!t.IsCompletedSucessfully()) + { + return AwaitProperty(t, levelsToComplete, token, type, cancellationToken); + } } if (_formatting == Formatting.Indented) { if (_currentState != State.ObjectStart && _currentState != State.ArrayStart) { - await WriteIndentAsync(cancellationToken).ConfigureAwait(false); + t = WriteIndentAsync(cancellationToken); + if (!t.IsCompletedSucessfully()) + { + return AwaitIndent(t, levelsToComplete, token, type, cancellationToken); + } } } - await WriteEndAsync(token, cancellationToken).ConfigureAwait(false); + t = WriteEndAsync(token, cancellationToken); + if (!t.IsCompletedSucessfully()) + { + return AwaitEnd(t, levelsToComplete, type, cancellationToken); + } UpdateCurrentState(); } + + return AsyncUtils.CompletedTask; + + // Local functions, params renamed (capitalized) so as not to capture and allocate when calling async + async Task AwaitProperty(Task task, int LevelsToComplete, JsonToken token, JsonContainerType Type, CancellationToken CancellationToken) + { + await task; + + // Finish current loop + if (_formatting == Formatting.Indented) + { + if (_currentState != State.ObjectStart && _currentState != State.ArrayStart) + { + await WriteIndentAsync(CancellationToken).ConfigureAwait(false); + } + } + + await WriteEndAsync(token, CancellationToken).ConfigureAwait(false); + + UpdateCurrentState(); + + await AwaitRemaining(LevelsToComplete, Type, CancellationToken).ConfigureAwait(false); + } + + async Task AwaitIndent(Task task, int LevelsToComplete, JsonToken token, JsonContainerType Type, CancellationToken CancellationToken) + { + await task; + + // Finish current loop + + await WriteEndAsync(token, CancellationToken).ConfigureAwait(false); + + UpdateCurrentState(); + + await AwaitRemaining(LevelsToComplete, Type, CancellationToken).ConfigureAwait(false); + } + + async Task AwaitEnd(Task task, int LevelsToComplete, JsonContainerType Type, CancellationToken CancellationToken) + { + await task; + + // Finish current loop + + UpdateCurrentState(); + + await AwaitRemaining(LevelsToComplete, Type, CancellationToken).ConfigureAwait(false); + } + + async Task AwaitRemaining(int LevelsToComplete, JsonContainerType Type, CancellationToken CancellationToken) + { + while (LevelsToComplete-- > 0) + { + JsonToken token = GetCloseTokenForType(Pop()); + + if (_currentState == State.Property) + { + await WriteNullAsync(CancellationToken).ConfigureAwait(false); + } + + if (_formatting == Formatting.Indented) + { + if (_currentState != State.ObjectStart && _currentState != State.ArrayStart) + { + await WriteIndentAsync(CancellationToken).ConfigureAwait(false); + } + } + + await WriteEndAsync(token, CancellationToken).ConfigureAwait(false); + + UpdateCurrentState(); + } + } } /// diff --git a/Src/Newtonsoft.Json/Linq/JObject.Async.cs b/Src/Newtonsoft.Json/Linq/JObject.Async.cs index 170b3c54..39f6493a 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.Async.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.Async.cs @@ -25,6 +25,7 @@ #if HAVE_ASYNC +using System; using System.Globalization; using System.Threading; using System.Threading.Tasks; @@ -41,16 +42,54 @@ namespace Newtonsoft.Json.Linq /// The token to monitor for cancellation requests. /// A collection of which will be used when writing the token. /// A that represents the asynchronous write operation. - public override async Task WriteToAsync(JsonWriter writer, CancellationToken cancellationToken, params JsonConverter[] converters) + public override Task WriteToAsync(JsonWriter writer, CancellationToken cancellationToken, params JsonConverter[] converters) { - await writer.WriteStartObjectAsync(cancellationToken).ConfigureAwait(false); + var t = writer.WriteStartObjectAsync(cancellationToken); + if (!t.IsCompletedSucessfully()) + { + return AwaitStartObject(t, writer, cancellationToken, converters); + } for (int i = 0; i < _properties.Count; i++) { - await _properties[i].WriteToAsync(writer, cancellationToken, converters).ConfigureAwait(false); + t = _properties[i].WriteToAsync(writer, cancellationToken, converters); + if (!t.IsCompletedSucessfully()) + { + return AwaitProperties(t, i + 1, writer, cancellationToken, converters); + } } - await writer.WriteEndObjectAsync(cancellationToken).ConfigureAwait(false); + t = writer.WriteEndObjectAsync(cancellationToken); + + return (t.IsCompletedSucessfully()) ? AsyncUtils.CompletedTask : AwaitEndObject(t); + + // Local functions, params renamed (capitalized) so as not to capture and allocate when calling async + async Task AwaitStartObject(Task task, JsonWriter Writer, CancellationToken CancellationToken, JsonConverter[] Converters) + { + await task.ConfigureAwait(false); + for (int i = 0; i < _properties.Count; i++) + { + await _properties[i].WriteToAsync(Writer, CancellationToken, Converters).ConfigureAwait(false); + } + + await Writer.WriteEndObjectAsync(CancellationToken).ConfigureAwait(false); + } + + async Task AwaitProperties(Task task, int i, JsonWriter Writer, CancellationToken CancellationToken, JsonConverter[] Converters) + { + await task.ConfigureAwait(false); + for (; i < _properties.Count; i++) + { + await _properties[i].WriteToAsync(Writer, CancellationToken, Converters).ConfigureAwait(false); + } + + await Writer.WriteEndObjectAsync(CancellationToken).ConfigureAwait(false); + } + + async Task AwaitEndObject(Task task) + { + await task.ConfigureAwait(false); + } } /// diff --git a/Src/Newtonsoft.Json/Linq/JProperty.Async.cs b/Src/Newtonsoft.Json/Linq/JProperty.Async.cs index 083e228d..bd5cfb7d 100644 --- a/Src/Newtonsoft.Json/Linq/JProperty.Async.cs +++ b/Src/Newtonsoft.Json/Linq/JProperty.Async.cs @@ -44,7 +44,7 @@ namespace Newtonsoft.Json.Linq public override Task WriteToAsync(JsonWriter writer, CancellationToken cancellationToken, params JsonConverter[] converters) { Task task = writer.WritePropertyNameAsync(_name, cancellationToken); - if (task.Status == TaskStatus.RanToCompletion) + if (task.IsCompletedSucessfully()) { return WriteValueAsync(writer, cancellationToken, converters); } diff --git a/Src/Newtonsoft.Json/Utilities/AsyncUtils.cs b/Src/Newtonsoft.Json/Utilities/AsyncUtils.cs index c5d2be0b..c0a7a90e 100644 --- a/Src/Newtonsoft.Json/Utilities/AsyncUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/AsyncUtils.cs @@ -92,6 +92,16 @@ namespace Newtonsoft.Json.Utilities Debug.Assert(reader != null); return cancellationToken.IsCancellationRequested ? FromCanceled(cancellationToken) : reader.ReadAsync(buffer, index, count); } + + public static bool IsCompletedSucessfully(this Task task) + { + // IsCompletedSucessfully is the faster method, but only currently exposed on .NET Core 2.0 +#if NETCOREAPP2_0 + return task.IsCompletedSucessfully; +#else + return task.Status == TaskStatus.RanToCompletion; +#endif + } } } diff --git a/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs b/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs index 66a81a86..890d5810 100644 --- a/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs @@ -383,7 +383,7 @@ namespace Newtonsoft.Json.Utilities bool[] charEscapeFlags, StringEscapeHandling stringEscapeHandling, JsonTextWriter client, char[] writeBuffer, CancellationToken cancellationToken) { Task task = writer.WriteAsync(delimiter, cancellationToken); - if (task.Status != TaskStatus.RanToCompletion) + if (!task.IsCompletedSucessfully()) { return WriteEscapedJavaScriptStringWithDelimitersAsync(task, writer, s, delimiter, charEscapeFlags, stringEscapeHandling, client, writeBuffer, cancellationToken); } @@ -391,7 +391,7 @@ namespace Newtonsoft.Json.Utilities if (!string.IsNullOrEmpty(s)) { task = WriteEscapedJavaScriptStringWithoutDelimitersAsync(writer, s, charEscapeFlags, stringEscapeHandling, client, writeBuffer, cancellationToken); - if (task.Status == TaskStatus.RanToCompletion) + if (task.IsCompletedSucessfully()) { return writer.WriteAsync(delimiter, cancellationToken); }