0

I was reading for this question: Difference between destructor, dispose and finalize method

I've read that the destructor was used to delete unmanaged resources, but after running a little test :

using System;

namespace Tests
{
    class ImABastardException : Exception 
    {
        public ImABastardException() : base() { }
    }

    class Mouarf
    {
        public Mouarf()
        {
            Console.WriteLine("Constructor");
            throw new ImABastardException();
        }

        ~Mouarf()
        {
            Console.WriteLine("Destructor");
        }
    }

    class Program
    {
        private static void Func()
        {
            Console.WriteLine("Before");
            try
            {
                Console.WriteLine("Try");
                Mouarf m = new Mouarf();
            }
            catch (ImABastardException)
            {
                Console.WriteLine("Catch");
            }
            finally
            {
                Console.WriteLine("Finally");
            }
            Console.WriteLine("After");
        }

        static void Main(string[] args)
        {
            Console.WriteLine("MainBefore");
            Func();
            Console.WriteLine("MainAfter");
            Console.ReadKey();  
        }
    }
}

The result was :

MainBefore
Before
Try
Constructor
Catch
Finally
After
MainAfter   
(I push enter or another key)   
Destructor

I'm afraid that the destructor isn't called when I want (at the end of its range)..

I actually want to create a simple class :

public unsafe class StringArray
{
    private sbyte** pointer;
    private int count;

    public StringArray(string[] array)
    {
        count = array.Length;

        pointer = (sbyte**)Memory.Alloc(count * sizeof(sbyte*));
        for (int i = 0; i < count; i++)
        {
            fixed (byte* p = Encoding.ASCII.GetBytes(array[i]))
            {
                pointer[i] = (sbyte*)p;
            }
        }
    }

    ~StringArray()
    {
        Memory.Free(pointer);
    }

    public sbyte** Pointer
    {
        get
        {
            return pointer;
        }
    }

    public int Count
    {
        get
        {
            return count;
        }
    }
}

public unsafe class Memory
{
    // see : https://msdn.microsoft.com/fr-fr/library/aa664786%28v=vs.71%29.aspx
}

But I think it would be better to use Dispose for unmanaged resources, what do you think?

Thanks!

Sylafrs.


Edit:

I've had asked that question as an answer to the topic I've given the link above;

Lasse V. Karlsen answered:

"As a quick comment to your actual question, I would implement IDisposable on that type, and have the finalizer call Dispose(false);"

So I've tested:

using System;

namespace Tests
{
    class ImABastardException : Exception 
    {
        public ImABastardException() : base() { }
    }

    class Mouarf: IDisposable
    {
        // Flag: Has Dispose already been called?
        bool disposed = false;

        public Mouarf()
        {
            Console.WriteLine("Constructor");
            throw new ImABastardException();
        }

        ~Mouarf()
        {
            Dispose(false);
        }

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

        // Protected implementation of Dispose pattern.
        protected virtual void Dispose(bool disposing)
        {
            if (disposed)
                return;

            if (disposing)
            {
                Console.WriteLine("Disposing managed objects");
            }

            Console.WriteLine("Disposing unmanaged objects");
            disposed = true;
        }
    }

    class Program
    {
        private static void Func()
        {
            Console.WriteLine("Before");
            Mouarf m = null;
            try
            {
                Console.WriteLine("Try");
                using (m = new Mouarf())
                {
                    ;
                }
            }
            catch (ImABastardException)
            {
                Console.WriteLine("Catch");
            }
            finally
            {
                Console.WriteLine("Finally");
            }
            Console.WriteLine("After");
        }

        static void Main(string[] args)
        {
            Console.WriteLine("MainBefore");
            Func();
            Console.WriteLine("MainAfter");
            Console.ReadKey();
        }
    }
}

IDisposable doesn't work too.. (the "Disposing unmanaged objects" message was written at the program's end)

I guess the problem comes from the constructor.. Must I use a method to do risky tasks?

Community
  • 1
  • 1
Sylafrs
  • 1
  • 1
  • 2
  • The destructor just doesn't run when you think it does, "end of scope" has absolutely nothing to do with it. What you see now is the final collection just before the program terminates. Don't use a destructor to solve your problem, whatever it might be. The SafeHandle and SafeBuffer base classes are very good wrappers for unmanaged resources. – Hans Passant Mar 30 '15 at 09:55
  • Thanks :) I don't really have a problem, in fact. I just want to be sure that my resources are destroyed as soon as possible – Sylafrs Mar 30 '15 at 10:17

1 Answers1

0

The full example should be:

public unsafe sealed class StringArray : IDisposable
{
    private sbyte** pointer;
    private int count;

    public StringArray(string[] array)
    {
        count = array.Length;

        pointer = (sbyte**)Marshal.AllocCoTaskMem(count * sizeof(sbyte*));

        for (int i = 0; i < count; i++)
        {
            fixed (char* p = array[i])
            {
                int len = Encoding.ASCII.GetByteCount(array[i]);
                pointer[i] = (sbyte*)Marshal.AllocCoTaskMem(len + 1); // Zero final byte

                Encoding.ASCII.GetBytes(p, array[i].Length, (byte*)pointer[i], len);
                pointer[i][len] = 0; // Final zero
            }
        }
    }

