Skip to content

Commit

Permalink
Easy reproduction of bug
Browse files Browse the repository at this point in the history
  • Loading branch information
Tewr committed Sep 22, 2020
1 parent 0385bc7 commit 71d5065
Show file tree
Hide file tree
Showing 10 changed files with 247 additions and 9 deletions.
64 changes: 59 additions & 5 deletions src/Blazor.FileReader/FileReaderJsInterop.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Threading;
Expand Down Expand Up @@ -127,31 +129,83 @@ private async Task<string> ReadFileMarshalledBase64Async(

}

static bool isRetry = false;
private async Task<int> ReadFileUnmarshalledAsync(
int fileRef, byte[] buffer, long position, long bufferOffset, int count,
CancellationToken cancellationToken)
{
var taskCompletionSource = new TaskCompletionSource<int>();
var id = ++_readFileUnmarshalledCallIdSource;
_readFileUnmarshalledCalls[id] = taskCompletionSource;
var taskId = ++_readFileUnmarshalledCallIdSource;
_readFileUnmarshalledCalls[taskId] = taskCompletionSource;
cancellationToken.Register(() => taskCompletionSource.TrySetCanceled());

// Temporary buffer as whatever will be shared with js
// might be corrupted under gc pressure
var pool = ArrayPool<byte>.Shared;
var tempBuffer = pool.Rent(buffer.Length);
for (int i = 0; i < buffer.Length; i++)
{
tempBuffer[i] = 0;
}
tempBuffer[0] = (byte)(taskId%255);
((IJSUnmarshalledRuntime) CurrentJSRuntime)
.InvokeUnmarshalled<ReadFileParams, int>(
$"FileReaderComponent.ReadFileUnmarshalledAsync",
new ReadFileParams {
Buffer = buffer,
Buffer = tempBuffer,
BufferOffset = bufferOffset,
Count = count,
FileRef = fileRef,
Position = position,
TaskId = id
TaskId = taskId
});

var bytesRead = await taskCompletionSource.Task;

var tempBuffer2 = pool.Rent(buffer.Length);
var testCall = await ReadFileMarshalledAsync(fileRef, tempBuffer2, position, bufferOffset, count, cancellationToken);
var testEqual = ByteArrayCompare(tempBuffer, tempBuffer2);
if (!testEqual)
{
Console.Error.WriteLine($"******** Broken! Params was " +
$"{nameof(bufferOffset)} {bufferOffset} {nameof(count)} {count} {nameof(fileRef)} {fileRef} " +
$"{nameof(position)} {position} {nameof(taskId)} {taskId}");

Console.Error.WriteLine($"******** unmanaged {string.Join(" ", tempBuffer)}");
Console.Error.WriteLine($"******** managed {string.Join(" ", tempBuffer2)}");

// Can I try again and succeed??
if (!isRetry == true)
{
isRetry = true;
return await ReadFileUnmarshalledAsync(fileRef, buffer, position, bufferOffset, count, cancellationToken);
}
throw new InvalidOperationException("Call was broken");
}


Array.Copy(tempBuffer, bufferOffset, buffer, bufferOffset, bytesRead);
pool.Return(tempBuffer);
pool.Return(tempBuffer2);
if (isRetry)
{
throw new InvalidOperationException("Call was broken, but was correct after retrying!!");
}
return bytesRead;
}

static bool ByteArrayCompare(byte[] a1, byte[] a2)
{
if (a1.Length != a2.Length)
return false;

for (int i = 0; i < a1.Length; i++)
if (a1[i] != a2[i])
return false;

return true;
}

