Deprecate GStaticPrivate and g_thread_foreach

This commit moves GStaticPrivate, g_thread_foreach and all
related functions and variables to gthread-deprecated.c. We
introduce some internal API to make this possible.

g_thread_foreach is not a very useful function, since there is
virtually nothing you can do with a GThread*, and implementing
it requires us to keep a list of threads around.

GStaticPrivate has been made redundant by adding comparable
capabilities to GPrivate.

https://bugzilla.gnome.org/show_bug.cgi?id=660635
This commit is contained in:
Matthias Clasen 2011-10-02 01:29:08 -04:00
parent 12287c8cc7
commit 3d4846d923
5 changed files with 370 additions and 354 deletions

View File

@ -109,6 +109,14 @@ gettime (void)
guint64 (*g_thread_gettime) (void) = gettime;
/* Internal variables {{{1 */
static GRealThread *g_thread_all_threads = NULL;
static GSList *g_thread_free_indices = NULL;
/* Protects g_thread_all_threads and g_thread_free_indices */
G_LOCK_DEFINE_STATIC (g_thread);
/* Misc. GThread functions {{{1 */
/**
@ -186,6 +194,88 @@ g_thread_create_full (GThreadFunc func,
return g_thread_new_internal (NULL, func, data, joinable, stack_size, TRUE, error);
}
/**
* g_thread_foreach:
* @thread_func: function to call for all #GThread structures
* @user_data: second argument to @thread_func
*
* Call @thread_func on all #GThreads that have been
* created with g_thread_create().
*
* Note that threads may decide to exit while @thread_func is
* running, so without intimate knowledge about the lifetime of
* foreign threads, @thread_func shouldn't access the GThread*
* pointer passed in as first argument. However, @thread_func will
* not be called for threads which are known to have exited already.
*
* Due to thread lifetime checks, this function has an execution complexity
* which is quadratic in the number of existing threads.
*
* Since: 2.10
*
* Deprecated:2.32: There are not very many interesting things you can
* do with a #GThread, except comparing it with one that was returned
* from g_thread_create(). There are better ways to find out if your
* thread is still alive.
*/
void
g_thread_foreach (GFunc thread_func,
gpointer user_data)
{
GSList *slist = NULL;
GRealThread *thread;
g_return_if_fail (thread_func != NULL);
/* snapshot the list of threads for iteration */
G_LOCK (g_thread);
for (thread = g_thread_all_threads; thread; thread = thread->next)
slist = g_slist_prepend (slist, thread);
G_UNLOCK (g_thread);
/* walk the list, skipping non-existent threads */
while (slist)
{
GSList *node = slist;
slist = node->next;
/* check whether the current thread still exists */
G_LOCK (g_thread);
for (thread = g_thread_all_threads; thread; thread = thread->next)
if (thread == node->data)
break;
G_UNLOCK (g_thread);
if (thread)
thread_func (thread, user_data);
g_slist_free_1 (node);
}
}
void
g_enumerable_thread_add (GRealThread *thread)
{
G_LOCK (g_thread);
thread->next = g_thread_all_threads;
g_thread_all_threads = thread;
G_UNLOCK (g_thread);
}
void
g_enumerable_thread_remove (GRealThread *thread)
{
GRealThread *t, *p;
G_LOCK (g_thread);
for (t = g_thread_all_threads, p = NULL; t; p = t, t = t->next)
{
if (t == thread)
{
if (p)
p->next = t->next;
else
g_thread_all_threads = t->next;
break;
}
}
G_UNLOCK (g_thread);
}
/* GStaticMutex {{{1 ------------------------------------------------------ */
/**
@ -1029,5 +1119,237 @@ g_private_new (GDestroyNotify notify)
return key;
}
/* Epilogue {{{1 */
/* {{{1 GStaticPrivate */
typedef struct _GStaticPrivateNode GStaticPrivateNode;
struct _GStaticPrivateNode
{
gpointer data;
GDestroyNotify destroy;
GStaticPrivate *owner;
};
/**
* GStaticPrivate:
*
* A #GStaticPrivate works almost like a #GPrivate, but it has one
* significant advantage. It doesn't need to be created at run-time
* like a #GPrivate, but can be defined at compile-time. This is
* similar to the difference between #GMutex and #GStaticMutex. Now
* look at our <function>give_me_next_number()</function> example with
* #GStaticPrivate:
*
* <example>
* <title>Using GStaticPrivate for per-thread data</title>
* <programlisting>
* int
* give_me_next_number (<!-- -->)
* {
* static GStaticPrivate current_number_key = G_STATIC_PRIVATE_INIT;
* int *current_number = g_static_private_get (&amp;current_number_key);
*
* if (!current_number)
* {
* current_number = g_new (int,1);
* *current_number = 0;
* g_static_private_set (&amp;current_number_key, current_number, g_free);
* }
*
* *current_number = calc_next_number (*current_number);
*
* return *current_number;
* }
* </programlisting>
* </example>
*/
/**
* G_STATIC_PRIVATE_INIT:
*
* Every #GStaticPrivate must be initialized with this macro, before it
* can be used.
*
* |[
* GStaticPrivate my_private = G_STATIC_PRIVATE_INIT;
* ]|
*/
/**
* g_static_private_init:
* @private_key: a #GStaticPrivate to be initialized
*
* Initializes @private_key. Alternatively you can initialize it with
* #G_STATIC_PRIVATE_INIT.
*/
void
g_static_private_init (GStaticPrivate *private_key)
{
private_key->index = 0;
}
/**
* g_static_private_get:
* @private_key: a #GStaticPrivate
*
* Works like g_private_get() only for a #GStaticPrivate.
*
* This function works even if g_thread_init() has not yet been called.
*
* Returns: the corresponding pointer
*/
gpointer
g_static_private_get (GStaticPrivate *private_key)
{
GRealThread *self = (GRealThread*) g_thread_self ();
GArray *array;
gpointer ret = NULL;
array = self->private_data;
if (array && private_key->index != 0 && private_key->index <= array->len)
{
GStaticPrivateNode *node;
node = &g_array_index (array, GStaticPrivateNode, private_key->index - 1);
/* Deal with the possibility that the GStaticPrivate which used
* to have this index got freed and the index got allocated to
* a new one. In this case, the data in the node is stale, so
* free it and return NULL.
*/
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;
}
return ret;
}
/**
* g_static_private_set:
* @private_key: a #GStaticPrivate
* @data: the new pointer
* @notify: a function to be called with the pointer whenever the
* current thread ends or sets this pointer again
*
* Sets the pointer keyed to @private_key for the current thread and
* the function @notify to be called with that pointer (%NULL or
* non-%NULL), whenever the pointer is set again or whenever the
* current thread ends.
*
* This function works even if g_thread_init() has not yet been called.
* If g_thread_init() is called later, the @data keyed to @private_key
* will be inherited only by the main thread, i.e. the one that called
* g_thread_init().
*
* <note><para>@notify is used quite differently from @destructor in
* g_private_new().</para></note>
*/
void
g_static_private_set (GStaticPrivate *private_key,
gpointer data,
GDestroyNotify notify)
{
GRealThread *self = (GRealThread*) g_thread_self ();
GArray *array;
static guint next_index = 0;
GStaticPrivateNode *node;
if (!private_key->index)
{
G_LOCK (g_thread);
if (!private_key->index)
{
if (g_thread_free_indices)
{
private_key->index = GPOINTER_TO_UINT (g_thread_free_indices->data);
g_thread_free_indices = g_slist_delete_link (g_thread_free_indices,
g_thread_free_indices);
}
else
private_key->index = ++next_index;
}
G_UNLOCK (g_thread);
}
array = self->private_data;
if (!array)
{
array = g_array_new (FALSE, TRUE, sizeof (GStaticPrivateNode));
self->private_data = array;
}
if (private_key->index > array->len)
g_array_set_size (array, private_key->index);
node = &g_array_index (array, GStaticPrivateNode, private_key->index - 1);
if (node->destroy)
node->destroy (node->data);
node->data = data;
node->destroy = notify;
node->owner = private_key;
}
/**
* g_static_private_free:
* @private_key: a #GStaticPrivate to be freed
*
* Releases all resources allocated to @private_key.
*
* You don't have to call this functions for a #GStaticPrivate with an
* unbounded lifetime, i.e. objects declared 'static', but if you have
* a #GStaticPrivate as a member of a structure and the structure is
* freed, you should also free the #GStaticPrivate.
*/
void
g_static_private_free (GStaticPrivate *private_key)
{
guint idx = private_key->index;
if (!idx)
return;
private_key->index = 0;
/* Freeing the per-thread data is deferred to either the
* thread end or the next g_static_private_get() call for
* the same index.
*/
G_LOCK (g_thread);
g_thread_free_indices = g_slist_prepend (g_thread_free_indices,
GUINT_TO_POINTER (idx));
G_UNLOCK (g_thread);
}
void
g_static_private_cleanup (GRealThread *thread)
{
GArray *array;
array = thread->private_data;
thread->private_data = NULL;
if (array)
{
guint i;
for (i = 0; i < array->len; i++ )
{
GStaticPrivateNode *node = &g_array_index (array, GStaticPrivateNode, i);
if (node->destroy)
node->destroy (node->data);
}
g_array_free (array, TRUE);
}
}
/* {{{1 Epilogue */
/* vim: set foldmethod=marker: */