    ~StringArray()
    {
        Dispose();
    }

    public void Dispose()
    {
        if (pointer != null)
        {
            for (int i = 0; i < count; i++)
            {
                Marshal.FreeCoTaskMem((IntPtr)pointer[i]);
            }

            Marshal.FreeCoTaskMem((IntPtr)pointer);
            pointer = null;
            count = 0;
        }
    }

    public sbyte** Pointer
    {
        get
        {
            return pointer;
        }
    }

    public int Count
    {
        get
        {
            return count;
        }
    }
}

And to use it "correctly" you should write something like:

using (var sa = new StringArray(new[] { "a", "ab", "abc" }))
{

}

Note that the class is sealed, so you don't need to implement the full IDisposable "pattern".

You can't directly use the memory returned by GetBytes, because that is memory handled by the GC. You would need to pin it with GCHandle or write/copy it to a buffer you manually allocated (like I did).

In general the byte is preferred to the sbyte.

I'm adding a zero terminator to the strings, because C strings are built that way. Often in C, "things" like the StringArray are implemented without a Count "property" and the array is terminated with a NULL pointer at the last element, like

public StringArray(string[] array)
{
    pointer = (sbyte**)Marshal.AllocCoTaskMem((array.Length + 1) * sizeof(sbyte*));
    pointer[array.Length] = null;

    for (int i = 0; i < array.Length; i++)
    {
        fixed (char* p = array[i])
        {
            int len = Encoding.ASCII.GetByteCount(array[i]);
            pointer[i] = (sbyte*)Marshal.AllocCoTaskMem(len + 1); // Zero final byte

            Encoding.ASCII.GetBytes(p, array[i].Length, (byte*)pointer[i], len);
            pointer[i][len] = 0; // Final zero
        }
    }
}

Then the Count can be implemented like:

public int Count
{
    get
    {
        if (pointer == null)
        {
            return 0;
        }

        int count = 0;

        sbyte** pointer2 = pointer;

        while (*pointer2 != null)
        {
            count++;
            pointer2++;
        }

        return count;
    }
}

I'll add that every memory allocation is "expensive" (both in terms of space, because there is bookkeeping) and in terms of time. If all the strings in the StringArray have the same lifetime, then you could allocate a single block of memory. Clearly this is much more complex to write... But the Dispose() is much easier!

public StringArray(string[] array)
{
    int indexLength = (array.Length + 1) * sizeof(sbyte*);

    // space for null terminator at end of array
    int totalLen = indexLength;

    for (int i = 0; i < array.Length; i++)
    {
        fixed (char* p = array[i])
        {
            int len = Encoding.ASCII.GetByteCount(array[i]);
            totalLen += len + 1; // zero byte at end of string
        }
    }

    pointer = (sbyte**)Marshal.AllocCoTaskMem(totalLen);

    // last element at null
    pointer[array.Length] = null;

    // Memory at the end of the "index" space
    sbyte* pointer2 = (sbyte*)pointer + indexLength;

    // remaining space starting from pointer2
    int remainingLen = totalLen - indexLength;

    for (int i = 0; i < array.Length; i++)
    {
        fixed (char* p = array[i])
        {
            pointer[i] = pointer2;

            int len = Encoding.ASCII.GetBytes(p, array[i].Length, (byte*)pointer2, remainingLen);
            pointer2[len] = 0;

            len++; // Zero terminator

            pointer2 += len;
            remainingLen -= len;
        }
    }
}

