mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-03-31 12:53:07 +02:00
Rework GPrivate
- expose the structure types for GLib internal use only - avoid infinite recursion hazards by ensuring that GPrivate never calls back into any other part of GLib - substantially rework the Windows implementation so that it never holds locks, contains no arbitrary limits and doesn't waste 100*sizeof(void*) per thread We have to keep the macro hacks for the time being since some code inside libglib depends on it.
This commit is contained in:
parent
4ec6d47806
commit
b0d83576e2
@ -1610,3 +1610,6 @@ g_mutex_lock
|
|||||||
g_mutex_new
|
g_mutex_new
|
||||||
g_mutex_trylock
|
g_mutex_trylock
|
||||||
g_mutex_unlock
|
g_mutex_unlock
|
||||||
|
g_private_new
|
||||||
|
g_private_get
|
||||||
|
g_private_set
|
||||||
|
@ -27,8 +27,8 @@
|
|||||||
* GLib at ftp://ftp.gtk.org/pub/gtk/.
|
* GLib at ftp://ftp.gtk.org/pub/gtk/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* The GMutex and GCond implementations in this file are some of the
|
/* The GMutex, GCond and GPrivate implementations in this file are some
|
||||||
* lowest-level code in GLib. All other parts of GLib (messages,
|
* of the lowest-level code in GLib. All other parts of GLib (messages,
|
||||||
* memory, slices, etc) assume that they can freely use these facilities
|
* memory, slices, etc) assume that they can freely use these facilities
|
||||||
* without risking recursion.
|
* without risking recursion.
|
||||||
*
|
*
|
||||||
@ -42,6 +42,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "gthread.h"
|
#include "gthread.h"
|
||||||
|
#include "gthreadprivate.h"
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -205,6 +206,45 @@ g_cond_timedwait (GCond *cond,
|
|||||||
|
|
||||||
/* {{{1 GPrivate */
|
/* {{{1 GPrivate */
|
||||||
|
|
||||||
|
GPrivate *
|
||||||
|
(g_private_new) (GDestroyNotify notify)
|
||||||
|
{
|
||||||
|
GPrivate *key;
|
||||||
|
|
||||||
|
key = malloc (sizeof (GPrivate));
|
||||||
|
if G_UNLIKELY (key == NULL)
|
||||||
|
g_thread_abort (errno, "malloc");
|
||||||
|
g_private_init (key, notify);
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
g_private_init (GPrivate *key,
|
||||||
|
GDestroyNotify notify)
|
||||||
|
{
|
||||||
|
pthread_key_create (&key->key, notify);
|
||||||
|
}
|
||||||
|
|
||||||
|
gpointer
|
||||||
|
(g_private_get) (GPrivate *key)
|
||||||
|
{
|
||||||
|
/* quote POSIX: No errors are returned from pthread_getspecific(). */
|
||||||
|
return pthread_getspecific (key->key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
(g_private_set) (GPrivate *key,
|
||||||
|
gpointer value)
|
||||||
|
{
|
||||||
|
gint status;
|
||||||
|
|
||||||
|
if G_UNLIKELY ((status = pthread_setspecific (key->key, value)) != 0)
|
||||||
|
g_thread_abort (status, "pthread_setspecific");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* {{{1 GThread */
|
||||||
|
|
||||||
#include "glib.h"
|
#include "glib.h"
|
||||||
#include "gthreadprivate.h"
|
#include "gthreadprivate.h"
|
||||||
|
|
||||||
@ -320,37 +360,6 @@ _g_thread_impl_init(void)
|
|||||||
#endif /* HAVE_PRIORITIES */
|
#endif /* HAVE_PRIORITIES */
|
||||||
}
|
}
|
||||||
|
|
||||||
static GPrivate *
|
|
||||||
g_private_new_posix_impl (GDestroyNotify destructor)
|
|
||||||
{
|
|
||||||
GPrivate *result = (GPrivate *) g_new (pthread_key_t, 1);
|
|
||||||
posix_check_cmd (pthread_key_create ((pthread_key_t *) result, destructor));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NOTE: the functions g_private_get and g_private_set may not use
|
|
||||||
functions from gmem.c and gmessages.c */
|
|
||||||
|
|
||||||
static void
|
|
||||||
g_private_set_posix_impl (GPrivate * private_key, gpointer value)
|
|
||||||
{
|
|
||||||
if (!private_key)
|
|
||||||
return;
|
|
||||||
pthread_setspecific (*(pthread_key_t *) private_key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gpointer
|
|
||||||
g_private_get_posix_impl (GPrivate * private_key)
|
|
||||||
{
|
|
||||||
if (!private_key)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return pthread_getspecific (*(pthread_key_t *) private_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* {{{1 GThread */
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
g_thread_create_posix_impl (GThreadFunc thread_func,
|
g_thread_create_posix_impl (GThreadFunc thread_func,
|
||||||
gpointer arg,
|
gpointer arg,
|
||||||
@ -475,9 +484,9 @@ GThreadFunctions g_thread_functions_for_glib_use =
|
|||||||
g_cond_wait,
|
g_cond_wait,
|
||||||
g_cond_timed_wait,
|
g_cond_timed_wait,
|
||||||
g_cond_free,
|
g_cond_free,
|
||||||
g_private_new_posix_impl,
|
g_private_new,
|
||||||
g_private_get_posix_impl,
|
g_private_get,
|
||||||
g_private_set_posix_impl,
|
g_private_set,
|
||||||
g_thread_create_posix_impl,
|
g_thread_create_posix_impl,
|
||||||
g_thread_yield_posix_impl,
|
g_thread_yield_posix_impl,
|
||||||
g_thread_join_posix_impl,
|
g_thread_join_posix_impl,
|
||||||
|
@ -43,8 +43,8 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "gthread.h"
|
#include "gthread.h"
|
||||||
|
#include "gthreadprivate.h"
|
||||||
|
|
||||||
#define _WIN32_WINDOWS 0x0401 /* to get IsDebuggerPresent */
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include <process.h>
|
#include <process.h>
|
||||||
@ -234,6 +234,64 @@ g_cond_timed_wait (GCond *cond,
|
|||||||
|
|
||||||
/* {{{1 GPrivate */
|
/* {{{1 GPrivate */
|
||||||
|
|
||||||
|
typedef struct _GPrivateDestructor GPrivateDestructor;
|
||||||
|
|
||||||
|
struct _GPrivateDestructor
|
||||||
|
{
|
||||||
|
DWORD index;
|
||||||
|
GDestroyNotify notify;
|
||||||
|
GPrivateDestructor *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
static GPrivateDestructor * volatile g_private_destructors;
|
||||||
|
|
||||||
|
GPrivate *
|
||||||
|
(g_private_new) (GDestroyNotify notify)
|
||||||
|
{
|
||||||
|
GPrivate *key;
|
||||||
|
|
||||||
|
key = malloc (sizeof (GPrivate));
|
||||||
|
if G_UNLIKELY (key == NULL)
|
||||||
|
g_thread_abort (errno, "malloc");
|
||||||
|
g_private_init (key, notify);
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
g_private_init (GPrivate *key,
|
||||||
|
GDestroyNotify notify)
|
||||||
|
{
|
||||||
|
GPrivateDestructor *destructor;
|
||||||
|
|
||||||
|
key->index = TlsAlloc ();
|
||||||
|
|
||||||
|
destructor = malloc (sizeof (GPrivateDestructor));
|
||||||
|
if G_UNLIKELY (destructor == NULL)
|
||||||
|
g_thread_abort (errno, "malloc");
|
||||||
|
destructor->index = key->index;
|
||||||
|
destructor->notify = notify;
|
||||||
|
|
||||||
|
do
|
||||||
|
destructor->next = g_private_destructors;
|
||||||
|
while (InterlockedCompareExchangePointer (&g_private_destructors, destructor->next, destructor) != destructor->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
gpointer
|
||||||
|
(g_private_get) (GPrivate *key)
|
||||||
|
{
|
||||||
|
return TlsGetValue (key->index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
(g_private_set) (GPrivate *key,
|
||||||
|
gpointer value)
|
||||||
|
{
|
||||||
|
TlsSetValue (key->index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* {{{1 GThread */
|
||||||
|
|
||||||
#include "glib.h"
|
#include "glib.h"
|
||||||
#include "gthreadprivate.h"
|
#include "gthreadprivate.h"
|
||||||
|
|
||||||
@ -248,18 +306,9 @@ g_cond_timed_wait (GCond *cond,
|
|||||||
|
|
||||||
static DWORD g_thread_self_tls;
|
static DWORD g_thread_self_tls;
|
||||||
static DWORD g_private_tls;
|
static DWORD g_private_tls;
|
||||||
static CRITICAL_SECTION g_thread_global_spinlock;
|
|
||||||
|
|
||||||
typedef BOOL (__stdcall *GTryEnterCriticalSectionFunc) (CRITICAL_SECTION *);
|
typedef BOOL (__stdcall *GTryEnterCriticalSectionFunc) (CRITICAL_SECTION *);
|
||||||
|
|
||||||
/* As noted in the docs, GPrivate is a limited resource, here we take
|
|
||||||
* a rather low maximum to save memory, use GStaticPrivate instead. */
|
|
||||||
#define G_PRIVATE_MAX 100
|
|
||||||
|
|
||||||
static GDestroyNotify g_private_destructors[G_PRIVATE_MAX];
|
|
||||||
|
|
||||||
static guint g_private_next = 0;
|
|
||||||
|
|
||||||
typedef struct _GThreadData GThreadData;
|
typedef struct _GThreadData GThreadData;
|
||||||
struct _GThreadData
|
struct _GThreadData
|
||||||
{
|
{
|
||||||
@ -269,64 +318,6 @@ struct _GThreadData
|
|||||||
gboolean joinable;
|
gboolean joinable;
|
||||||
};
|
};
|
||||||
|
|
||||||
static GPrivate *
|
|
||||||
g_private_new_win32_impl (GDestroyNotify destructor)
|
|
||||||
{
|
|
||||||
GPrivate *result;
|
|
||||||
EnterCriticalSection (&g_thread_global_spinlock);
|
|
||||||
if (g_private_next >= G_PRIVATE_MAX)
|
|
||||||
{
|
|
||||||
char buf[100];
|
|
||||||
sprintf (buf,
|
|
||||||
"Too many GPrivate allocated. Their number is limited to %d.",
|
|
||||||
G_PRIVATE_MAX);
|
|
||||||
MessageBox (NULL, buf, NULL, MB_ICONERROR|MB_SETFOREGROUND);
|
|
||||||
if (IsDebuggerPresent ())
|
|
||||||
G_BREAKPOINT ();
|
|
||||||
abort ();
|
|
||||||
}
|
|
||||||
g_private_destructors[g_private_next] = destructor;
|
|
||||||
result = GUINT_TO_POINTER (g_private_next);
|
|
||||||
g_private_next++;
|
|
||||||
LeaveCriticalSection (&g_thread_global_spinlock);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NOTE: the functions g_private_get and g_private_set may not use
|
|
||||||
functions from gmem.c and gmessages.c */
|
|
||||||
|
|
||||||
static void
|
|
||||||
g_private_set_win32_impl (GPrivate * private_key, gpointer value)
|
|
||||||
{
|
|
||||||
gpointer* array = TlsGetValue (g_private_tls);
|
|
||||||
guint index = GPOINTER_TO_UINT (private_key);
|
|
||||||
|
|
||||||
if (index >= G_PRIVATE_MAX)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!array)
|
|
||||||
{
|
|
||||||
array = (gpointer*) calloc (G_PRIVATE_MAX, sizeof (gpointer));
|
|
||||||
TlsSetValue (g_private_tls, array);
|
|
||||||
}
|
|
||||||
|
|
||||||
array[index] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gpointer
|
|
||||||
g_private_get_win32_impl (GPrivate * private_key)
|
|
||||||
{
|
|
||||||
gpointer* array = TlsGetValue (g_private_tls);
|
|
||||||
guint index = GPOINTER_TO_UINT (private_key);
|
|
||||||
|
|
||||||
if (index >= G_PRIVATE_MAX || !array)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return array[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* {{{1 GThread */
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
g_thread_set_priority_win32_impl (gpointer thread, GThreadPriority priority)
|
g_thread_set_priority_win32_impl (gpointer thread, GThreadPriority priority)
|
||||||
@ -386,38 +377,36 @@ static void
|
|||||||
g_thread_exit_win32_impl (void)
|
g_thread_exit_win32_impl (void)
|
||||||
{
|
{
|
||||||
GThreadData *self = TlsGetValue (g_thread_self_tls);
|
GThreadData *self = TlsGetValue (g_thread_self_tls);
|
||||||
guint i, private_max;
|
gboolean dtors_called;
|
||||||
gpointer *array = TlsGetValue (g_private_tls);
|
|
||||||
|
|
||||||
EnterCriticalSection (&g_thread_global_spinlock);
|
do
|
||||||
private_max = g_private_next;
|
|
||||||
LeaveCriticalSection (&g_thread_global_spinlock);
|
|
||||||
|
|
||||||
if (array)
|
|
||||||
{
|
{
|
||||||
gboolean some_data_non_null;
|
GPrivateDestructor *dtor;
|
||||||
|
|
||||||
do {
|
/* We go by the POSIX book on this one.
|
||||||
some_data_non_null = FALSE;
|
*
|
||||||
for (i = 0; i < private_max; i++)
|
* If we call a destructor then there is a chance that some new
|
||||||
{
|
* TLS variables got set by code called in that destructor.
|
||||||
GDestroyNotify destructor = g_private_destructors[i];
|
*
|
||||||
GDestroyNotify data = array[i];
|
* Loop until nothing is left.
|
||||||
|
*/
|
||||||
|
dtors_called = FALSE;
|
||||||
|
|
||||||
if (data)
|
for (dtor = g_private_destructors; dtor; dtor = dtor->next)
|
||||||
some_data_non_null = TRUE;
|
{
|
||||||
|
gpointer value;
|
||||||
|
|
||||||
array[i] = NULL;
|
value = TlsGetValue (dtor->index);
|
||||||
|
if (value != NULL && dtor->notify != NULL)
|
||||||
if (destructor && data)
|
{
|
||||||
destructor (data);
|
/* POSIX says to clear this before the call */
|
||||||
}
|
TlsSetValue (dtor->index, NULL);
|
||||||
} while (some_data_non_null);
|
dtor->notify (value);
|
||||||
|
dtors_called = TRUE;
|
||||||
free (array);
|
}
|
||||||
|
}
|
||||||
win32_check_for_error (TlsSetValue (g_private_tls, NULL));
|
|
||||||
}
|
}
|
||||||
|
while (dtors_called);
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
@ -514,8 +503,8 @@ g_thread_join_win32_impl (gpointer thread)
|
|||||||
|
|
||||||
/* {{{1 SRWLock and CONDITION_VARIABLE emulation (for Windows XP) */
|
/* {{{1 SRWLock and CONDITION_VARIABLE emulation (for Windows XP) */
|
||||||
|
|
||||||
static DWORD g_thread_xp_waiter_tls;
|
|
||||||
static CRITICAL_SECTION g_thread_xp_lock;
|
static CRITICAL_SECTION g_thread_xp_lock;
|
||||||
|
static DWORD g_thread_xp_waiter_tls;
|
||||||
|
|
||||||
/* {{{2 GThreadWaiter utility class for CONDITION_VARIABLE emulation */
|
/* {{{2 GThreadWaiter utility class for CONDITION_VARIABLE emulation */
|
||||||
typedef struct _GThreadXpWaiter GThreadXpWaiter;
|
typedef struct _GThreadXpWaiter GThreadXpWaiter;
|
||||||
@ -807,9 +796,9 @@ GThreadFunctions g_thread_functions_for_glib_use =
|
|||||||
g_cond_wait,
|
g_cond_wait,
|
||||||
g_cond_timed_wait,
|
g_cond_timed_wait,
|
||||||
g_cond_free,
|
g_cond_free,
|
||||||
g_private_new_win32_impl, /* private thread data */
|
g_private_new, /* private thread data */
|
||||||
g_private_get_win32_impl,
|
g_private_get,
|
||||||
g_private_set_win32_impl,
|
g_private_set,
|
||||||
g_thread_create_win32_impl, /* thread */
|
g_thread_create_win32_impl, /* thread */
|
||||||
g_thread_yield_win32_impl,
|
g_thread_yield_win32_impl,
|
||||||
g_thread_join_win32_impl,
|
g_thread_join_win32_impl,
|
||||||
@ -834,7 +823,6 @@ _g_thread_impl_init (void)
|
|||||||
(g_thread_self_tls = TlsAlloc ()));
|
(g_thread_self_tls = TlsAlloc ()));
|
||||||
win32_check_for_error (TLS_OUT_OF_INDEXES !=
|
win32_check_for_error (TLS_OUT_OF_INDEXES !=
|
||||||
(g_private_tls = TlsAlloc ()));
|
(g_private_tls = TlsAlloc ()));
|
||||||
InitializeCriticalSection (&g_thread_global_spinlock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -408,6 +408,11 @@ void g_mutex_free (GMutex
|
|||||||
GCond * g_cond_new (void);
|
GCond * g_cond_new (void);
|
||||||
void g_cond_free (GCond *cond);
|
void g_cond_free (GCond *cond);
|
||||||
|
|
||||||
|
GPrivate * (g_private_new) (GDestroyNotify notify);
|
||||||
|
gpointer (g_private_get) (GPrivate *key);
|
||||||
|
void (g_private_set) (GPrivate *key,
|
||||||
|
gpointer value);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __G_THREAD_H__ */
|
#endif /* __G_THREAD_H__ */
|
||||||
|
@ -55,6 +55,18 @@ G_GNUC_INTERNAL void _g_messages_thread_init_nomessage (void);
|
|||||||
/* full fledged initializers */
|
/* full fledged initializers */
|
||||||
G_GNUC_INTERNAL void _g_thread_impl_init (void);
|
G_GNUC_INTERNAL void _g_thread_impl_init (void);
|
||||||
|
|
||||||
|
struct _GPrivate
|
||||||
|
{
|
||||||
|
#ifdef G_OS_WIN32
|
||||||
|
gint index;
|
||||||
|
#else
|
||||||
|
pthread_key_t key;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
G_GNUC_INTERNAL void g_private_init (GPrivate *key,
|
||||||
|
GDestroyNotify notify);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __G_THREADPRIVATE_H__ */
|
#endif /* __G_THREADPRIVATE_H__ */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user