realm-dotnet: [Bug]: NullReferenceException in a loop

What happened?

NullReferenceException in a loop

Repro steps

I have a hard time creating a 100% repro step, however this is the situation i’m having:

I have a ‘highly’ concurrent setup, where items are added / updated / removed by multiple threads.

In one of the threads I then loop over all items, using:

using (var realm = Realm.GetInstance())
{
          var models = realm.All<MyModel>();

          foreach (var model in models)
          {

                //model is sometimes NULL here
                // or model is 'no longer valid'

          }
}

How can I make this access safe ?

Version

10.9.0

What SDK flavour are you using?

Local Database only

What type of application is this?

Xamarin

Client OS and version

iOS 15.3

Code snippets

No response

Stacktrace of the exception/crash you’re getting

No response

Relevant log output

No response

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 15 (7 by maintainers)

Most upvoted comments

Phew, thanks for the confirmation. I wrote up a test in the mean time and can confirm the behaviour is as we expect, and also as you mention regarding the implicit refresh on write:

// internally calls Realm.GetInstance()
realm.Write(ctx => ctx.Add(new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata())));

ManualResetEventSlim resetEventSlim = new ManualResetEventSlim();

var task = Task.Factory.StartNew(() =>
{
    // internally calls Realm.GetInstance()
    realm.Run(ctx =>
    {
        var beatmaps = ctx.All<BeatmapInfo>();

        Assert.That(beatmaps.Count(), Is.EqualTo(1));

        // wait for delete operation on main thread.
        resetEventSlim.Wait();

        // can still access all objects with no exception.
        Assert.That(beatmaps.Count(), Is.EqualTo(1));
        var beatmapInfo = beatmaps.First();
        Assert.That(beatmapInfo.Metadata, Is.Not.Null);

        // .. until a refresh
        ctx.Refresh(); // <- can be replaced with an empty write to produce the same effect ctx.Write(() => { });

        // .. after which the object goes away and will throw an invalid object exception
        Assert.That(beatmaps.Count(), Is.EqualTo(0));
        Assert.Throws<RealmInvalidObjectException>(() => Console.WriteLine(beatmapInfo.Metadata.ToString()));
    });
}, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler);

// internally calls Realm.GetInstance()
realm.Write(ctx =>
{
    ctx.RemoveAll<BeatmapInfo>();
    ctx.Refresh();
});

resetEventSlim.Set();

task.WaitSafely();