tags:

views:

51

answers:

3

Dear skilled,

I have a class with a field of Dictionary<object, IntPtr>.

I allocate the memory dynamically, when user calls some method of my class:

IntPtr somePointer = Marshal.AllocHGlobal(/*some desired size*/);

Then I'm going to use that memory in another thread. Actually, after doing some job that thread frees allocated memory thru Marshal.FreeHGlobal and removes the appropriate key from collection. But there is some probability for this thread to crash, so I'm thinking about proper finalization.

How I can finalize that entire collection (in case when some thread has crashed and my memory still remains allocated)?

My inclination is to change IntPtr to SafeHandle. Will this help?

+1  A: 

One option would be to put your thread code in a try/finally block and have the finally block free the memory, that way if the thread crashes (I assume you mean throws an exception?) the finally block will ensure the memory is freed.

A custom SafeHandle will help if you free the memory in the Dispose method, but for this to work you will need to have removed the item from the dictionary before the thread crashes, so that there is no longer a reference to the SafeHandle via the dictionary. Otherwise the memory is only released once the dictionary is released which depends on your lifetime management of the dictionary it self.

Alternatively you might consider looking at a more exotic solution using a WeakReference, though if you can I would suggest the SafeHandle in conjunction with try/finally.

Chris Taylor
That's a good point, but:my class is performing the allocation of memory in main thread (the thread where user calls exposed method). Mentioned try-catch should be in another thread. There is some probability (actually really low) that a new thread will never start. That's why I'm thinking about finalization.
xenn_33
@xenn_33, if the thread never starts then it might be better to have a background thread thread the walks the dictionary and releases any "orphaned" allocations, so if you allocated memory for a thread and the thread never picked it up after a certain amount of time, or something similar. Are you allocating the memory then passing it to the new thread? If so, why not have the new thread request the memory that way you know the thread started and the finally block in the thread can take reponsibility for freeing the memory it asked for.
Chris Taylor
Yup, you're right about the proper place for allocation. But I was worrying regarding finalizers only for the case when my new spawned thread will be crashed due to OutOfMemory, ThreadAbort and other exceptions.
xenn_33
@xenn_33, fot ThreadAbortException the runtime will execute all finally blocks, so you should be fine on that one. In the case of OutOfMemoryExceptions, I guess at that point you would probably want to save what you can an exit the application in which case the process will be cleaned up anyway.
Chris Taylor
A: 

I think you'll find SafeHandle just calls CloseHandle when cleaning-up, which I don't think is the same thing as FreeHGlobal.

You'd probably be better adding the full Dispose/Finalizer pattern to your class which contains the collection, and have it walk through the dictionary and cleanup.

Or write a wrapper with a finalizer for the HGlobal.

Update: This might be useful - http://social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/f74b7c3c-12c4-466b-9754-82e9dea8b83e

Will Dean
Thanks for the response. >>You'd probably be better adding the full Dispose/Finalizer pattern to your class which contains the collection, and have it walk through the dictionary and cleanup. My inclination is that we cannot access managed resource (Dictionary) in finalizer (at least it is an extremely bad practice). Am I wrong?
xenn_33
Will, the link is really great, thanks a lot :)
xenn_33
You're new here, but it's the done-thing to vote-up stuff you like! Glad you liked it anyway. I don't think there's a problem enumerating a dictionary in a finalizer because the dictionary itself is not going to have been finalized itself, but anyway, a safe wrapper on the handle itself avoids the question and feels cleaner.
Will Dean
I cannot vote-up since my reputation is 6 (must be at least 15). sorry
xenn_33
+1  A: 

Writing a wrapper for the IntPtr could certainly work. But it is unnecessary if the memory should stay valid as long as the item is stored in the dictionary. If that's the case, it is much easier on the client code if you create your own directory, one that automatically frees the memory when an item is removed from the dictionary. No need for the client to call Dispose() for each item, that's always an advantage.

To do this, derive your own class from IDictionary<object, IntPtr> and IDisposable. You can simply delegate most of the method calls to a private dictionary. You'll want a custom Add() to allocate the memory, Delete to release it. And implement Dispose() and the finalizer to clean up. The code is kinda ugly though:

class MyDictionary : IDictionary<object, IntPtr>, IDisposable {
    private Dictionary<object, IntPtr> impl = new Dictionary<object, IntPtr>();

    public void Add(object key) {
        IntPtr mem = Marshal.AllocCoTaskMem(666);  // Something smarter here...
        impl.Add(key, mem);
    }
    public bool Remove(object key) {
        if (!impl.ContainsKey(key)) return false;
        Marshal.FreeCoTaskMem(impl[key]);
        return impl.Remove(key);
    }
    protected void Dispose(bool disposing) {
        foreach (IntPtr mem in impl.Values) Marshal.FreeCoTaskMem(mem);
        if (disposing) impl.Clear();
    }
    public void Dispose() { 
        Dispose(true); 
    }
    ~MyDictionary() { 
        Dispose(false); 
    }

    // Boilerplate
    public void Add(object key, IntPtr value) { throw new NotImplementedException(); }
    public void Add(KeyValuePair<object, IntPtr> item) { throw new NotImplementedException(); }
    public bool Remove(KeyValuePair<object, IntPtr> item) { throw new NotImplementedException(); }
    public bool ContainsKey(object key) { return impl.ContainsKey(key); }
    public ICollection<object> Keys { get { return impl.Keys; }}
    public bool TryGetValue(object key, out IntPtr value) { return impl.TryGetValue(key, out value); }
    public ICollection<IntPtr> Values { get {return impl.Values; }}
    public IntPtr this[object key] { get { return impl[key]; } set { impl[key] = value; } }
    public void Clear() { impl.Clear(); }
    public bool Contains(KeyValuePair<object, IntPtr> item) { return impl.Contains(item); }
    public void CopyTo(KeyValuePair<object, IntPtr>[] array, int arrayIndex) { (impl as ICollection<KeyValuePair<object, IntPtr>>).CopyTo(array, arrayIndex); }
    public int Count { get { return impl.Count; }}
    public bool IsReadOnly { get { return (impl as ICollection<KeyValuePair<object, IntPtr>>).IsReadOnly; } }
    public IEnumerator<KeyValuePair<object, IntPtr>> GetEnumerator() { return impl.GetEnumerator(); }
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return (impl as System.Collections.IEnumerable).GetEnumerator(); }
}

AllocCoTaskMem is the better allocator btw, it doesn't have the legacy baggage.

Hans Passant
Thanks for the answer. Does it really safe to access impl.Values in finalizer? I'm a bit hesitating about that...
xenn_33
Accessing members that have a finalizer is unsafe. Dictionary<> doesn't have one.
Hans Passant
In such case I could simply go over all dictionary keys in a finalizer of my class and relase the rest allocated memory. But I really haven't heard anywhere that accessing such managed stuff like Dictionary is safe in finalizers. Hence asking
xenn_33
BTW I haven't found any info regarding why AllocCoTaskMem should have been used. AFAIK Win32 stuff which I'm calling doesn't use COM heap
xenn_33
Your life is filled with FUD. Why don't you start a new thread about it if you don't trust me? More opinions you'll have to weigh for yourself though. More FUD.
Hans Passant
Hans is making a fair point somewhat rudely - but if you're worried about all this finalizer stuff, I'd recommend Jeff Richter's C#/CLR book.
Will Dean