[JSInvokable(nameof(EndReadFileUnmarshalledAsyncResult))]
public static void EndReadFileUnmarshalledAsyncResult(long taskId, int bytesRead)
{
Expand Down
11 changes: 10 additions & 1 deletion src/Blazor.FileReader/script/FileReaderComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,17 @@ class FileReaderComponent {
try {
const dotNetBufferView = Blazor.platform.toUint8Array(readFileParams.buffer);
const arrayBuffer = r.result as ArrayBuffer;
const isZero = dotNetBufferView.every((v, i, a) => (i === 0 || a[i] === 0));
if (!isZero || dotNetBufferView[0] !== (readFileParams.taskId % 255)) {
console.debug("Buffer had unexpected content", readFileParams, dotNetBufferView);
}
if (readFileParams.taskId > 323) {
console.debug("Params output", readFileParams, dotNetBufferView);
}
dotNetBufferView.set(new Uint8Array(arrayBuffer), readFileParams.bufferOffset);

if (readFileParams.taskId > 323) {
console.debug("Params output after set call", readFileParams, dotNetBufferView);
}
const byteCount = Math.min(arrayBuffer.byteLength, readFileParams.count);
resolve(byteCount);
} catch (e) {
Expand Down
10 changes: 10 additions & 0 deletions src/Blazor.FileReader/wwwroot/FileReaderComponent.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/Blazor.FileReader/wwwroot/FileReaderComponent.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/Demo/Blazor.FileReader.Demo.Common/KnuthCommon.razor
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ This demo calculates a Knuth hash.
{
var kHash = await CalculateKnuthHashAsync(fs, BufferSize);
value = fs.Position;
Output += $"Read 100%). {fileInfo.Size} / {fileInfo.Size}{nl}";
Output += $"Read 100%. {fileInfo.Size} / {fileInfo.Size}{nl}";
Output += $"Knuth hash for {fileInfo.Name} : {kHash}";
stopwatch.Stop();
this.InvokeAsync(this.StateHasChanged);
Expand Down
28 changes: 27 additions & 1 deletion src/Demo/Blazor.FileReader.Wasm.Demo/Pages/Index.razor
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
@page "/"
@using System.Text
@using Blazor.FileReader.Wasm.Demo.Shared
<h1>Hello, world!</h1>

<IndexCommon BufferSize="81920"/>
Welcome to your new app.

<div class="container">
<div class="row mb-2">
<div class="col">
<DataInputFileForm @ref="_dataInputFileForm"
FileContent="_fileContent"
SiteID="_id"
OnUseLogs="SaveData" />

</div>
</div>
</div>

@code {
private int _id;
private string _status = "";
private DataInputFileForm _dataInputFileForm;

private void SaveData()
{
//...
}
}"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
@using Tewr.Blazor.FileReader;
@inject Tewr.Blazor.FileReader.IFileReaderService fileReaderService
<div class="container">
<div class="row mb-2">
<div class="col">

<div class="input-group">
<div class="custom-file">
<input type="file" class="custom-file-input" id="jsonInputFilename" @ref="InputElement" @onchange="FilesSelected" accept=".json" />
<label class="custom-file-label" for="jsonInputFilename">@Label</label>
</div>
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="button" @onclick="OnConvertData">Convert text</button>
<button class="btn btn-outline-secondary" type="button" @onclick="OnUseLogs">Use logs</button>
</div>
</div>
</div>
</div>
@if (LoadedData != null)
{
<div class="row mb-2">
<div class="col">
<div class="text-info">Number of logs loaded: @LoadedData.Count</div>
</div>
</div>
}
<div class="row">
<div class="col">
<div class="alert alert-info">
<textarea class="form-control" readonly>@_status</textarea>
</div>
</div>
</div>
</div>


Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.Json;

namespace Blazor.FileReader.Wasm.Demo.Shared
{
public partial class DataInputFileForm
{

[Parameter] public string Label { get; set; } = "Select file (-s)";
[Parameter] public string FileContent { get; set; } = "";
[Parameter] public EventCallback OnUseLogs { get; set; }
[Parameter] public List<SiteLog> LoadedData { get; set; } = new List<SiteLog>();
[Parameter] public int SiteID { get; set; }
ElementReference InputElement;
private string _status;

async Task FilesSelected()
{
LoadedData = new List<SiteLog>();
_status = "Loading text from file (-s).. " + Environment.NewLine;
foreach (var file in await fileReaderService.CreateReference(InputElement).EnumerateFilesAsync())
{
var fileInfo = await file.ReadFileInfoAsync();

var filename = fileInfo.Name;
_status += $"Loading {filename}, ";
this.InvokeAsync(this.StateHasChanged);
using (Stream stream = await file.OpenReadAsync())
{
this.InvokeAsync(this.StateHasChanged);
FileContent = await ReadAllText(stream, Encoding.UTF8);
}
_status += $"loaded text from file with length {FileContent.Length}, ";
this.InvokeAsync(this.StateHasChanged);
}
}

public async Task<string> ReadAllText(Stream stream, Encoding encoding)
{

using (var reader = new StreamReader(stream, encoding))
{
var result = await reader.ReadToEndAsync();

return result;
}
}

private async Task OnConvertData()
{
if (!string.IsNullOrEmpty(FileContent))
{
_status += $"{Environment.NewLine}Parsing text to logs...";
try
{
LoadedData.AddRange(await ConvertJsonToSiteLogs(FileContent));
_status += $"{Environment.NewLine}Parsed data into {LoadedData.Count} site logs. ";
}
catch (Exception ex)
{
_status += $"{Environment.NewLine}Unable to convert file content:";
_status += ex.Message;
}
}
else
{
_status += "There is no text loaded.";
}
this.InvokeAsync(this.StateHasChanged);
}


public async Task<List<SiteLog>> ConvertJsonToSiteLogs(string json)
{
return JsonSerializer.Deserialize<SiteLog[]>(json).ToList();
}
}
}
6 changes: 6 additions & 0 deletions src/Demo/Blazor.FileReader.Wasm.Demo/Shared/Site.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Blazor.FileReader.Wasm.Demo.Shared
{
public class Site
{
}
}
13 changes: 13 additions & 0 deletions src/Demo/Blazor.FileReader.Wasm.Demo/Shared/SiteLog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;

namespace Blazor.FileReader.Wasm.Demo.Shared
{
public class SiteLog
{
public int ID { get; set; }
public DateTime DateTime { get; set; }
public Site Site { get; set; }
public int SiteID { get; set; }
public double[] Lev { get; set; }
}
}

0 comments on commit 71d5065

Please sign in to comment.