Freeze when trying to read a file using PCLStorage - c#

I am using PCLStorage library in my project so that I can access filesystem from my PCL lib. I am trying to read a file as follows:
static async Task<T> LoadAsync<T> (string fileName) where T : class
{
var rootFolder = FileSystem.Current.LocalStorage; // debugger stops here
var m5cacheFolder = await rootFolder.GetFolderAsync (CacheFolderName); // but instead of going to this line, jumps to end of this method
var userInfoFile = await m5cacheFolder.GetFileAsync (fileName);
var userInfoFileContent = await userInfoFile.ReadAllTextAsync ();
var stringReader = new StringReader (userInfoFileContent);
var serializer = new XmlSerializer (typeof(T));
return (T)serializer.Deserialize (stringReader);
}
Since PCLStorage is asynchronous and I want to use it in a syncrhonous code I am calling it like this:
var task = LoadAsync<User> (UserInfoFileName);
user = task.Result;
The problem is that whole application freezes when I try to execute this code. As described in comments above, the code in LoadAsync method is not executed. I am using newest Xamarin 3. My PCL library is referenced in Xamarin iOS project. Both projects have references to PCLStorage through nugget.
On the other hand following code is executed correctly:
static async void PersistAsync (object obj, string fileName)
{
var rootFolder = FileSystem.Current.LocalStorage;
var m5cacheFolder = await rootFolder.CreateFolderAsync (CacheFolderName, CreationCollisionOption.OpenIfExists);
var userInfoFile = await m5cacheFolder.CreateFileAsync (fileName, CreationCollisionOption.ReplaceExisting);
var serializer = new XmlSerializer (obj.GetType ());
var stringWriter = new StringWriter ();
serializer.Serialize (stringWriter, obj);
await userInfoFile.WriteAllTextAsync (stringWriter.ToString ());
}

