-
-
Save SteveSandersonMS/ba34011dd4ee8bb3021c8837ff68ef28 to your computer and use it in GitHub Desktop.
@using StandaloneApp.Util | |
@(Layout<StandaloneApp.Shared.MainLayout>()) | |
<h1>Hello, world!</h1> | |
Welcome to your new app. | |
<button @onclick(DoRequest)>Do request</button> | |
<div><strong>Response: </strong>@responseText</div> | |
@functions { | |
string responseText; | |
private async void DoRequest() | |
{ | |
responseText = await new MyHttpClient().GetStringAsync("/index.html"); | |
StateHasChanged(); | |
} | |
} |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<title>Blazor standalone</title> | |
<link href="/css/bootstrap/bootstrap.min.css" rel="stylesheet" /> | |
<link href="/css/site.css" rel="stylesheet" /> | |
</head> | |
<body> | |
<app>Loading...</app> | |
<script src="/css/bootstrap/bootstrap-native.min.js"></script> | |
<script type="blazor-boot"></script> | |
<script src="myhttpclient.js"></script> | |
</body> | |
</html> |
using Microsoft.AspNetCore.Blazor.Browser.Interop; | |
using System.Collections.Generic; | |
using System.Net; | |
using System.Net.Http; | |
using System.Threading.Tasks; | |
namespace StandaloneApp.Util | |
{ | |
public class MyHttpClient | |
{ | |
static object _idLock = new object(); | |
static int _nextRequestId = 0; | |
static IDictionary<int, TaskCompletionSource<HttpResponseMessage>> _pendingRequests | |
= new Dictionary<int, TaskCompletionSource<HttpResponseMessage>>(); | |
public async Task<string> GetStringAsync(string requestUri) | |
{ | |
var response = await GetAsync(requestUri); | |
if (!response.IsSuccessStatusCode) | |
{ | |
throw new HttpRequestException($"The response status code was {response.StatusCode}"); | |
} | |
return await response.Content.ReadAsStringAsync(); | |
} | |
public Task<HttpResponseMessage> GetAsync(string requestUri) | |
{ | |
var tcs = new TaskCompletionSource<HttpResponseMessage>(); | |
int id; | |
lock (_idLock) | |
{ | |
id = _nextRequestId++; | |
_pendingRequests.Add(id, tcs); | |
} | |
RegisteredFunction.Invoke<object>("HttpClientSend", id, requestUri); | |
return tcs.Task; | |
} | |
private static void ReceiveResponse(string id, string statusCode, string responseText, string errorText) | |
{ | |
TaskCompletionSource<HttpResponseMessage> tcs; | |
var idVal = int.Parse(id); | |
lock (_idLock) | |
{ | |
tcs = _pendingRequests[idVal]; | |
_pendingRequests.Remove(idVal); | |
} | |
if (errorText == null) | |
{ | |
tcs.SetResult(new HttpResponseMessage | |
{ | |
StatusCode = (HttpStatusCode)int.Parse(statusCode), | |
Content = new StringContent(responseText) | |
}); | |
} | |
else | |
{ | |
tcs.SetException(new HttpRequestException(errorText)); | |
} | |
} | |
} | |
} |
(function () { | |
function dispatchResponse(id, statusCode, responseText, errorInfo) { | |
var method = Blazor.platform.findMethod('StandaloneApp', 'StandaloneApp.Util', 'MyHttpClient', 'ReceiveResponse'); | |
Blazor.platform.callMethod(method, null, [ | |
Blazor.platform.toDotNetString(id.toString()), | |
Blazor.platform.toDotNetString(statusCode.toString()), | |
responseText === null ? null : Blazor.platform.toDotNetString(responseText), | |
errorInfo === null ? null : Blazor.platform.toDotNetString(errorInfo.toString()) | |
]); | |
} | |
Blazor.registerFunction('HttpClientSend', function (id, requestUri) { | |
fetch(requestUri).then(function (response) { | |
return response.text().then(function (responseText) { | |
dispatchResponse(id, response.status, responseText, null); | |
}); | |
}).catch(function (errorInfo) { dispatchResponse(id, 0, null, errorInfo); }); | |
}); | |
})(); |
@SteveSandersonMS Thanks. Does that mean the runtime will be updated to ignore the locks for performance reasons?
I just ran a quick test and my machine takes about 50 million locks a second. In this code we are making network calls through scripting interop. The overhead of the lock is exceedingly unlikely to be even remotely relevant.
@mlidbom I agree in this instance the performance of the lock is not going to be significant but in the general case it would probably be good for the runtime to optimize locks away.
@TylerBrinkley
Sure. I would guess that such an optimization would be pretty far down the backlog though. We are talking about client code here. I would assume that other aspects needs optimization more. Assembly size is likely to be rather important. JIT support would likely utterly eclipse any locking optimization etc.
And, of course, unless there is no contention for resources between the tasks, I would focus on API surfaces for now. Not performance.
👍
@TylerBrinkley You're right that the locks aren't strictly necessary. I still usually prefer to put them in for types that would be used on multiple threads simultaneously if that was supported.