= FDO RFC 47 - Add Thread Support to FdoIDisposable !AddRef/Release = This page contains an change request (RFC) for the FDO Open Source project. More FDO RFCs can be found on the [wiki:FDORfcs RFCs] page. == Status == ||RFC Template Version||(1.0)|| ||Submission Date||Jan. 14, 2010|| ||Last Modified||Greg Boone [[Timestamp]]|| ||Author||Greg Boone|| ||RFC Status||Adopted|| ||Implementation Status||Implemented|| ||Proposed Milestone||3.5.0.0|| ||Assigned PSC guide(s)||Greg Boone|| ||'''Voting History'''|| || ||+1|| Greg, Frank, Jackie, Jason, Haris, Orest, Traian || ||+0|| || ||-0|| || ||-1|| || == Overview == The !AddRef and Release methods on FdoIDisposable are not currently thread-safe. As a consequence the m_refCount data member can get corrupted if two separate threads simultaneously call any of these methods on the same object. This is known to happen during garbage collection of Managed FDO objects. Because the managed objects implement a finalizer, they are processed by a separate GC finalizer thread. So while the GC finalizer thread is releasing ref counts on unmanaged objects, the main thread can be simultaneously modifying ref-counts on those same objects. == Proposed Solution == In !MapGuide the refcount threading issue is addressed by having the refcounted classes extend from !MgGuardDisposable, which in turn uses a mutex to limit access to the refcount member variable to one thread at a time. A similar solution will be implemented for the FDO API, with a slight variation. On Linux, a mutex will be used. On Windows, the FDO API will use the !InterlockedIncrement and !InterlockedDecrement methods. On Windows, the use of thread safe functions will always be activated in case where unmanaged FDO objects are wrapped by a Managed object wrapper. Otherwise, API callers will have the option of setting a per-object or global flag that will activate thread-safe access of the reference count member variable. Here are the detailed steps that will need to be taken to implement the solution: * Add a static flag to FdoIDisposable (Boolean) to indicate if thread locking support should be enabled for all disposable objects * Add a local flag to FdoIDisposable (Boolean) to indicate if thread locking support should be enabled for this disposable objects * Add a virtual method on FdoIDispoable that will allow callers to enable thread locking for this object. * Add a static method on FdoIDispoable that will allow callers to enable thread locking for all disposable objects. * Add thread locking calls to the implementation !AddRef and Release that will only be executed if one of the boolean flags defined above is True. * Change the Managed FDO API's MgFdoIDisposable class to enable thread locking whenever creating an FDO managed wrapper object around an FDO unmanaged object. This solution should provide thread safe access for clients using the FDO Managed API while allowing those who use the unmanaged FDO API directly to avoid the added overhead of Interlocked support. Managed API callers will experience some performance impact as outlined below. All clients will incur a larger memory allocation/overhead due to the addition of the Boolean variable. NOTE: Timing tests were made to test the impact of implementing an Interlocked strategy vs using a Mutex. The testing involved repeatedly calling !AddRef and Release on a single unmanaged FDO object. 100 million repetitions (100 million !AddRef and 100 million Release calls) were done using a release build. 3 different cases were tried: * Unguarded !AddRef and Release (as is now) * !AddRef and Release guarded by Mutex * !AddRef and Release implemented by !InterlockedIncrement and !InterlockedDecrement. The times in seconds were: * Unguarded: 0.876 sec * With Mutex: 4.034 sec * Interlocked: 2.314 sec == Alternate Solution (Jan. 15, 2010) == In !MapGuide the refcount threading issue is addressed by having the refcounted classes extend from !MgGuardDisposable, which in turn uses a mutex to limit access to the refcount member variable to one thread at a time. While this strategy would work in FDO, an alternate solution will be implemented for the FDO API that avoids the use of a mutex to control thread access. On Linux, atomic calls to atomic_inc() and atomic_dec() will be used to increment and decrement the m_reCount variable. On Windows, the API will use the !InterlockedIncrement and !InterlockedDecrement methods. == Test Plan == Unit Testing and System testing using OSGeo !MapGuide == Funding/Resources == Autodesk to provide resources / funding.