Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit eb71264

Browse files
committed
Merge pull request #2153 from Maxwe11/system.net.http-closure
Eliminate closure allocations in System.Net.Http
2 parents b219b8d + 155dfa7 commit eb71264

File tree

2 files changed

+50
-29
lines changed

2 files changed

+50
-29
lines changed

src/System.Net.Http/src/System/Net/Http/HttpContent.cs

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,20 @@ public Task<string> ReadAsStringAsync()
6767
{
6868
CheckDisposed();
6969

70-
TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
70+
var tcs = new TaskCompletionSource<string>(this);
7171

72-
LoadIntoBufferAsync().ContinueWithStandard(task =>
72+
LoadIntoBufferAsync().ContinueWithStandard(tcs, (task, state) =>
7373
{
74-
if (HttpUtilities.HandleFaultsAndCancelation(task, tcs))
74+
var innerTcs = (TaskCompletionSource<string>)state;
75+
var innerThis = (HttpContent)innerTcs.Task.AsyncState;
76+
if (HttpUtilities.HandleFaultsAndCancelation(task, innerTcs))
7577
{
7678
return;
7779
}
7880

79-
if (_bufferedContent.Length == 0)
81+
if (innerThis._bufferedContent.Length == 0)
8082
{
81-
tcs.TrySetResult(string.Empty);
83+
innerTcs.TrySetResult(string.Empty);
8284
return;
8385
}
8486

@@ -90,21 +92,21 @@ public Task<string> ReadAsStringAsync()
9092
Encoding encoding = null;
9193
int bomLength = -1;
9294

93-
byte[] data = this.GetDataBuffer(_bufferedContent);
95+
byte[] data = innerThis.GetDataBuffer(innerThis._bufferedContent);
9496

95-
int dataLength = (int)_bufferedContent.Length; // Data is the raw buffer, it may not be full.
97+
int dataLength = (int)innerThis._bufferedContent.Length; // Data is the raw buffer, it may not be full.
9698

9799
// If we do have encoding information in the 'Content-Type' header, use that information to convert
98100
// the content to a string.
99-
if ((Headers.ContentType != null) && (Headers.ContentType.CharSet != null))
101+
if ((innerThis.Headers.ContentType != null) && (innerThis.Headers.ContentType.CharSet != null))
100102
{
101103
try
102104
{
103-
encoding = Encoding.GetEncoding(Headers.ContentType.CharSet);
105+
encoding = Encoding.GetEncoding(innerThis.Headers.ContentType.CharSet);
104106
}
105107
catch (ArgumentException e)
106108
{
107-
tcs.TrySetException(new InvalidOperationException(SR.net_http_content_invalid_charset, e));
109+
innerTcs.TrySetException(new InvalidOperationException(SR.net_http_content_invalid_charset, e));
108110
return;
109111
}
110112
}
@@ -143,11 +145,11 @@ public Task<string> ReadAsStringAsync()
143145
{
144146
// Drop the BOM when decoding the data.
145147
string result = encoding.GetString(data, bomLength, dataLength - bomLength);
146-
tcs.TrySetResult(result);
148+
innerTcs.TrySetResult(result);
147149
}
148150
catch (Exception ex)
149151
{
150-
tcs.TrySetException(ex);
152+
innerTcs.TrySetException(ex);
151153
}
152154
});
153155

@@ -158,13 +160,15 @@ public Task<byte[]> ReadAsByteArrayAsync()
158160
{
159161
CheckDisposed();
160162

161-
TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>();
163+
var tcs = new TaskCompletionSource<byte[]>(this);
162164

163-
LoadIntoBufferAsync().ContinueWithStandard(task =>
165+
LoadIntoBufferAsync().ContinueWithStandard(tcs, (task, state) =>
164166
{
165-
if (!HttpUtilities.HandleFaultsAndCancelation(task, tcs))
167+
var innerTcs = (TaskCompletionSource<byte[]>)state;
168+
var innerThis = (HttpContent)innerTcs.Task.AsyncState;
169+
if (!HttpUtilities.HandleFaultsAndCancelation(task, innerTcs))
166170
{
167-
tcs.TrySetResult(_bufferedContent.ToArray());
171+
innerTcs.TrySetResult(innerThis._bufferedContent.ToArray());
168172
}
169173
});
170174

@@ -175,7 +179,7 @@ public Task<Stream> ReadAsStreamAsync()
175179
{
176180
CheckDisposed();
177181

178-
TaskCompletionSource<Stream> tcs = new TaskCompletionSource<Stream>();
182+
TaskCompletionSource<Stream> tcs = new TaskCompletionSource<Stream>(this);
179183

180184
if (_contentReadStream == null && IsBuffered)
181185
{
@@ -194,12 +198,14 @@ public Task<Stream> ReadAsStreamAsync()
194198
return tcs.Task;
195199
}
196200

197-
CreateContentReadStreamAsync().ContinueWithStandard(task =>
201+
CreateContentReadStreamAsync().ContinueWithStandard(tcs, (task, state) =>
198202
{
199-
if (!HttpUtilities.HandleFaultsAndCancelation(task, tcs))
203+
var innerTcs = (TaskCompletionSource<Stream>)state;
204+
var innerThis = (HttpContent)innerTcs.Task.AsyncState;
205+
if (!HttpUtilities.HandleFaultsAndCancelation(task, innerTcs))
200206
{
201-
_contentReadStream = task.Result;
202-
tcs.TrySetResult(_contentReadStream);
207+
innerThis._contentReadStream = task.Result;
208+
innerTcs.TrySetResult(innerThis._contentReadStream);
203209
}
204210
});
205211

@@ -232,19 +238,20 @@ public Task CopyToAsync(Stream stream, TransportContext context)
232238
}
233239

234240
// If the copy operation fails, wrap the exception in an HttpRequestException() if appropriate.
235-
task.ContinueWithStandard(copyTask =>
241+
task.ContinueWithStandard(tcs, (copyTask, state) =>
236242
{
243+
var innerTcs = (TaskCompletionSource<object>)state;
237244
if (copyTask.IsFaulted)
238245
{
239-
tcs.TrySetException(GetStreamCopyException(copyTask.Exception.GetBaseException()));
246+
innerTcs.TrySetException(GetStreamCopyException(copyTask.Exception.GetBaseException()));
240247
}
241248
else if (copyTask.IsCanceled)
242249
{
243-
tcs.TrySetCanceled();
250+
innerTcs.TrySetCanceled();
244251
}
245252
else
246253
{
247-
tcs.TrySetResult(null);
254+
innerTcs.TrySetResult(null);
248255
}
249256
});
250257
}
@@ -354,15 +361,17 @@ public Task LoadIntoBufferAsync(long maxBufferSize)
354361

355362
protected virtual Task<Stream> CreateContentReadStreamAsync()
356363
{
357-
TaskCompletionSource<Stream> tcs = new TaskCompletionSource<Stream>();
364+
var tcs = new TaskCompletionSource<Stream>(this);
358365
// By default just buffer the content to a memory stream. Derived classes can override this behavior
359366
// if there is a better way to retrieve the content as stream (e.g. byte array/string use a more efficient
360367
// way, like wrapping a read-only MemoryStream around the bytes/string)
361-
LoadIntoBufferAsync().ContinueWithStandard(task =>
368+
LoadIntoBufferAsync().ContinueWithStandard(tcs, (task, state) =>
362369
{
363-
if (!HttpUtilities.HandleFaultsAndCancelation(task, tcs))
370+
var innerTcs = (TaskCompletionSource<Stream>)state;
371+
var innerThis = (HttpContent)innerTcs.Task.AsyncState;
372+
if (!HttpUtilities.HandleFaultsAndCancelation(task, innerTcs))
364373
{
365-
tcs.TrySetResult(_bufferedContent);
374+
innerTcs.TrySetResult(innerThis._bufferedContent);
366375
}
367376
});
368377

src/System.Net.Http/src/System/Net/Http/HttpUtilities.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,22 @@ internal static Task ContinueWithStandard(this Task task, Action<Task> continuat
5858
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
5959
}
6060

61+
internal static Task ContinueWithStandard(this Task task, object state, Action<Task, object> continuation)
62+
{
63+
return task.ContinueWith(continuation, state, CancellationToken.None,
64+
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
65+
}
66+
6167
internal static Task ContinueWithStandard<T>(this Task<T> task, Action<Task<T>> continuation)
6268
{
6369
return task.ContinueWith(continuation, CancellationToken.None,
6470
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
6571
}
72+
73+
internal static Task ContinueWithStandard<T>(this Task<T> task, object state, Action<Task<T>, object> continuation)
74+
{
75+
return task.ContinueWith(continuation, state, CancellationToken.None,
76+
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
77+
}
6678
}
6779
}

0 commit comments

Comments
 (0)