Home > front end >  Xamarin What happens to Lazy initialized singleton after App goes to background
Xamarin What happens to Lazy initialized singleton after App goes to background

Time:01-05

I use Lazy initialization for app singleton in Xamarin.Forms (app runs on iOS):

public sealed class DataSingleton
{
    private static readonly Lazy<DataSingleton> lazy = new Lazy<DataSingleton>(() => new DataSingleton(), LazyThreadSafetyMode.PublicationOnly); // tried withou 2nd parameter as well

    public static DataSingleton Instance
    {
        get { return lazy.Value; }
    }
    ...
}

And i call it in webserver which runs to provide data for Front-end which is in Angular (used web view to show Angular code)

var server = new WebServer(o => o
                .WithUrlPrefix($"http://localhost:{appSettings.ApiPort}")
                .WithMode(HttpListenerMode.EmbedIO))
            .WithCors()
            .WithLocalSessionManager()
            .WithWebApi("/api", m => m
                .WithController<TestController>()
                .WithController<SettingsController>()
                .WithController<ReportController>()
                .WithController<SystemController>())
            .WithModule(new ActionModule("/", HttpVerbs.Any,
                ctx => ctx.SendDataAsync(new { Message = "Error" })))
            .RunAsync();

In controllers is called DataSingleton to get/set data, but after app returns from background, DataSingleton.Instance is null.

What should I do to don't lose data of singleton, while app is in background for short time (approximately 5 minutes)

Update - I've figured out that this problem is only in Controllers, cause when app gets back to front I can see all the data in AppDelegate WillEnterForeground event..

CodePudding user response:

In this case, there can be race-condition. If two (or more threads) simultaneously reads Instance for first time, there will be created multiple instances of DataSingleton. However every other read will get just one instance. It depends on your scenario, if it is ok.

public sealed class DataSingleton {
  private static instance;

  // will assign new DataSingleton only if "instance" is null.
  public static Instance => instance ??= new DataSingleton();
}

Or you can use Interlocked class ensuring, the instance field will not be overriden if another thread already initialized the instance field.

public sealed class DataSingleton {
  private static instance;
  public static Instance {
    get {
      var result = instance;

      // early exit if singleton is already initialized
      if (result is not null) return result;

      var created = new DataSingleton();
      // thread-safe way how to assign "created" into "instance" only if "instance" refers to null. othervise no assignment will be made
      var original = Interlocked.CompareExchange(ref instance, null, created);

      // some other thread already initialized singleton
      if (original is not null) return original;

      // return newly created instance
      return result;
    }
  }
}

Or you can use lock to ensure, just one instance is created.

public sealed class DataSingleton {
  private static instance;
  public static Instance {
    get {
      var result = instance;

      // early exit if singleton is already initialized
      if (result is not null) return result;

      lock(typeof(DataSingleton)) {
        result = instance;

        // double check, if instance was not initialized by another thread
        if (result is not null) return result;
    
        return instance = new DataSingleton();
      }
    }
  }
}

CodePudding user response:

Given that it is the webserver that is having the problem, stop it when app goes into background. Start it again when app returns (or lazy-start as needed).

Code might be something like this:

App.xaml.cs:

public static Webserver MyWebServer
{
    get
    {
        if (_server == null)
        {
            _server = new Webserver(...);
        }

        return _server;
    }
}
public static void StopWebServer()
{
    if (_server != null)
    {
        _server.Dispose();
        // So will be created again, on next reference to MyWebServer.
        _server = null;
    }
}
private static Webserver _server;

...

protected override void OnSleep()
{
    StopWebServer();
}

Usage elsewhere:

... App.MyWebServer ...

If you don't want to make static variable (though IMHO that is okay for App, because there is only one, and its lifetime is that of the app itself), then remove the "static"s above, usage elsewhere becomes:

... (Xamarin.Forms.Application.Current as App).MyWebServer ...
  •  Tags:  
  • Related