public void Dispose()
{
    if (pointer != null)
    {
        Marshal.FreeCoTaskMem((IntPtr)pointer);
        pointer = null;
        count = 0;
    }
}
xanatos
  • 109,618
  • 12
  • 197
  • 280
  • Thanks :) I did not know about these alloc/free functions. I thought that GetBytes added the null-character. Why does the byte is preferred to the sbyte? (my C++ parameter type "const char **" has been translated to "sbyte **" in C#) I will search :p Still, I'm not sure if the Dispose method is called (at the end of the object range) if AllocCoTaskMem throws an exception :/ Edit: I've changed char * to unsigned char *. – Sylafrs Mar 30 '15 at 09:50
  • @Sylafrs (`GetBytes`) doesn't, because the zero terminator is useful only if you interop with C, not in "general" (you could prepend the length of the string to the string, like Pascal strings. No zero terminator needed). I don't know why `byte` is preferred to `sbyte`, but if I have to say the truth, when I imagine the cells of a block of memory, or the bytes of a file, I wouldn't ever think that a single byte can be negative :-) If a byte of memory/of a file is 0xFF, it's 255, not -1. Adding a sign means adding a context/a formatting to what you are handling. It is external, not intrinsic. – xanatos Mar 30 '15 at 10:05
  • Okay :) (Anyway, I don't understand why char* has been translated to sbyte*.. I thought char was, by default, unsigned) – Sylafrs Mar 30 '15 at 10:15
  • @Sylafrs http://stackoverflow.com/a/2054941/613130 For historical reasons, `char` can be both "signed" or "unsigned" (clearly not at the same time :-) It isn't the *schrodinger* char). Both ms vc++ and gcc use "signed" `char` – xanatos Mar 30 '15 at 10:17
  • "If all the strings in the StringArray have the same lifetime, then you could allocate a single block of memory" Yup, I didn't think about that, thanks :) – Sylafrs Mar 30 '15 at 10:21
  • Don't forget to call `GC.SuppressFinalize(this)` when you dispose so you don't have the finalizer run when the disposal has been successful. – Jon Hanna Mar 30 '15 at 10:23
  • @JonHanna I find it fascinating... It is correct (by the pattern), but (troll mode on) it is a premature optimization... surely if there isn't a slowdown caused by useless finalizations, then you shouldn't add it :) There is no discernible difference, other than speed, in omitting it :-) – xanatos Mar 30 '15 at 10:27
  • @Jon Hanna : since there is " if(pointer != null) " I don't know if that's necessary (it could remove a function call, maybe?) – Sylafrs Mar 30 '15 at 10:28
  • @Sylafrs In the second version yes, because `Marshal.FreeCoTaskMem((IntPtr)0)` doesn't do anything. mmmh and even in the first one you can remove it, because I set count = 0. – xanatos Mar 30 '15 at 10:31
  • @Sylafrs. It's very necessary, even ignoring the performance hit of filling the finalisation queue with objects that don't need finalising and pushing objects into a later GC generation, the fact that you had to manually check that it couldn't cause serious problems (sometimes it can) is a premature pessimissation; don't make things slower and more bug-prone before you've demonstrated that being slower and more bug-prone is necessary! – Jon Hanna Mar 30 '15 at 10:33
  • @JonHanna I understand that's a good practice, but I really can't understand how, in that case, it can be bug-prone (slower, I understand). But, anyway, I've used it :) – Sylafrs Mar 30 '15 at 10:41
  • @Sylafrs: Omitting `GC.SuppressFinalize()` won't only cause a finalizer to run needlessly after destruction is complete; it may in some cases allow a finalizer to run *while destruction is still underway*. One could prevent the latter by using `GC.KeepAlive()` instead of `GC.SuppressFinalize()`, but I can't think of any reason to do so. – supercat Mar 31 '15 at 19:28
  • @supercat Do you have a reference on this? – xanatos Mar 31 '15 at 19:29
  • @xanatos: Do a search on why `GC.KeepAlive` is required. Basically what happens is that an object becomes eligible for finalization as soon as .NET determines that no part of it (including its header or fields) will ever be accessed. If code reads a field of a finalizable object, acts upon the thing identified by that field, and never uses any part of the outer object after reading the field, the object may be eligible for finalization between the read of the field and the manipulation of the thing identified thereby. It would be rare for the GC to strike right at that exact moment, but... – supercat Mar 31 '15 at 19:37
  • ...bugs which occur rarely are the hardest to track down. A call to `GC.SuppressFinalize` will access bits in the object's header, thus ensuring the object will be kept alive until after it executes. The `GC.KeepAlive` method doesn't actually access anything, but is specially marked to prevent the GC from noticing that it doesn't. – supercat Mar 31 '15 at 19:39
  • @supercat I don't see how this could impact. If the `Dispose` is called, then the object can be finalized at any time (because all the fields are set at `null`), if the `Dispose` isn't called then the `GC.SuppressFinalize` wouldn't have been called, and so the problem would still be there by your analysis. – xanatos Mar 31 '15 at 19:45
  • @xanatos: If `Dispose` isn't called, you're right that the problem is still there (that's IMHO a good reason for non-experts to avoid writing finalizers: it makes it very hard to confirm that code doesn't have odd finalizer race conditions). I don't know that the `Dispose` method as written would have a finalizer race condition in the absence of SuppressFinalize/KeepAlive, but it's a lot easier to show that a piece of code is guarded by such a method than to ensure that no present *or future* version of the method could erroneously try to release an object in two threads simultaneously. – supercat Mar 31 '15 at 19:58
  • @supercat Are you sure an object can be finalized while you are *inside* an instance method of that object? The `this` is present and referenced until the end of the method. For example with Java GC http://stackoverflow.com/a/14111686/613130 – xanatos Mar 31 '15 at 20:00
  • @xanatos: The compiler is allowed to discard any variable--including `this`--that will never again be examined. Search for information on `GC.KeepAlive()`--discussions on that topic should clarify things. – supercat Mar 31 '15 at 20:09
  • @supercat Yep you are right on finalizers, but *this* code is working "as is". There can't be any race condition, because the finalizer can't be called until the end of the *Dispose*, because the last act of the *Dispose* is setting some fields of the object. This protect the *this* until the end of the method. The *pointer = null* can't be removed by the CLR (as can some settings of fields/variable that won't be ever read) because *pointer* must then be read by the *Dispose* called by the finalizer. – xanatos Mar 31 '15 at 20:24
  • And adding the `GC.SuppressFinalize` wouldn't remove the main race condition: program access the Pointer and the GC runs and collects it before the program uses it. That would require the class to self-pin itself with `GCHandle.Allocate` – xanatos Mar 31 '15 at 20:28