mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-02-04 10:16:17 +01:00
Rework the way GStaticPrivate data is freed
To avoid iterating threads in g_static_private_free(), defer freeing the per-thread data to thread exit. The one complication here is that it is possible for the static private index to be reused while 'old' data is still around. To deal with that case, store the 'owner' with each per-thread data node, and free old data in g_static_private_get() if the owner doesn't match. The remaining possibility that a private index could be reused by a GStaticPrivate with the same address is sufficiently unlikely that we can probably ignore it. With this change, per-thread data is now truly private again, and we can drop the lock for it as well. https://bugzilla.gnome.org/show_bug.cgi?id=660635
This commit is contained in:
parent
6a31cc66cd
commit
1a5cc98ca2
119
glib/gthread.c
119
glib/gthread.c
@ -635,31 +635,24 @@ typedef struct _GRealThread GRealThread;
|
|||||||
struct _GRealThread
|
struct _GRealThread
|
||||||
{
|
{
|
||||||
GThread thread;
|
GThread thread;
|
||||||
/* Bit 0 protects private_data. To avoid deadlocks,
|
|
||||||
* do not block while holding this (particularly on
|
|
||||||
* the g_thread lock).
|
|
||||||
*/
|
|
||||||
volatile gint private_data_lock;
|
|
||||||
GArray *private_data;
|
GArray *private_data;
|
||||||
GRealThread *next;
|
GRealThread *next;
|
||||||
gpointer retval;
|
gpointer retval;
|
||||||
GSystemThread system_thread;
|
GSystemThread system_thread;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define LOCK_PRIVATE_DATA(self) g_bit_lock (&(self)->private_data_lock, 0)
|
|
||||||
#define UNLOCK_PRIVATE_DATA(self) g_bit_unlock (&(self)->private_data_lock, 0)
|
|
||||||
|
|
||||||
/* Local Data {{{1 -------------------------------------------------------- */
|
/* Local Data {{{1 -------------------------------------------------------- */
|
||||||
|
|
||||||
gboolean g_threads_got_initialized = FALSE;
|
gboolean g_threads_got_initialized = FALSE;
|
||||||
GSystemThread zero_thread; /* This is initialized to all zero */
|
GSystemThread zero_thread; /* This is initialized to all zero */
|
||||||
GMutex g_once_mutex = G_MUTEX_INIT;
|
|
||||||
|
|
||||||
|
GMutex g_once_mutex = G_MUTEX_INIT;
|
||||||
static GCond g_once_cond = G_COND_INIT;
|
static GCond g_once_cond = G_COND_INIT;
|
||||||
|
static GSList* g_once_init_list = NULL;
|
||||||
|
|
||||||
static GPrivate g_thread_specific_private;
|
static GPrivate g_thread_specific_private;
|
||||||
static GRealThread *g_thread_all_threads = NULL;
|
static GRealThread *g_thread_all_threads = NULL;
|
||||||
static GSList *g_thread_free_indices = NULL;
|
static GSList *g_thread_free_indices = NULL;
|
||||||
static GSList* g_once_init_list = NULL;
|
|
||||||
|
|
||||||
G_LOCK_DEFINE_STATIC (g_thread);
|
G_LOCK_DEFINE_STATIC (g_thread);
|
||||||
|
|
||||||
@ -925,6 +918,7 @@ struct _GStaticPrivateNode
|
|||||||
{
|
{
|
||||||
gpointer data;
|
gpointer data;
|
||||||
GDestroyNotify destroy;
|
GDestroyNotify destroy;
|
||||||
|
GStaticPrivate *owner;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1002,15 +996,25 @@ g_static_private_get (GStaticPrivate *private_key)
|
|||||||
GArray *array;
|
GArray *array;
|
||||||
gpointer ret = NULL;
|
gpointer ret = NULL;
|
||||||
|
|
||||||
LOCK_PRIVATE_DATA (self);
|
|
||||||
|
|
||||||
array = self->private_data;
|
array = self->private_data;
|
||||||
|
|
||||||
if (array && private_key->index != 0 && private_key->index <= array->len)
|
if (array && private_key->index != 0 && private_key->index <= array->len)
|
||||||
ret = g_array_index (array, GStaticPrivateNode,
|
{
|
||||||
private_key->index - 1).data;
|
GStaticPrivateNode *node;
|
||||||
|
|
||||||
|
node = &g_array_index (array, GStaticPrivateNode, private_key->index - 1);
|
||||||
|
if (G_UNLIKELY (node->owner != private_key))
|
||||||
|
{
|
||||||
|
if (node->destroy)
|
||||||
|
node->destroy (node->data);
|
||||||
|
node->destroy = NULL;
|
||||||
|
node->data = NULL;
|
||||||
|
node->owner = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = node->data;
|
||||||
|
}
|
||||||
|
|
||||||
UNLOCK_PRIVATE_DATA (self);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1043,8 +1047,6 @@ g_static_private_set (GStaticPrivate *private_key,
|
|||||||
GArray *array;
|
GArray *array;
|
||||||
static guint next_index = 0;
|
static guint next_index = 0;
|
||||||
GStaticPrivateNode *node;
|
GStaticPrivateNode *node;
|
||||||
gpointer ddata = NULL;
|
|
||||||
GDestroyNotify ddestroy = NULL;
|
|
||||||
|
|
||||||
if (!private_key->index)
|
if (!private_key->index)
|
||||||
{
|
{
|
||||||
@ -1054,10 +1056,8 @@ g_static_private_set (GStaticPrivate *private_key,
|
|||||||
{
|
{
|
||||||
if (g_thread_free_indices)
|
if (g_thread_free_indices)
|
||||||
{
|
{
|
||||||
private_key->index =
|
private_key->index = GPOINTER_TO_UINT (g_thread_free_indices->data);
|
||||||
GPOINTER_TO_UINT (g_thread_free_indices->data);
|
g_thread_free_indices = g_slist_delete_link (g_thread_free_indices,
|
||||||
g_thread_free_indices =
|
|
||||||
g_slist_delete_link (g_thread_free_indices,
|
|
||||||
g_thread_free_indices);
|
g_thread_free_indices);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1067,8 +1067,6 @@ g_static_private_set (GStaticPrivate *private_key,
|
|||||||
G_UNLOCK (g_thread);
|
G_UNLOCK (g_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOCK_PRIVATE_DATA (self);
|
|
||||||
|
|
||||||
array = self->private_data;
|
array = self->private_data;
|
||||||
if (!array)
|
if (!array)
|
||||||
{
|
{
|
||||||
@ -1081,16 +1079,12 @@ g_static_private_set (GStaticPrivate *private_key,
|
|||||||
|
|
||||||
node = &g_array_index (array, GStaticPrivateNode, private_key->index - 1);
|
node = &g_array_index (array, GStaticPrivateNode, private_key->index - 1);
|
||||||
|
|
||||||
ddata = node->data;
|
if (node->destroy)
|
||||||
ddestroy = node->destroy;
|
node->destroy (node->data);
|
||||||
|
|
||||||
node->data = data;
|
node->data = data;
|
||||||
node->destroy = notify;
|
node->destroy = notify;
|
||||||
|
node->owner = private_key;
|
||||||
UNLOCK_PRIVATE_DATA (self);
|
|
||||||
|
|
||||||
if (ddestroy)
|
|
||||||
ddestroy (ddata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1108,8 +1102,6 @@ void
|
|||||||
g_static_private_free (GStaticPrivate *private_key)
|
g_static_private_free (GStaticPrivate *private_key)
|
||||||
{
|
{
|
||||||
guint idx = private_key->index;
|
guint idx = private_key->index;
|
||||||
GRealThread *thread, *next;
|
|
||||||
GArray *garbage = NULL;
|
|
||||||
|
|
||||||
if (!idx)
|
if (!idx)
|
||||||
return;
|
return;
|
||||||
@ -1117,67 +1109,9 @@ g_static_private_free (GStaticPrivate *private_key)
|
|||||||
private_key->index = 0;
|
private_key->index = 0;
|
||||||
|
|
||||||
G_LOCK (g_thread);
|
G_LOCK (g_thread);
|
||||||
|
|
||||||
thread = g_thread_all_threads;
|
|
||||||
|
|
||||||
for (thread = g_thread_all_threads; thread; thread = next)
|
|
||||||
{
|
|
||||||
GArray *array;
|
|
||||||
|
|
||||||
next = thread->next;
|
|
||||||
|
|
||||||
LOCK_PRIVATE_DATA (thread);
|
|
||||||
|
|
||||||
array = thread->private_data;
|
|
||||||
|
|
||||||
if (array && idx <= array->len)
|
|
||||||
{
|
|
||||||
GStaticPrivateNode *node = &g_array_index (array,
|
|
||||||
GStaticPrivateNode,
|
|
||||||
idx - 1);
|
|
||||||
gpointer ddata = node->data;
|
|
||||||
GDestroyNotify ddestroy = node->destroy;
|
|
||||||
|
|
||||||
node->data = NULL;
|
|
||||||
node->destroy = NULL;
|
|
||||||
|
|
||||||
if (ddestroy)
|
|
||||||
{
|
|
||||||
/* defer non-trivial destruction til after we've finished
|
|
||||||
* iterating, since we must continue to hold the lock */
|
|
||||||
if (garbage == NULL)
|
|
||||||
garbage = g_array_new (FALSE, TRUE,
|
|
||||||
sizeof (GStaticPrivateNode));
|
|
||||||
|
|
||||||
g_array_set_size (garbage, garbage->len + 1);
|
|
||||||
|
|
||||||
node = &g_array_index (garbage, GStaticPrivateNode,
|
|
||||||
garbage->len - 1);
|
|
||||||
node->data = ddata;
|
|
||||||
node->destroy = ddestroy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UNLOCK_PRIVATE_DATA (thread);
|
|
||||||
}
|
|
||||||
g_thread_free_indices = g_slist_prepend (g_thread_free_indices,
|
g_thread_free_indices = g_slist_prepend (g_thread_free_indices,
|
||||||
GUINT_TO_POINTER (idx));
|
GUINT_TO_POINTER (idx));
|
||||||
G_UNLOCK (g_thread);
|
G_UNLOCK (g_thread);
|
||||||
|
|
||||||
if (garbage)
|
|
||||||
{
|
|
||||||
guint i;
|
|
||||||
|
|
||||||
for (i = 0; i < garbage->len; i++)
|
|
||||||
{
|
|
||||||
GStaticPrivateNode *node;
|
|
||||||
|
|
||||||
node = &g_array_index (garbage, GStaticPrivateNode, i);
|
|
||||||
node->destroy (node->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_array_free (garbage, TRUE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* GThread {{{1 -------------------------------------------------------- */
|
/* GThread {{{1 -------------------------------------------------------- */
|
||||||
@ -1190,10 +1124,8 @@ g_thread_cleanup (gpointer data)
|
|||||||
GRealThread* thread = data;
|
GRealThread* thread = data;
|
||||||
GArray *array;
|
GArray *array;
|
||||||
|
|
||||||
LOCK_PRIVATE_DATA (thread);
|
|
||||||
array = thread->private_data;
|
array = thread->private_data;
|
||||||
thread->private_data = NULL;
|
thread->private_data = NULL;
|
||||||
UNLOCK_PRIVATE_DATA (thread);
|
|
||||||
|
|
||||||
if (array)
|
if (array)
|
||||||
{
|
{
|
||||||
@ -1201,8 +1133,7 @@ g_thread_cleanup (gpointer data)
|
|||||||
|
|
||||||
for (i = 0; i < array->len; i++ )
|
for (i = 0; i < array->len; i++ )
|
||||||
{
|
{
|
||||||
GStaticPrivateNode *node =
|
GStaticPrivateNode *node = &g_array_index (array, GStaticPrivateNode, i);
|
||||||
&g_array_index (array, GStaticPrivateNode, i);
|
|
||||||
if (node->destroy)
|
if (node->destroy)
|
||||||
node->destroy (node->data);
|
node->destroy (node->data);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user