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.

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.