[mapguide-trac] #817: Improve Dispose thread safety for managed .NET API

MapGuide Open Source trac_mapguide at osgeo.org
Mon Jan 5 17:06:28 EST 2009


#817: Improve Dispose thread safety for managed .NET API
------------------------------+---------------------------------------------
   Reporter:  waltweltonlair  |       Owner:       
       Type:  defect          |      Status:  new  
   Priority:  high            |   Milestone:  2.2  
  Component:  Web API         |     Version:  2.0.2
   Severity:  minor           |    Keywords:       
External_id:                  |  
------------------------------+---------------------------------------------
 The managed .NET API in MapGuide is essentially a wrapper around unmanaged
 objects.  Some of these unmanaged objects inherit from !MgDisposable
 instead of
 !MgGuardDisposable, and this can result in thread-unsafe behavior during
 .NET garbage collection.  A simple example illustrates this:

 {{{
 // get a managed coordinate system object from somewhere
 MgCoordinateSystem mgCoordSys = ...;

 // loop
 for (int i=0; i<1000; ++i)
 {
     // get its catalog
     MgCoordinateSystemCatalog mgCatalog = mgCoordSys.GetCatalog();

     // do something with the catalog...
 }
 }}}

 The !MgCoordinateSystem object is a wrapper around a separate unmanaged
 coordinate system object, and the latter stores an unmanaged catalog
 object.  Inside the loop, each !MgCoordinateSystemCatalog object that gets
 created is a wrapper around this same unmanaged catalog object.  The ref
 count of the unmanaged catalog object is incremented by one for each
 !MgCoordinateSystemCatalog we create.

 In this example each !MgCoordinateSystemCatalog goes out of scope at the
 end of the loop's block, and therefore becomes a candidate for garbage
 collection.  At some point the garbage collector will kick in and collect
 unused objects, and the ref count of the unmanaged catalog object is
 decremented by one for each !MgCoordinateSystemCatalog that's collected.

 By default garbage collection happens in a separate thread, and so it's
 possible that garbage collection can take place at the same time as the
 loop is being executed.  So we can have one thread (the main thread)
 executing the loop and incrementing the ref count of the unmanaged catalog
 object, and another thread (the GC thread) decrementing the ref count of
 the same object.  Since the unmanaged catalog object inherits from
 !MgDisposable, the calls to update the ref count are not thread-safe, and
 therefore the example code above can lead to data corruption and/or a
 crash.

 The bottom line is that all unmanaged objects wrapped by managed objects
 need to inherit from !MgGuardDisposable and not !MgDisposable.

 Here's the list of managed objects whose unmanaged counterparts need to
 change:
 {{{
 Web\src\DotNetApi\MgAgfReaderWriter.cs(12):public class MgAgfReaderWriter
 : MgDisposable {
 Web\src\DotNetApi\MgByteSink.cs(12):public class MgByteSink : MgDisposable
 {
 Web\src\DotNetApi\MgByteSource.cs(12):public class MgByteSource :
 MgDisposable {

 Web\src\DotNetApi\MgCoordinateSystemCatalog.cs(12):public class
 MgCoordinateSystemCatalog : MgDisposable {
 Web\src\DotNetApi\MgCoordinateSystemDictionaryUtility.cs(12):public class
 MgCoordinateSystemDictionaryUtility : MgDisposable {
 Web\src\DotNetApi\MgCoordinateSystemEnum.cs(12):public class
 MgCoordinateSystemEnum : MgDisposable {
 Web\src\DotNetApi\MgCoordinateSystemEnumInteger32.cs(12):public class
 MgCoordinateSystemEnumInteger32 : MgDisposable {
 Web\src\DotNetApi\MgCoordinateSystemFactory.cs(12):public class
 MgCoordinateSystemFactory : MgDisposable {
 Web\src\DotNetApi\MgCoordinateSystemFormatConverter.cs(12):public class
 MgCoordinateSystemFormatConverter : MgDisposable {

 Web\src\DotNetApi\MgCoordinateCollection.cs(12):public class
 MgCoordinateCollection : MgDisposable,
 System.Collections.Generic.IList<MgCoordinate> {
 Web\src\DotNetApi\MgCoordinateIterator.cs(12):public class
 MgCoordinateIterator : MgDisposable {
 Web\src\DotNetApi\MgCurvePolygonCollection.cs(12):public class
 MgCurvePolygonCollection : MgDisposable,
 System.Collections.Generic.IList<MgCurvePolygon> {
 Web\src\DotNetApi\MgCurveRingCollection.cs(12):public class
 MgCurveRingCollection : MgDisposable,
 System.Collections.Generic.IList<MgCurveRing> {
 Web\src\DotNetApi\MgCurveSegmentCollection.cs(12):public class
 MgCurveSegmentCollection : MgDisposable,
 System.Collections.Generic.IList<MgCurveSegment> {
 Web\src\DotNetApi\MgCurveStringCollection.cs(12):public class
 MgCurveStringCollection : MgDisposable,
 System.Collections.Generic.IList<MgCurveString> {
 Web\src\DotNetApi\MgGeometryCollection.cs(12):public class
 MgGeometryCollection : MgDisposable,
 System.Collections.Generic.IList<MgGeometry> {
 Web\src\DotNetApi\MgGeometryFactory.cs(12):public class MgGeometryFactory
 : MgDisposable {
 Web\src\DotNetApi\MgLinearRingCollection.cs(12):public class
 MgLinearRingCollection : MgDisposable,
 System.Collections.Generic.IList<MgLinearRing> {
 Web\src\DotNetApi\MgLineStringCollection.cs(12):public class
 MgLineStringCollection : MgDisposable,
 System.Collections.Generic.IList<MgLineString> {
 Web\src\DotNetApi\MgPointCollection.cs(12):public class MgPointCollection
 : MgDisposable,  System.Collections.Generic.IList<MgPoint> {
 Web\src\DotNetApi\MgPolygonCollection.cs(12):public class
 MgPolygonCollection : MgDisposable,
 System.Collections.Generic.IList<MgPolygon> {

 Web\src\DotNetApi\MgHttpHeader.cs(12):public class MgHttpHeader :
 MgDisposable {
 Web\src\DotNetApi\MgHttpPrimitiveValue.cs(12):public class
 MgHttpPrimitiveValue : MgDisposable {
 Web\src\DotNetApi\MgHttpRequest.cs(12):public class MgHttpRequest :
 MgDisposable {
 Web\src\DotNetApi\MgHttpRequestMetadata.cs(12):public class
 MgHttpRequestMetadata : MgDisposable {
 Web\src\DotNetApi\MgHttpRequestParam.cs(12):public class
 MgHttpRequestParam : MgDisposable {
 Web\src\DotNetApi\MgHttpResponse.cs(12):public class MgHttpResponse :
 MgDisposable {
 Web\src\DotNetApi\MgHttpResult.cs(12):public class MgHttpResult :
 MgDisposable {

 Web\src\DotNetApi\MgLayerCollection.cs(12):public class MgLayerCollection
 : MgDisposable,  System.Collections.Generic.IList<MgLayerBase> {
 Web\src\DotNetApi\MgLayerGroupCollection.cs(12):public class
 MgLayerGroupCollection : MgDisposable,
 System.Collections.Generic.IList<MgLayerGroup> {
 Web\src\DotNetApi\MgMapCollection.cs(12):public class MgMapCollection :
 MgDisposable,  System.Collections.Generic.IList<MgMapBase> {
 Web\src\DotNetApi\MgMeasure.cs(12):public class MgMeasure : MgDisposable {
 Web\src\DotNetApi\MgReadOnlyLayerCollection.cs(12):public class
 MgReadOnlyLayerCollection : MgDisposable,
 System.Collections.Generic.IList<MgLayerBase> {

 Web\src\DotNetApi\MgServerAdmin.cs(12):public class MgServerAdmin :
 MgDisposable {
 Web\src\DotNetApi\MgSite.cs(12):public class MgSite : MgDisposable {
 Web\src\DotNetApi\MgSiteConnection.cs(12):public class MgSiteConnection :
 MgDisposable {
 Web\src\DotNetApi\MgTransform.cs(12):public class MgTransform :
 MgDisposable {

 Web\src\DotNetApi\MgWebCommandCollection.cs(12):public class
 MgWebCommandCollection : MgDisposable {
 Web\src\DotNetApi\MgWebLayout.cs(12):public class MgWebLayout :
 MgDisposable {
 Web\src\DotNetApi\MgWebUiPane.cs(12):public class MgWebUiPane :
 MgDisposable {
 Web\src\DotNetApi\MgWebWidget.cs(12):public class MgWebWidget :
 MgDisposable {
 Web\src\DotNetApi\MgWebWidgetCollection.cs(12):public class
 MgWebWidgetCollection : MgDisposable {
 Web\src\DotNetApi\MgWktReaderWriter.cs(12):public class MgWktReaderWriter
 : MgDisposable {
 }}}

-- 
Ticket URL: <https://trac.osgeo.org/mapguide/ticket/817>
MapGuide Open Source <http://mapguide.osgeo.org/>
MapGuide Open Source Internals


More information about the mapguide-trac mailing list