none
Threads creating handle leak? RRS feed

  • Question

  •  

    Hello,

    I'm trying to debug a handle leak I have found in my code which seems to consist of event, thread, and mutant (what are those?) type handles.

     

    This happens in a region of my code where I am looping on the status of a set of devices.  In each iteration the program creates threads for handling devices, then sleeps for a predetermined duration in the monitoring thread and upon waking abort all remaining active treads.  I'm wondering if something in the way that I'm doing this is causing garbage collection to determine that these threads are not ready to be cleaned.

     

    The threads are created with the following call against a list of devices (C#):

    Code Snippet
    DeviceList.ForEach(CheckCodeload);

    Thread.Sleep(scanInterval * 1000);

     

     

    Where CheckCodeload is defined as:

    Code Snippet

    private void CheckCodeload(TargetNode t)

    {

      if (t.rev != imageRev && t.side == side)

      {

        done = false;

        UpdateCodeloadEvents(string.Format(" {0} needs codeload {1}->{2}", t.id, t.rev, imageRev));

     

        t.myLoader = new Thread(StartCodeload);

        t.myLoader.Start(t);

      }

    }

     

     

    After the thread wakes from its slumber it goes through the following routine to make sure everything gets cleaned up:

    Code Snippet

    foreach (TargetNode t in DeviceList)

    {

      if (t.myLoader != null && t.myLoader.IsAlive)

      {

        UpdateCodeloadEvents(string.Format(" {0} needs thread cleanup.", t.id));

        t.myLoader.Abort();

        t.myLoader.Join();

      }

      t.myLoader = null;

      //UpdateCodeloadEvents(string.Format(" {0} loader completed.", t.id));

    }

    UpdateCodeloadEvents(" complete.");

    GC.Collect(GC.GetGeneration(DeviceList));

     

    From the outside I run a performance counter log on the process and am able to watch the handle count slowly increase with each iteration through the loop.  I admit I'm not all that clear on how garbage collection works so perhaps there is a step I am missing here in "destroying" the thread?

     

    If I run sysInternals Handle.exe I can see a long list of handles for the process with many of the thread handles reporting thread IDs that are not visible to me in spy++. 

     

    Snippet from handle output:

      9EC: Thread        SESTool.exe(10512): 8880
      9F0: Mutant
      9F4: Mutant
      9F8: Thread        SESTool.exe(10512): 17380
      9FC: Mutant
      A00: Event
      A04: Thread        SESTool.exe(10512): 11628
      A08: Event
      A10: Event

     

    I have been able to trace some of these to being threads that were created with the above code, I'd assume it was the case for all of them.

     

    In this case it seems as though the thread has been destroyed but that there are still handles remaining?  Is there a way for me to find where in the program these handles are still referenced?

     

    Thanks,

    Devan

     

    Friday, March 21, 2008 4:14 PM

Answers

All replies

  • Could you please clarify the DeviceList and TargetNode in more details?

    In this case it seems as though the thread has been destroyed but that there are still handles remaining?
    Handles are hold by the process, not by thread.

    To troubleshoot this issue, we really need the source code and the detailed repro steps to reproduce the problem, so that we can investigate the issue in house. It is not necessary that you send out the complete source of your project. We just need a simplest sample to reproduce the problem. You can remove any confidential information or business logic from it.


    Monday, March 24, 2008 9:02 AM
  •  Feng Chen - MSFT wrote:
    Could you please clarify the DeviceList and TargetNode in more details?



    Code Snippet

            public class TargetNode
            {
                public string id = string.Empty;
                public string side = string.Empty;
                public string rev = string.Empty;
                public DateTime lastLoaded = DateTime.MinValue;
                public DateTime lastSeen = DateTime.Now;
                public uint timesLoaded = 0;
                public uint retries = 0;
                public uint retriesTot = 0;
                public uint missing = 0;
                public uint missingTot = 0;
                public uint failValidate = 0;
                public uint failValidateTot = 0;
                public Thread myLoader = null;
                public Target targ = null;
                public string lastRev = string.Empty;

                public TargetNode(Target target)
                {
                    id = string.Format("{0}{1}", target.shelfNum, target.side);
                    side = target.side;
                    rev = target.Description.Substring(target.Description.Length-4, 4);
                    targ = target;
                    myLoader = null;
                }
            }


            public class Target
            {
                    public byte HostAdapter;
                    public byte ScsiId;
                    public byte Lun;
                    public byte Type;
                    public string shelfNum;
                    public string side;
                    public string Description;
                    public string DevPath;
                    public const byte None = 0xFF;
            }

            DeviceList = new List<TargetNode>();


     Feng Chen - MSFT wrote:
    To troubleshoot this issue, we really need the source code and the detailed repro steps to reproduce the problem, so that we can investigate the issue in house. It is not necessary that you send out the complete source of your project. We just need a simplest sample to reproduce the problem. You can remove any confidential information or business logic from it.



    In the case of providing code would it be useful to provide just the source files in which this is occuring or would you be expecting a project that you can compile around this?
    Monday, March 24, 2008 4:50 PM
  • You can send a sample project and repro steps in details to my email address which can be found in my personal profile page.

    And I'll try to troubleshoot and figure out the issue.
    Tuesday, March 25, 2008 3:25 AM
  • Since this issue was very specific to the device you are testing like you said in your mail message. I can only provide you with some references with techniques troubleshooting handle leaks:
    TalkBackVideo Understanding handle leaks and How to use !htrace to find them
    Debugging handle leaks

    HowTo: Track down a handle leak in your code - trace the stacks
    Debug Tutorial Part 5: Handle Leaks

    Hope this helps!

    Thanks!
    Thursday, March 27, 2008 5:38 AM
  • FYI - I reported a similar problem to Microsoft (see https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=430646

    They acknowledged the Thread Handle Leak as a bug in the CLR and suggested as a workaround that I call GC.WaitForPendingFinalizers.  I also found that using the ThreadPool object avoided the Thread leak handle leak. 

    Here is MSFT's response:

    I am the threading developer on the Common Language Runtime (CLR) team. The problem you report is a known issue in the CLR, and we are working to fix it in a future release. The problem is that CLR thread handles (and other associated data structures) are cleaned up by the finalizer thread, which normally only runs in response to a garbage collection (GC). If many threads are created and destroyed before a GC occurs (which would be the case if there were few memory allocations in the meantime) then it has the effect of "leaking" handles and memory, althgough these will be reclaimed the next time finalization is triggered. A workaround is to periodically manually trigger finalization, through calls to GC.WaitForPendingFinalizers. As I said, we are working to correct this in a future CLR release.

    Ed
    Friday, April 10, 2009 11:13 PM