View File

@ -114,6 +114,9 @@ GThread* g_thread_create_full (GThreadFunc func,
void g_thread_set_priority (GThread *thread,
GThreadPriority priority);
void g_thread_foreach (GFunc thread_func,
gpointer user_data);
#ifdef G_OS_WIN32
typedef GMutex * GStaticMutex;
#define G_STATIC_MUTEX_INIT NULL
@ -179,9 +182,22 @@ gboolean g_static_rw_lock_writer_trylock (GStaticRWLock* lock);
void g_static_rw_lock_writer_unlock (GStaticRWLock* lock);
void g_static_rw_lock_free (GStaticRWLock* lock);
GPrivate * g_private_new (GDestroyNotify notify);
struct _GStaticPrivate
{
/*< private >*/
guint index;
};
#define G_STATIC_PRIVATE_INIT { 0 }
void g_static_private_init (GStaticPrivate *private_key);
gpointer g_static_private_get (GStaticPrivate *private_key);
void g_static_private_set (GStaticPrivate *private_key,
gpointer data,
GDestroyNotify notify);
void g_static_private_free (GStaticPrivate *private_key);
G_END_DECLS
#endif /* __G_DEPRECATED_THREAD_H__ */

View File

@ -42,7 +42,6 @@
#include "gthread.h"
#include "gthreadprivate.h"
#include "deprecated/gthread.h"
#include <string.h>
@ -57,9 +56,7 @@
#include <windows.h>
#endif /* G_OS_WIN32 */
#include "garray.h"
#include "gslice.h"
#include "gslist.h"
#include "gtestutils.h"
/**
@ -576,20 +573,6 @@ g_thread_error_quark (void)
return g_quark_from_static_string ("g_thread_error");
}
/* Miscellaneous Structures {{{1 ------------------------------------------ */
typedef struct _GRealThread GRealThread;
struct _GRealThread
{
GThread thread;
GArray *private_data;
GRealThread *next;
const gchar *name;
gboolean enumerable;
gpointer retval;
GSystemThread system_thread;
};
/* Local Data {{{1 -------------------------------------------------------- */
gboolean g_threads_got_initialized = FALSE;
@ -601,11 +584,8 @@ static GSList *g_once_init_list = NULL;
static void g_thread_cleanup (gpointer data);
static GPrivate g_thread_specific_private = G_PRIVATE_INIT (g_thread_cleanup);
static GRealThread *g_thread_all_threads = NULL;
static GSList *g_thread_free_indices = NULL;
/* Protects g_thread_all_threads and g_thread_free_indices */
G_LOCK_DEFINE_STATIC (g_thread);
G_LOCK_DEFINE_STATIC (g_thread_new);
/* Initialisation {{{1 ---------------------------------------------------- */
@ -859,219 +839,6 @@ g_once_init_leave (volatile gsize *value_location,
g_mutex_unlock (&g_once_mutex);
}
/* GStaticPrivate {{{1 ---------------------------------------------------- */
typedef struct _GStaticPrivateNode GStaticPrivateNode;
struct _GStaticPrivateNode
{
gpointer data;
GDestroyNotify destroy;
GStaticPrivate *owner;
};
/**
* GStaticPrivate:
*
* A #GStaticPrivate works almost like a #GPrivate, but it has one
* significant advantage. It doesn't need to be created at run-time
* like a #GPrivate, but can be defined at compile-time. This is
* similar to the difference between #GMutex and #GStaticMutex. Now
* look at our <function>give_me_next_number()</function> example with
* #GStaticPrivate:
*
* <example>
* <title>Using GStaticPrivate for per-thread data</title>
* <programlisting>
* int
* give_me_next_number (<!-- -->)
* {
* static GStaticPrivate current_number_key = G_STATIC_PRIVATE_INIT;
* int *current_number = g_static_private_get (&amp;current_number_key);
*
* if (!current_number)
* {
* current_number = g_new (int,1);
* *current_number = 0;
* g_static_private_set (&amp;current_number_key, current_number, g_free);
* }
*
* *current_number = calc_next_number (*current_number);
*
* return *current_number;
* }
* </programlisting>
* </example>
*/
/**
* G_STATIC_PRIVATE_INIT:
*
* Every #GStaticPrivate must be initialized with this macro, before it
* can be used.
*
* |[
* GStaticPrivate my_private = G_STATIC_PRIVATE_INIT;
* ]|
*/
/**
* g_static_private_init:
* @private_key: a #GStaticPrivate to be initialized
*
* Initializes @private_key. Alternatively you can initialize it with
* #G_STATIC_PRIVATE_INIT.
*/
void
g_static_private_init (GStaticPrivate *private_key)
{
private_key->index = 0;
}
/**
* g_static_private_get:
* @private_key: a #GStaticPrivate
*
* Works like g_private_get() only for a #GStaticPrivate.
*
* This function works even if g_thread_init() has not yet been called.
*
* Returns: the corresponding pointer
*/
gpointer
g_static_private_get (GStaticPrivate *private_key)
{
GRealThread *self = (GRealThread*) g_thread_self ();
GArray *array;
gpointer ret = NULL;
array = self->private_data;
if (array && private_key->index != 0 && private_key->index <= array->len)
{
GStaticPrivateNode *node;
node = &g_array_index (array, GStaticPrivateNode, private_key->index - 1);
/* Deal with the possibility that the GStaticPrivate which used
* to have this index got freed and the index got allocated to
* a new one. In this case, the data in the node is stale, so
* free it and return NULL.
*/
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;
}
return ret;
}
/**
* g_static_private_set:
* @private_key: a #GStaticPrivate
* @data: the new pointer
* @notify: a function to be called with the pointer whenever the
* current thread ends or sets this pointer again
*
* Sets the pointer keyed to @private_key for the current thread and
* the function @notify to be called with that pointer (%NULL or
* non-%NULL), whenever the pointer is set again or whenever the
* current thread ends.
*
* This function works even if g_thread_init() has not yet been called.
* If g_thread_init() is called later, the @data keyed to @private_key
* will be inherited only by the main thread, i.e. the one that called
* g_thread_init().
*
* <note><para>@notify is used quite differently from @destructor in
* g_private_new().</para></note>
*/
void
g_static_private_set (GStaticPrivate *private_key,
gpointer data,
GDestroyNotify notify)
{
GRealThread *self = (GRealThread*) g_thread_self ();
GArray *array;
static guint next_index = 0;
GStaticPrivateNode *node;
if (!private_key->index)
{
G_LOCK (g_thread);
if (!private_key->index)
{
if (g_thread_free_indices)
{
private_key->index = GPOINTER_TO_UINT (g_thread_free_indices->data);
g_thread_free_indices = g_slist_delete_link (g_thread_free_indices,
g_thread_free_indices);
}
else
private_key->index = ++next_index;
}
G_UNLOCK (g_thread);
}
array = self->private_data;
if (!array)
{
array = g_array_new (FALSE, TRUE, sizeof (GStaticPrivateNode));
self->private_data = array;
}
if (private_key->index > array->len)
g_array_set_size (array, private_key->index);
node = &g_array_index (array, GStaticPrivateNode, private_key->index - 1);
if (node->destroy)
node->destroy (node->data);
node->data = data;
node->destroy = notify;
node->owner = private_key;
}
/**
* g_static_private_free:
* @private_key: a #GStaticPrivate to be freed
*
* Releases all resources allocated to @private_key.
*
* You don't have to call this functions for a #GStaticPrivate with an
* unbounded lifetime, i.e. objects declared 'static', but if you have
* a #GStaticPrivate as a member of a structure and the structure is
* freed, you should also free the #GStaticPrivate.
*/
void
g_static_private_free (GStaticPrivate *private_key)
{
guint idx = private_key->index;
if (!idx)
return;
private_key->index = 0;
/* Freeing the per-thread data is deferred to either the
* thread end or the next g_static_private_get() call for
* the same index.
*/
G_LOCK (g_thread);
g_thread_free_indices = g_slist_prepend (g_thread_free_indices,
GUINT_TO_POINTER (idx));
G_UNLOCK (g_thread);
}
/* GThread {{{1 -------------------------------------------------------- */
static void
@ -1080,23 +847,8 @@ g_thread_cleanup (gpointer data)
if (data)
{
GRealThread* thread = data;
GArray *array;
array = thread->private_data;
thread->private_data = NULL;
if (array)
{
guint i;
for (i = 0; i < array->len; i++ )
{
GStaticPrivateNode *node = &g_array_index (array, GStaticPrivateNode, i);
if (node->destroy)
node->destroy (node->data);
}
g_array_free (array, TRUE);
}
g_static_private_cleanup (thread);
/* We only free the thread structure if it isn't joinable.
* If it is, the structure is freed in g_thread_join()
@ -1104,23 +856,8 @@ g_thread_cleanup (gpointer data)
if (!thread->thread.joinable)
{
if (thread->enumerable)
{
GRealThread *t, *p;
g_enumerable_thread_remove (thread);
G_LOCK (g_thread);
for (t = g_thread_all_threads, p = NULL; t; p = t, t = t->next)
{
if (t == thread)
{
if (p)
p->next = t->next;
else
g_thread_all_threads = t->next;
break;
}
}
G_UNLOCK (g_thread);
}
/* Just to make sure, this isn't used any more */
g_system_thread_assign (thread->system_thread, zero_thread);
g_free (thread);
@ -1144,8 +881,8 @@ g_thread_create_proxy (gpointer data)
/* The lock makes sure that thread->system_thread is written,
* before thread->thread.func is called. See g_thread_create().
*/
G_LOCK (g_thread);
G_UNLOCK (g_thread);
G_LOCK (g_thread_new);
G_UNLOCK (g_thread_new);
thread->retval = thread->thread.func (thread->thread.data);
@ -1260,16 +997,13 @@ g_thread_new_internal (const gchar *name,
result->private_data = NULL;
result->enumerable = enumerable;
result->name = name;
G_LOCK (g_thread);
G_LOCK (g_thread_new);
g_system_thread_create (g_thread_create_proxy, result,
stack_size, joinable,
&result->system_thread, &local_error);
if (enumerable && !local_error)
{
result->next = g_thread_all_threads;
g_thread_all_threads = result;
}
G_UNLOCK (g_thread);
g_enumerable_thread_add (result);
G_UNLOCK (g_thread_new);
if (local_error)
{
@ -1328,7 +1062,6 @@ gpointer
g_thread_join (GThread *thread)
{
GRealThread *real = (GRealThread*) thread;
GRealThread *p, *t;
gpointer retval;
g_return_val_if_fail (thread, NULL);
@ -1340,21 +1073,8 @@ g_thread_join (GThread *thread)
retval = real->retval;
if (real->enumerable)
{
G_LOCK (g_thread);
for (t = g_thread_all_threads, p = NULL; t; p = t, t = t->next)
{
if (t == real)
{
if (p)
p->next = t->next;
else
g_thread_all_threads = t->next;
break;
}
}
G_UNLOCK (g_thread);
}
g_enumerable_thread_remove (real);
/* Just to make sure, this isn't used any more */
thread->joinable = 0;
g_system_thread_assign (real->system_thread, zero_thread);
@ -1402,52 +1122,6 @@ g_thread_self (void)
return (GThread*)thread;
}
/**
* g_thread_foreach:
* @thread_func: function to call for all #GThread structures
* @user_data: second argument to @thread_func
*
* Call @thread_func on all existing #GThread structures.
* Note that threads may decide to exit while @thread_func is
* running, so without intimate knowledge about the lifetime of
* foreign threads, @thread_func shouldn't access the GThread*
* pointer passed in as first argument. However, @thread_func will
* not be called for threads which are known to have exited already.
*
* Due to thread lifetime checks, this function has an execution complexity
* which is quadratic in the number of existing threads.
*
* Since: 2.10
*/
void
g_thread_foreach (GFunc thread_func,
gpointer user_data)
{
GSList *slist = NULL;
GRealThread *thread;
g_return_if_fail (thread_func != NULL);
/* snapshot the list of threads for iteration */
G_LOCK (g_thread);
for (thread = g_thread_all_threads; thread; thread = thread->next)
slist = g_slist_prepend (slist, thread);
G_UNLOCK (g_thread);
/* walk the list, skipping non-existent threads */
while (slist)
{
GSList *node = slist;
slist = node->next;
/* check whether the current thread still exists */
G_LOCK (g_thread);
for (thread = g_thread_all_threads; thread; thread = thread->next)
if (thread == node->data)
break;
G_UNLOCK (g_thread);
if (thread)
thread_func (thread, user_data);
g_slist_free_1 (node);
}
}
/* GMutex {{{1 ------------------------------------------------------ */
/**

View File

@ -152,22 +152,6 @@ void g_thread_exit (gpointer retval);
gpointer g_thread_join (GThread *thread);
void g_thread_yield (void);
void g_thread_foreach (GFunc thread_func,
gpointer user_data);
struct _GStaticPrivate
{
/*< private >*/
guint index;
};
#define G_STATIC_PRIVATE_INIT { 0 }
void g_static_private_init (GStaticPrivate *private_key);
gpointer g_static_private_get (GStaticPrivate *private_key);
void g_static_private_set (GStaticPrivate *private_key,
gpointer data,
GDestroyNotify notify);
void g_static_private_free (GStaticPrivate *private_key);
typedef enum
{
G_ONCE_STATUS_NOTCALLED,

View File

@ -23,6 +23,10 @@
#ifndef __G_THREADPRIVATE_H__
#define __G_THREADPRIVATE_H__
#include "deprecated/gthread.h"
#include "garray.h"
#include "gslist.h"
G_BEGIN_DECLS
/* System thread identifier comparison and assignment */
@ -56,9 +60,25 @@ G_GNUC_INTERNAL GThread *g_thread_new_internal (const gchar *name,
gboolean enumerable,
GError **error);
typedef struct _GRealThread GRealThread;
struct _GRealThread
{
GThread thread;
GArray *private_data;
GRealThread *next;
const gchar *name;
gboolean enumerable;
gpointer retval;
GSystemThread system_thread;
};
G_GNUC_INTERNAL GSystemThread zero_thread;
G_GNUC_INTERNAL GMutex g_once_mutex;
G_GNUC_INTERNAL void g_static_private_cleanup (GRealThread *thread);
G_GNUC_INTERNAL void g_enumerable_thread_add (GRealThread *thread);
G_GNUC_INTERNAL void g_enumerable_thread_remove (GRealThread *thread);
/* Is called from gthread/gthread-impl.c */
void g_thread_init_glib (void);