Threading issues in App.cs OnSleep()

RHudsonRHudson CAMember ✭✭
edited September 2015 in Xamarin.Forms

I'm using PCL Storage to write my JSON data to the device, and it's working great for the most part.

However it is failing during the App OnSleep() event.

The functions CreateFileAsync() and WriteAllTextAsync are of course asynchronous

So when the device goes to sleep the OS kills the main thread before the contents get written and I end up with a blank file.

What's the best way to handle housekeeping and saving when the app goes to sleep?

Can these functions run synchronously? I tried removing "await" but I get compiler errors, I think because of the Task<> structure

Best Answers

Answers

  • AlanSpiresAlanSpires USBeta ✭✭

    On iOS I use "Background Safe Tasks", this allows me to fire off tasks that will finish even after the device has lost focus.

    https://developer.xamarin.com/guides/ios/application_fundamentals/backgrounding/part_3_ios_backgrounding_techniques/ios_backgrounding_with_tasks/#background_tasks_in_ios7

    Using dependency service this is pretty easy to accomplish from your PCL.

  • RHudsonRHudson CAMember ✭✭

    Thanks @AlanSpires , I could try this as a last resort. But I'd like a native XF solution that works on all 3 platforms.

    The App.cs class provides an OnSleep event so surely there must be an easy way to persist data. I'm sure this is an extremely common thing to do, apps saving their data.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    The App.cs class provides an OnSleep event so surely there must be an easy way to persist data.

    There is if you are using a synchronous API (that doesn't take too long). If you're using an asynchronous API then you have to do something to keep the app from being suspended before you're finished writing, and there is no built-in cross-platform API for that.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    If you want to use another API for settings then look at App.Properties, which has a method for writing the file synchronously.

  • ylemsoulylemsoul RUMember ✭✭✭
    edited September 2015

    Using async or sync API is orthogonal to the problem where app must save it state before termination.
    It is completely acceptable to block the main thread while app is suspending. Because the user is not interacting with the app anymore. But no matter what API is used it must complete in specific time range or OS while forcibly terminate it. This restriction is also applied to Xamarin's Applcation.Properties.SavePropertiesAsync(). No magic here.

    Use CreateFileAsync().Wait(); while remembering about this restriction.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    Use CreateFileAsync().Wait(); while remembering about these restrictions.

    It is generally a terrible idea to use the .Wait() method, especially in the UI thread. It may cause deadlocks because the asynchronous code may need to go to a background thread and then return back to the UI thread later. In that case it will hang because the UI thread is busy waiting for the task to complete, but the task is waiting for the UI thread to do something.

    It may work in this case, but it's dangerous code. I strongly discourage you from doing this.

  • ylemsoulylemsoul RUMember ✭✭✭

    Yes, generally it is bad. I'm just talking about this particular case (while app is suspending).

  • adamkempadamkemp USInsider, Developer Group Leader mod

    The fact that the app is suspending doesn't change the risk factors. The risk is all in what exactly the async function does. For instance, this code might hang:

    public Task DoSomethingAsync()
    {
        await Task.Run(() => { /* some code in background */ });
        // Back in UI thread
        DoSomethingElse();
    }
    

    If you called that function in the UI thread (e.g., in OnSleep()) and used .Wait() then your app would hang. As soon as the Task.Run task is complete then the function tries to continue in the originating synchronization context, which means it tries to go back to the UI thread, which is blocked. DoSomethingElse would never be called, and thus this task would never be finished.

    Maybe your file saving code doesn't do this now, but can you guarantee it never will? That's a big assumption.

  • ylemsoulylemsoul RUMember ✭✭✭

    Indeed, this may hang when the library does things like that.
    What about wrapping this dangerous function in Task.Run(..)? :
    private void OnSleep() { Task.Run(() => DoSomethingElse()).Wait(); }

  • RHudsonRHudson CAMember ✭✭

    Wow, more complex than it appears.

    I will try a different approach. Like saving the data at more appropriate times, such as when the user steps out of the editor page and reloads the menu.

    A technique I've used is to include an 'IsDirty' property in the classes which automatically gets set whenever some object is changed. An easy way to determine if a Save is required.

    @adamkemp @ylemsoul Thanks for your input

Sign In or Register to comment.