Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lazy<T> with LazyThreadSafeMode.PublicationOnly and IDisposable

Today I was playing with Lazy<T> and found an interesting case (to my mind).

http://msdn.microsoft.com/en-us/library/system.threading.lazythreadsafetymode.aspx

  • PublicationOnly:

    When multiple threads try to initialize a Lazy instance simultaneously, all threads are allowed to run the initialization method ... Any instances of T that were created by the competing threads are discarded.

    If we look at the code of Lazy<T>.LazyInitValue() we will find that there is no check for IDisposable implementation and resoruces may leak here:

     case LazyThreadSafetyMode.PublicationOnly:
            boxed = this.CreateValue();
            if (Interlocked.CompareExchange(ref this.m_boxed, boxed, null) != null)
            {  
               //* boxed.Dispose(); -> see below.
               boxed = (Boxed<T>) this.m_boxed;
            }
            break;
    

As of now the only way to ensure that only instance is created is to use LazyThreadSafetyMode.ExceptionAndPublication.

So I have 2 questions:

  • Do I miss something or we can see that few insntance can be created and resources can leak in this situation ?
  • If it's correct assumption why not to check for IDisposable in this situation and implement Dispose() on Boxed<T> such that it delegates disposal to the Boxed instance of T if it implements IDisposable or in some different way:

       class Boxed<T>
       {
            internal T m_value;
            void Dispose()
            {
                if (m_value is IDisposable)
                {     ((IDisposable) m_value).Dispose();  }
            }
       }
    
like image 756
Andrey Taptunov Avatar asked May 15 '11 11:05

Andrey Taptunov


1 Answers

To answer the first question, if a class implements IDisposable "properly" then no, there should be no danger of a resource leak. There may however be a delay in which unmanaged resources would remain un-released until garbage collection occurs.

Consider the following application:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;

namespace LazyInit {
    class DisposableClass : IDisposable {
        private IntPtr _nativeResource = Marshal.AllocHGlobal(100);
        private System.IO.MemoryStream _managedResource = new System.IO.MemoryStream();
        public string ThreadName { get; set; }

        public void Dispose() {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        ~DisposableClass() {
            Console.WriteLine("Disposing object created on thread " + this.ThreadName);
            Dispose(false);
        }

        private void Dispose(bool disposing) {
            if (disposing) {
                // free managed resources
                if (_managedResource != null) {
                    _managedResource.Dispose();
                    _managedResource = null;
                }
            }
            // free native resources if there are any.
            if (_nativeResource != IntPtr.Zero) {
                Marshal.FreeHGlobal(_nativeResource);
                _nativeResource = IntPtr.Zero;
            }
        }
    }

    static class Program {
        private static Lazy<DisposableClass> _lazy;

        [STAThread]
        static void Main() {
            List<Thread> t1 = new List<Thread>();

            for (int u = 2, i = 0; i <= u; i++)
                t1.Add(new Thread(new ThreadStart(InitializeLazyClass)) { Name = i.ToString() });
            t1.ForEach(t => t.Start());
            t1.ForEach(t => t.Join());

            Console.WriteLine("The winning thread was " + _lazy.Value.ThreadName);
            Console.WriteLine("Garbage collecting...");
            GC.Collect();
            Thread.Sleep(2000);
            Console.WriteLine("Application exiting...");
        }

        static void InitializeLazyClass() {
            _lazy = new Lazy<DisposableClass>(LazyThreadSafetyMode.PublicationOnly);
            _lazy.Value.ThreadName = Thread.CurrentThread.Name;
        }
    }
}

Using LazyThreadSafetyMode.PublicationOnly, it creates three threads, each instantiating Lazy<DisposableClass> then exits.

The output looks something like this:

The winning thread was 1

Garbage collecting...

Disposing object created on thread 2

Disposing object created on thread 0

Application exiting...

Disposing object created on thread 1

As mentioned in the question, Lazy<>.LazyInitValue() does not check for IDisposable, and Dispose() is not being called explicitly, yet still all three objects were ultimately disposed; two objects were disposed because they fell out of scope and were garbage collected, and the third was disposed on application exit. This occurs because we're making use of the destructor/finalizer that is called when all managed objects are destroyed, and in turn, using it to ensure that our unmanaged resources are released.

To the second question, it's anyone's guess as to why no IDisposable check was put in place. Perhaps they didn't envision unmanaged resources being allocated upon instantiation.

Further reading:

For more on how to properly implement IDisposable, look on MSDN here (which is where half of my example is sourced from).

Also, there is an excellent SO article here, which gives the best explanation I've ever seen of why IDisposable should be implemented this way.

like image 122
cokeman19 Avatar answered Jan 02 '23 11:01

cokeman19