1

I have a class which contains a Sync object

[DataContract]
class C1
{
  [DataMember]
  private object mySyncObject = new object();
}

This object was previously part of a DataContract which was serialized for no apparent reason. Since it contains no data I have omitted DataMember on the sync object. But since DataContractSerializer will not call the default ctor during deserialization this leads to NullReferenceExceptions at runtime when I use it.

lock(mySyncObject) // NullReferenceException

Now I would like to encapsulate the lock object access in a property and ensure that multithreaded access does not cause the property to return null or different objects.

My revised object now looks like

[DataContract]
class C1
{
    private volatile object mySyncObject = new object();

    object SyncObject
    {
        get
        {
            if (mySyncObject == null)
            {
                Interlocked.CompareExchange<object>(ref mySyncObject, new object(), null);
            }
            return mySyncObject;
        }
    }
}

Can it be so easy or have I missed a potential race condition here?

Osama AbuSitta
  • 3,918
  • 4
  • 35
  • 51
Alois Kraus
  • 13,229
  • 1
  • 38
  • 64

1 Answers1

1

You haven't missed a potential race condition, as the Interlock.CompareExchange<T>(ref T location1, T value, T comparand) is an atomic operation. You rather created a similar implementation as the Lazy<T> implementation.

A great example can be found on this blog discussing a very similar implementation you are showing (for this reason, I also won't add the implementation again).



Original answer, with an alternative to the OPs suggested implementation

As an alternative, you could also make use of the OnDeserializedAttribute to initialize the mySyncObject after the deserialization took place, as following example

[DataContract]
class C1 
{
    private object mySyncObject = new object();

    [OnDeserialized]
    internal void OnDeserialized(StreamingContext context)
    {
        mySyncObject = new object();
    }
}

This way you can keep your original logic everywhere, and directly after the deserialization happened, the method would be called and your mySyncObject would be initialized.

This would be, as we discussed in the comments, compatible with the DataContractSerializer, BinaryFormatter, JSON.Net.

More information about why the DataContractSerializer doesn't use the default constructor, can be found in this answer

Community
  • 1
  • 1
Icepickle
  • 12,689
  • 3
  • 34
  • 48
  • Well actually I want to refactor the code to use a different serializer which is not DataContracts. The constraint is that the code should just work with the new (Json.NET) and old serializer. I do not want to rely therefore on any specific serializer attribute magic which is called or not depending on the used serializer. – Alois Kraus Jan 13 '17 at 08:09
  • @AloisKraus Updated my answer with some more findings :) – Icepickle Jan 13 '17 at 21:16
  • Thanks I just wanted to make sure that I did not break anything with a dumb error on my side. – Alois Kraus Jan 13 '17 at 23:21