Is it safe to replace immutable data structure with Interlocked.Exchange(ref oldValue, newValue) in ASP.NET Core Web-Api

Viewed 128

I got an api that's an end point for geographic coordinate requests. That means users can search for specific locations in their area. At the same time new locations can be added. To make the query as fast as possible, I thought I would make the R-tree unchangeable. That is, there are no locks within the R-Tree, since several threads can read at the same time, without race condition. The updates are collected and if e.g. 100 updates are collected, I want to create a new R-Tree and replace the old one. And now my question is how to do this best?

I have a SearchService, which is stored as a single tone and has an R-Tree as private instance.

In my Startup.cs

services.AddSingleton<ISearchService, SearchService>();

ISearchService.cs

public interface ISearchService
{
    IEnumerable<GeoLocation> Get(RTreeQuery query);

    void Update(IEnumerable<GeoLocation> data);
}

SearchService.cs

public class SearchService : ISearchService
{
    private RTree rTree;

    public IEnumerable<GeoLocation> Get(RTreeQuery query)
    {
        return rTree.Get(query);
    }

    public void Update(IEnumerable<GeoLocation> data)
    {
        var newTree = new RTree(data);

        Interlocked.Exchange<RTree>(ref rTree, newTree);
    }
}

My question is, if I exchange the reference with Interlock.Exchange() the operation is atomic and there should be no race condition. But what happens if threads still use the old instance to process their request. Could it be that the garbage collector deletes the old instance when threads still access it? After all, there is no longer a reference to the old instance.

I am relatively new to this topic, so any help is welcome. Thanks for your support!

1 Answers

Read and writes to references are atomic, which means there will be no alignment issues. However, they could be stale.

Section 12.6.6 of the CLI specs

Unless explicit layout control (see Partition II (Controlling Instance Layout)) is used to alter the default behavior, data elements no larger than the natural word size (the size of a native int) shall be properly aligned. Object references shall be treated as though they are stored in the native word size.

In regards to the GC, your trees are safe from garbage collection while they are running Get.

So in summary, your methods are thread safe as far as reference atomicity go, you can also use the Update method and safely overwrite the reference, there is no need for Interlocked.Exchange. The worst that can happen with your current implementation is you just get a stale tree which you have mentioned is not an issue.

Related