6 Replies Latest reply on Nov 16, 2012 5:04 PM by charlie@osisoft.com

    Thread Leak from ASP.Net Web Service calling PI-SDK

    tinklerj

      Further to a  http://vcampus.osisoft.com/discussion_hall/development_with_osisoft_sdks/f/8/t/3192.aspx about PI-SDK, threading and ASP.Net:

      I have an application running in an ASP.Net Web service and using PI-SDK to read data.  To slim down a lot of code into the essentials:

      - The Web service and its worker threads run in STA apartments (not that this seems to make any difference).

      On first time initialization:

      1. a thread is run to create a ServerManager object.  This thread creates the first PISDK object but does not terminate (sleeps 10 sec at low priority and loops) as recommended in the SDK help, or at least the way I understood it.

      Code in the SDK accessing class:

       

      private static ServerManager SDKServerMan;
      private static PISDK.PISDK mySDK;
      //...
      // in the thread
      SDKServerMan = new PISDK.ServerManager();
      mySDK = SDKServerMan.PISDK;
      

       
      2. a PointList object is then created (from a worker thread) for an array of tag names (\\server\tag).  This code is called once only.  Note: all tags are on the same server.

      private PointList tags;
      //...
      string[] tagNames = new string[numoftags];
      //...
      tags = new PISDK.PointListClass();
      IPointList2 ipl = (IPointList2)tags;
      PIErrors tagErrors = ipl.AddItems((object)tagNames);
      //... check errors ...
      System.Runtime.InteropServices.Marshal.ReleaseComObject(tagErrors); 
      

       
      Each time the service method is called (atTime is a method argument) it essentially does this:

      //...
      NamedValues nvErrors;
      PointValues dataRead = tags.Data.ArcValue(atTime, RetrievalTypeConstants.rtAuto, out nvErrors);
      //... check nvErrors ...
      System.Runtime.InteropServices.Marshal.ReleaseComObject(nvErrors); 
      
      // process tag values read
      for (int tagn = 0; tagn < dataRead.Count; tagn++)
      {
           PointValue pv = dataRead[tagn + 1];
           PIPoint pipt = pv.PIPoint;
           PIValue val = pv.PIValue;
           ....
           System.Runtime.InteropServices.Marshal.ReleaseComObject(val);
           System.Runtime.InteropServices.Marshal.ReleaseComObject(pipt);
           System.Runtime.InteropServices.Marshal.ReleaseComObject(pv);
      }
      System.Runtime.InteropServices.Marshal.ReleaseComObject(dataRead);
      //...
      

       

      The problem is, I am finding that the ASP.Net worker process is leaking threads (and their associated stack at 1MB a pop).  See picture.  Debugging with Visual Studio 2010, the list of managed threads does not seem to increase, so I can only assume they are unmanaged background threads created by PI-SDK.  But why are these threads getting stuck?  The example uses 5 tags but the number seems to be irrelevant.

      I'm assuming that all PISDK objects created will be in the hierarchy pointed at by mySDK (since no other code creates PISDK objects).

      I would love to use AF SDK's Rich Data Model and leave all this COM nonsense behind.  Unfortunately I haven't time and I can't use a beta SDK in a production environment.  Anyway I won't be able to use that until it supports data fanout to a collective.

        • Re: Thread Leak from ASP.Net Web Service calling PI-SDK
          tinklerj

          Forgot to post the picture.  The blue trace is performance counter  .NET CLR LocksAndThreads(aspnet_wp)\Nr of current Logical Threads.  Green is Process(aspnet_wp)\Private Bytes (ie unmanaged heap).

           

          1768.Leak.png

            • Re: Thread Leak from ASP.Net Web Service calling PI-SDK
              hanyong

              Hi Jeremy,

               

              Just like to find out the architecture of your web service. Are you writing it like what Charlie have suggested in the previous discussion threads, having a thread pool to handle the PI SDK related tasks?

               

              You probably know by now that RCW will hold reference to a COM objects unless the reference count to the object goes to 0, after which the COM object is released. One way to verify if PI SDK COM objects are released is getting the return value from the System.Runtime.InteropServices.Marshal.ReleaseComObject method. The return value from the method gives us the number of references to the COM object through RCW. If it doesn't go down to 0, that means that some other locations is still holding reference to the COM object.

               

              Just looking at the code snippets that you have posted, seems like you have released the PointValue, PIPoint and PIValue objects at the right places. I guess you are releasing the NamedValue objects if you go through the nvErrors collection? 

                • Re: Thread Leak from ASP.Net Web Service calling PI-SDK
                  tinklerj

                  I am releasing everything I can find... what happens about intermediate/implicit objects temporarily created using the dot notation?

                   

                  Anyway it is not really leaking memory, only threads.  I can set the asp.net worker process to recycle every day but it's a bit of a kludge.  Will try checking return values.

                    • Re: Thread Leak from ASP.Net Web Service calling PI-SDK

                      1. For troubleshooting, please turn on PISDK tracing and run your application for a bit. Look for the ThreadID of ArcValue to see if it is consistent.  You may want to use the Threading.CurrentThread.Join(10); method after releases to ensure some messages are marshaled properly.

                       

                      2. For the class that is holding the ServerManager, create a wait event to trigger an ArcValue call on locally created PointList and populate a List, Dictionary or other collection with the results.

                        • Re: Thread Leak from ASP.Net Web Service calling PI-SDK
                          tinklerj

                          Thanks for your help, Charles.  I will try (1). However,  I'm not sure what is meant by "create a wait event to trigger an ArcValue call...".

                           

                          I have just noticed that the thread count keeps incrementing even when there are no Web requests being made! (i.e no code executing in the worker process). How is that possible?

                            • Re: Thread Leak from ASP.Net Web Service calling PI-SDK

                              Jeremy,

                               

                              I've been tied up, thus the delay in responding.

                               

                              PISDK tracing will give you an idea of any thread creation coming from the PISDK.  You also might try WinDbg and/or adplus to grab a snapshot of the image and look at the thread creators.

                               

                              If there are PISDK threads getting orphaned, perhaps a pool of PISDK threads (or one) may be created to service PI calls and hand the results back to the calling .NET thread.  There are likely several architectures to choose from, so you will need to discuss any constraints first.