This is always a potential recipe for disaster:
var task = LoadAsync<User>(UserInfoFileName);
user = task.Result;
If this is happening in the UI thread, then you're basically blocking the UI thread until the task has completed - but the task will need to execute its continuations on the UI thread too. You've got a deadlock.
Basically you should be trying to structure your app to embrace asynchrony more. You could use Task.ConfigureAwait() to schedule the continuations in LoadAsync to execute on thread-pool threads instead, but you're still going to be blocking the UI until it's completed, which is against the spirit of asynchrony.
Asynchrony is somewhat viral - if you try to make just one part of your app asynchronous, you're going to have a hard time. You need to be asynchronous all the way up, at least in terms of UI operations.
(If you block waiting for the task returned by PersistAsync you'll have a similar issue, by the way.)

Related

How to fix GDI+ Errors when downloading files?

I have been making a client that installs a program I am also making. The problem is when I go to download the files. Sometimes, it gets stuck. I get thrown a error stating
System.Runtime.InteropServices.ExternalException: 'A generic error occurred in GDI+.'
...and my UI freaks out (Buttons go blank, labels disapper, images go missing, ect). I have researched this error and it seems it happens with images, but I am downloading zip files that contain no images. I have found that it might have something to do with 5 files I am extracting from the zip files but thats not entirely accurate because it doesnt always happen and I have no real way to determine EXACTLY whats causing it. I suspect its because I cannot download so much in a short period of time but I do not know if this is why exactly either.
Also to add to this, the files still complete downloading when in debug mode, they ever continue through the async process, awaiting properly and everything.
I have tried narrowing down what file is causing it but I dont have any evidence to support it is a specific file. I have also tried spliting up the zip files to see if its the size of how much Im downloading at once, still no luck.
These are the download functions.
The RunWorkerTaskAsync() is a custom reference I created to allow a worker to be "awaited". I privide the code below.(I take no credit as its pieces of code I have pulled from others)
private async Task DownloadLibs()
{
Response.Text = "Updating Libraries...";
this.Update();
string url = #"http://akumamc.com/AkumaMC/Libraries.zip";
if (!string.IsNullOrEmpty(url))
{
Uri uri = new Uri(url);
string fileName = System.IO.Path.GetFileName(uri.AbsolutePath);
await DLclient.DownloadFileTaskAsync(uri, #"C:\temp\" + fileName);
DLclient.Dispose();
}
FileZipName = #"C:\temp\Libraries.zip";
FileZipPath = #"C:\temp\.minecraft";
Response.Text = "Extracting Libraries...";
this.Update();
await extractFile.RunWorkerTaskAsync();
}
private async Task DownloadMods()
{
Response.Text = "Updating Mods (1/2)...";
this.Update();
string url = #"http://akumamc.com/AkumaMC/Mods.zip";
if (!string.IsNullOrEmpty(url))
{
Uri uri = new Uri(url);
string fileName = System.IO.Path.GetFileName(uri.AbsolutePath);
await DLclient.DownloadFileTaskAsync(uri, #"C:\temp\" + fileName);
DLclient.Dispose();
}
FileZipName = #"C:\temp\Mods.zip";
FileZipPath = #"C:\temp\.minecraft";
Response.Text = "Extracting Mods (1/2)...";
this.Update();
await extractFile.RunWorkerTaskAsync();
}
private async Task DownloadExtras()
{
Response.Text = "Updating Mods (2/2)...";
this.Update();
string url = #"http://akumamc.com/AkumaMC/Mods2.zip";
if (!string.IsNullOrEmpty(url))
{
Uri uri = new Uri(url);
string fileName = System.IO.Path.GetFileName(uri.AbsolutePath);
await DLclient.DownloadFileTaskAsync(uri, #"C:\temp\" + fileName);
DLclient.Dispose();
}
FileZipName = #"C:\temp\Mods2.zip";
FileZipPath = #"C:\temp\.minecraft";
Response.Text = "Extracting Mods (2/2)...";
this.Update();
await extractFile.RunWorkerTaskAsync();
}
RunWorkerTaskAsync:
public static Task<object> RunWorkerTaskAsync(this BackgroundWorker backgroundWorker)
{
var tcs = new TaskCompletionSource<object>();
RunWorkerCompletedEventHandler handler = null;
handler = (sender, args) =>
{
if (args.Cancelled)
tcs.TrySetCanceled();
else if (args.Error != null)
tcs.TrySetException(args.Error);
else
tcs.TrySetResult(args.Result);
};
backgroundWorker.RunWorkerCompleted += handler;
try
{
backgroundWorker.RunWorkerAsync();
}
catch
{
backgroundWorker.RunWorkerCompleted -= handler;
throw;
}
return tcs.Task;
}
I expect the files to download without the form causing UI glitches and crashing.
EDIT: Link to author's client code (taken from comment below)
This is a summary of my comments beneath the OP's question
System.Runtime.InteropServices.ExternalException: 'A generic error occurred in GDI+.'
So the "interop" error implies some form of component object model (COM) problem and the things that stick out are:
the use of some 3rd party library that may be using COM
your RunWorkerTaskAsync extension method seems to be making multiple calls to BackgroundWorker.RunWorkerAsync without first checking that the worker is busy.
BackgroundWorker.RunWorkerAsync() is a void, it does not return a Task and so can't be used in async/await. Therefore your extension method is essentially kicking off a background worker without waiting for it to complete. Your extension method RunWorkerTaskAsync() (which isn't entirely async) returns immediately to those that called it.
You need to for the worker to complete before calling RunWorkerAsync again.
A possible fix:
Inside your extension method, check BackgroundWorker.IsBusy before telling it to run. (A better way is to wait for RunWorkerCompleted and kicking off a new one there)
Call RunWorkerAsync
Because you want to wait for this "task" to complete before returning control to say DownloadMods(), your extension method will need to monitor RunWorkerCompleted. This is kinda ugly as it goes against the original best practices of BackgroundWorker where everything is event-driven.
Alternatives
Considering you are using async/await anyway, why use BackgroundWorker at all? Consider wrapping up the essence of your extension method into a new method and call it via Task.Run().
You can still have a async Task ExtractFilesAsync method that runs in a child task (because we used Task.Run() it will also be a child thread) can report progress.
Something like (pseudo code):
await Task.Run ( async () => await
UnzipFilesAsync ( p =>
{
myProgressBar.BeginInvoke (new Action( () =>
myprogressBar.Progress = p; ));
});
.
.
.
UnzipFilesAsync (Action<int> progressCallback)
{
.
.
.
int percent = ...;
progressCallback (percent);
}
Tell me more about async progress bar updates
By the way, you shouldn't call MessageBox.Show or update the UI directly in a child thread, even if the call is a dialog with its own message pump.

asp.net Program execution continues before tasks finish executing

I am trying to run 3 of tasks on different threads (there will be a few more added.) The tasks that are called then call other tasks that are async / await.
The program execution continues after my command to wait. Execution needs to wait until all tasks are complete. My code is below (the null return is just there to test, I still need to create the return code.
public List<string> CopyFilesAsync(List<ModelIterationModel> model)
{
var copyFileTaskParameters = GetCopyFileTaskParameters(model);
Task<List<CopyFitDataResult>> fitDataResulLits = null;
Task<List<CopyNMStoreResult>> nmStoreResultsList = null;
Task<List<CopyDecompAnalyzerResult>> decompAnalyzerStoreResultsList = null;
Task parent = Task.Factory.StartNew(() =>
{
var cancellationToken = new CancellationToken();
TaskFactory factory = new TaskFactory(TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously);
factory.StartNew(() => fitDataResulLits = CopyFitDataFiles(copyFileTaskParameters, cancellationToken));
factory.StartNew(() => decompAnalyzerStoreResultsList = CopyDecompAnalyzerFiles(copyFileTaskParameters, cancellationToken));
factory.StartNew(() => nmStoreResultsList = CopyNMStoreResultsFiles(copyFileTaskParameters, cancellationToken));
});
parent.Wait();
return null;
}
The calling code is synchronous. Execution continues in this method before the tasks above complete.
public void CreateConfigFile(CreateConfigFileParameter parameter)
{
try
{
//data for this will need to come from UI, return values will include local file paths. All copy operations will be ran on it's own thread
//return value will include local file paths
var userFileListModel = _copyFilesToLocalDirectoryService.CopyFilesAsync(temp);
//will return object graph of data read from speadsheets and excel files
_readLocalFilesToDataModelService.ReadAllFiles();
//will take object graph and do date period logic and event type compression and any other business
//logic to extract an object model to create the config file
_processDataModelsToCreateConfigService.Process();
//will take extracted object model and use config file template to create the config file workbook
_writeConfigFileService.WriteConfigFile();
}
catch(Exception ex)
{
}
}
This code is in a class library in a WPF application. I don't know if that is important, but this is the first time I have had to interact with WPF (15 years of web development only.)
What do I need to do to stop execution until all tasks have completed? I played around with a few other approaches, such as attaching as children but nothing I do seems to work.
Edit - I keep trying approaches straight out of MSDN samples with no luck whatsoever. Just tried this
var cancellationToken = new CancellationToken();
var tasks = new List<Task>();
tasks.Add(Task.Run(() =>
{
fitDataResulLits = CopyFitDataFiles(copyFileTaskParameters, cancellationToken);
}));
Task t = Task.WhenAll(tasks.ToArray());
t.Wait();
Exactly like the MSDN sample, and I tried WaitAll but it runs right past it.
Could this have something to do with the Visual Studio debugger?
There are many questions to your code:
If you do not wait for files to be copied, how next lines of code should run?
Why do you need to create a TaskFactory to start a background work, which is already a Task?
Why do you create a CancellationToken? You need to create a CancellationTokenSource, and use it's Token for all your code you may need to cancel.
Also, this code:
tasks.Add(Task.Run(() =>
{
fitDataResulLits = CopyFitDataFiles(copyFileTaskParameters, cancellationToken);
}));
doesn't fire the CopyFitDataFiles, it simply assigns a task reference. You need to do this:
tasks.Add(CopyFitDataFiles(copyFileTaskParameters, cancellationToken));
Your code should be rewritten in this way:
public async Task<List<string>> CopyFilesAsync(List<ModelIterationModel> model)
{
var copyFileTaskParameters = GetCopyFileTaskParameters(model);
// do not await tasks here, just get the reference for them
var fitDataResulLits = CopyFitDataFiles(copyFileTaskParameters, cancellationToken);
// ...
// wait for all running tasks
await Task.WhenAll(copyFileTaskParameters, ...);
// now all of them are finished
}
// note sugnature change
public async Task CreateConfigFile
{
// if you really need to wait for this task after some moment, save the reference for task
var userFileListModel = _copyFilesToLocalDirectoryService.CopyFilesAsync(temp);
...
// now await it
await userFileListModel;
...
}
There is a great article about async/await: Async/Await - Best Practices in Asynchronous Programming by #StephenCleary

Getting deadlock with foreach and await

I am trying to call a service, but the service has a max length per request, so I am splitting my request into multiple smaller requests.
I am then trying to to use HttpClient together with Await as its async
public async Task<string> CallGenoskanAsync(List<string> requestList)
{
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
var credentials = new NetworkCredential(userId, password);
var tasks = new List<Task<string>>();
foreach (string requestString in requestList)
{
using (HttpClientHandler handler = new HttpClientHandler { Credentials = credentials })
{
using (HttpClient client = new HttpClient(handler))
{
client.BaseAddress = new Uri(baseAddress);
using (HttpResponseMessage response = client.GetAsync(requestString).Result)
using (HttpContent content = response.Content)
{
tasks.Add(content.ReadAsStringAsync());
}
}
}
}
Task.WaitAll(tasks.ToArray());
var result = "";
foreach (var task in tasks)
{
result += task.Result;
}
return result;
}
This code deadlocks when await client.GetAsync is called, it never finishes.
If I change that line to
using (HttpResponseMessage response = client.GetAsync(requestString).Result)
Then I dont get any deadlocks, I suppose I am using await improperly together with foreach, but I can not figure out how
Edit: Changed example code
The compiler will give you a warning for the code you posted; in particular, it will point out that CallGenoskanAsync is synchronous, not asynchronous.
The core problem is this line:
Task.WaitAll(tasks.ToArray());
When you're writing asynchronous code, you shouldn't block on it. To do so can cause deadlocks, as I explain in my blog post. The deadlock occurs because await will capture a "context" that it uses to resume execution of the async method. This "context" is SynchronizationContext.Current or TaskScheduler.Current, and many contexts (UI and ASP.NET request contexts in particular) only allow one thread. So, when your code blocks a thread (Task.WaitAll), it's blocking a thread in that context, and this prevents the await from continuing since it's waiting for that context.
To fix, make your code asynchronous all the way. As I explain in my async intro post, the asynchronous equivalent of Task.WaitAll is await Task.WhenAll:
await Task.WhenAll(tasks);
WhenAll also has the nice property that it unwraps the results for you, so you don't have to use the problematic Result property:
var results = await Task.WhenAll(tasks);
return string.Join("", results);
Relevant context is not provided:
If
Your code sample is part of a callstack that returns a Task
There is a blocking wait at the root of the callstack (e.g.,
DoSomething().Result or DoSomething.Wait())
You have a SynchronizationContext that's thread affinitized (IE, this is running pretty much anywhere but a Console application)
Then
Your synchronization context's thread may be blocked on the root Result/Wait(), so it can never process the continuation that the completed call to GetAsync() scheduled to it. Deadlock.
If you meet the above conditions, try applying .ConfigureAwait(false) to your awaited GetAsync. This will let the continuation get scheduled to a Threadpool thread. Be aware that you are potentially getting scheduled to a different thread at that point.

await task inside another task

I'm working on a C# console application that will be responsible for running an array of tasks. The basic structure for that is as follows:
var tasks = workItems.Select(x => Task.Factory.StartNew(() =>
{
DoSomeWork(x);
})).ToArray();
Task.WaitAll(tasks);
The problem is that DoSomeWork() is an async method which awaits the result of another task, which also needs to await a call to the Facebook API.
http://facebooksdk.net/docs/reference/SDK/Facebook.FacebookClient.html#GetTaskAsync(string)
public async void DoSomeWork(WorkItem item)
{
var results = await GetWorkData();
}
public async Task<List<WorkData>> GetWorkData()
{
var fbClient = new FacebookClient();
var task = fbClient.GetTaskAsync("something");
var fbResults = await task;
};
I thought I would be able to support this notion of nested tasks with the call to Task.WaitAll() but the execution of the parent tasks finishes almost immediately. Putting a Console.ReadLine() at the end of the application to prevent it from early execution shows that the result will indeed come back later from Facebook.
Am I missing something obvious or is there a better way to block for my Task collection that will allow for this kind of scenario?
DoSomeWork needs to not return void. The by not returning a Task the caller has no way of knowing when if finishes.
Additionally, it's already asynchronous, so there is no reason to use StartNew here. Just call DoSomeWork directly, since it should be returning a Task.

Read a file from background task

I'm trying to call a method from inside the Run method of a background task which among other it desirializes a xml file. The problem is that I end up in a deadlock. This is the methos that reads the file
protected async Task<Anniversaries> readFile(string fileName)
{
IStorageFile file;
Anniversaries tempAnniversaries;
file = await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
using (IRandomAccessStream stream =
await file.OpenAsync(FileAccessMode.Read))
using (Stream inputStream = stream.AsStreamForRead())
{
DataContractSerializer serializer = new DataContractSerializer(typeof(Anniversaries));
tempAnniversaries = serializer.ReadObject(inputStream) as Anniversaries;
}
return tempAnniversaries;
}
and here is the Run method
public sealed class TileUpdater : IBackgroundTask
{
GeneralAnniversariesManager generalManager = new GeneralAnniversariesManager();
Anniversaries tempAnn = new Anniversaries();
string test = "skata";
public async void Run(IBackgroundTaskInstance taskInstance)
{
DateTime curentTime = new DateTime();
var defferal = taskInstance.GetDeferral();
await generalManager.InitializeAnniversariesAsync().AsAsyncAction();
curentTime = DateTime.Now;
var updater = TileUpdateManager.CreateTileUpdaterForApplication();
updater.EnableNotificationQueue(true);
updater.Clear();
for (int i = 1; i < 6; i++)
{
var tile = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWide310x150BlockAndText01);
tile.GetElementsByTagName("text")[0].InnerText = test + i;
tile.GetElementsByTagName("text")[1].InnerText = curentTime.ToString();
updater.Update(new TileNotification(tile));
}
defferal.Complete();
}
I'm assuming that by deadlock you mean that the deserialization method finishes too late and your original program tries to read the data before it's finished loading.
It depends on how complicated/reliable you want your solution to be and how you're intending to use the program. The simplest way relies on the fact that the directory creation function is always 100% atomic in Windows/Unix and OSX. For example at the top of your readFile function have something like this.
Directory.CreateDirectory("lock");
Before you start parsing the results of your async action in TileUpdater, have a loop that looks like this.
while (Directory.Exists("lock"))
{
Thread.Sleep(50);
}
This assumes that everything is happening in the same directory, generally you'll want to replace "lock" with a path that leads to the user's temp directory for their version of Windows/Linux/OSX.
If you want to implement something more complicated where you're reading from a series of files while at the same time reading the deserialized output into your class, you'll want to use something like a System.Collections.Concurrent.ConcurrentQueue that allows your threads to act completely independently without blocking each other.
Incidentally I'm assuming that you know that the class Process and the function .waitfor() exists. You can spin off a thread and then at a later point, halt the main thread until the spawned thread finishes.
Actually I think I've found where the problem is. At the namespaces, I've tried a try and catch and I got an exception about using different namespaces at the datacontract serealizer. I have updated the code like this
file = await ApplicationData.Current.LocalFolder.GetFileAsync("EortologioMovingEntries.xml");
try
{
using (IRandomAccessStream stream =
await file.OpenAsync(FileAccessMode.Read))
using (Stream inputStream = stream.AsStreamForRead())
{
DataContractSerializer serializer = new DataContractSerializer(typeof(Anniversaries), "Anniversaries", "http://schemas.datacontract.org/2004/07/Eortologio.Model");
tempAnniversaries = serializer.ReadObject(inputStream) as Anniversaries;
}
}
catch (Exception ex)
{
error = ex.ToString();
tempAnniversaries.Entries.Add(new AnniversaryEntry("Ena", DateTime.Now, "skata", PriorityEnum.High));
}
I don't get any exceptions now but the tempAnniversaries returns null. Any ideas?

Categories