win32: GPrivate can leak some objects
(English is not my native language so if you can't understand some part please ask me for rephrasing.)
GLib assumes that valid Win32 TLS keys (e.g. return value of TlsAlloc
) are not zero. But it is not.
The document of TlsAlloc
function says:
If the function succeeds, the return value is a TLS index.
...
The threads of the process can use the TLS index in subsequent calls to the TlsFree, TlsSetValue, or TlsGetValue functions. The value of the TLS index should be treated as an opaque value; do not assume that it is an index into a zero-based array.
But, the document of TlsSetValue
function says:
TlsSetValue was implemented with speed as the primary goal. The function performs minimal parameter validation and error checking. In particular, it succeeds if dwTlsIndex is in the range 0 through (TLS_MINIMUM_AVAILABLE – 1). It is up to the programmer to ensure that the index is valid before calling TlsGetValue.
So a valid key (called the TLS index in Win32) CAN be zero.
But, g_private_get_impl
in gthread-win32.c is:
static DWORD
g_private_get_impl (GPrivate *key)
{
DWORD impl = (DWORD) key->p;
if G_UNLIKELY (impl == 0)
{
EnterCriticalSection (&g_private_lock);
impl = (DWORD) key->p;
if (impl == 0)
{
GPrivateDestructor *destructor;
impl = TlsAlloc ();
if (impl == TLS_OUT_OF_INDEXES)
g_thread_abort (0, "TlsAlloc");
if (key->notify != NULL)
{
/* ... */
}
/* Ditto, due to the unlocked access on the fast path */
if (InterlockedCompareExchangePointer (&key->p, impl, NULL) != NULL)
g_thread_abort (0, "g_private_get_impl(2)");
}
LeaveCriticalSection (&g_private_lock);
}
return impl;
}
If TlsAlloc
successfully returns 0, then in next call, its result will be ignored and allocation will be retried. And some g_private_set
-ed object can leak along the way.