#include "gthreadprivate.h"
#include "deprecated/gthread.h"
-#include "gslice.h"
-#include "gmain.h"
+#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#include <windows.h>
#endif /* G_OS_WIN32 */
-#include <string.h>
-
#include "garray.h"
-#include "gbitlock.h"
+#include "gslice.h"
#include "gslist.h"
#include "gtestutils.h"
-#include "gtimer.h"
/**
* SECTION:threads
* Since: 2.32
*/
-/* GPrivate Documentation {{{1 --------------------------------------- */
-
-/**
- * GPrivate:
- *
- * <note><para>
- * #GStaticPrivate is a better choice for most uses.
- * </para></note>
- *
- * The #GPrivate struct is an opaque data structure to represent a
- * thread private data key. Threads can thereby obtain and set a
- * pointer which is private to the current thread. Take our
- * <function>give_me_next_number(<!-- -->)</function> example from
- * above. Suppose we don't want <literal>current_number</literal> to be
- * shared between the threads, but instead to be private to each thread.
- * This can be done as follows:
- *
- * <example>
- * <title>Using GPrivate for per-thread data</title>
- * <programlisting>
- * GPrivate* current_number_key = NULL; /<!-- -->* Must be initialized somewhere
- * with g_private_new (g_free); *<!-- -->/
- *
- * int
- * give_me_next_number (void)
- * {
- * int *current_number = g_private_get (current_number_key);
- *
- * if (!current_number)
- * {
- * current_number = g_new (int, 1);
- * *current_number = 0;
- * g_private_set (current_number_key, current_number);
- * }
- *
- * *current_number = calc_next_number (*current_number);
- *
- * return *current_number;
- * }
- * </programlisting>
- * </example>
- *
- * Here the pointer belonging to the key
- * <literal>current_number_key</literal> is read. If it is %NULL, it has
- * not been set yet. Then get memory for an integer value, assign this
- * memory to the pointer and write the pointer back. Now we have an
- * integer value that is private to the current thread.
- *
- * The #GPrivate struct should only be accessed via the
- * <function>g_private_</function> functions.
- */
-
/* GThread Documentation {{{1 ---------------------------------------- */
/**
struct _GRealThread
{
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;
GRealThread *next;
+ const gchar *name;
gpointer retval;
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 -------------------------------------------------------- */
gboolean g_threads_got_initialized = FALSE;
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 GPrivate g_thread_specific_private;
+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;
-static GSList* g_once_init_list = NULL;
+static GSList *g_thread_free_indices = NULL;
+/* Protects g_thread_all_threads and g_thread_free_indices */
G_LOCK_DEFINE_STATIC (g_thread);
/* Initialisation {{{1 ---------------------------------------------------- */
* having to link with the thread libraries.</para></note>
*/
-static void g_thread_cleanup (gpointer data);
-
void
g_thread_init_glib (void)
{
/* setup the basic threading system */
g_threads_got_initialized = TRUE;
- g_private_init (&g_thread_specific_private, g_thread_cleanup);
g_private_set (&g_thread_specific_private, main_thread);
g_system_thread_self (&main_thread->system_thread);
typedef struct _GStaticPrivateNode GStaticPrivateNode;
struct _GStaticPrivateNode
{
- gpointer data;
- GDestroyNotify destroy;
+ gpointer data;
+ GDestroyNotify destroy;
+ GStaticPrivate *owner;
};
/**
GArray *array;
gpointer ret = NULL;
- LOCK_PRIVATE_DATA (self);
-
array = self->private_data;
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);
+
+ /* 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;
+ }
- UNLOCK_PRIVATE_DATA (self);
return ret;
}
*/
void
g_static_private_set (GStaticPrivate *private_key,
- gpointer data,
- GDestroyNotify notify)
+ gpointer data,
+ GDestroyNotify notify)
{
GRealThread *self = (GRealThread*) g_thread_self ();
GArray *array;
static guint next_index = 0;
GStaticPrivateNode *node;
- gpointer ddata = NULL;
- GDestroyNotify ddestroy = NULL;
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;
- }
+ {
+ 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);
}
- LOCK_PRIVATE_DATA (self);
-
array = self->private_data;
if (!array)
{
node = &g_array_index (array, GStaticPrivateNode, private_key->index - 1);
- ddata = node->data;
- ddestroy = node->destroy;
+ if (node->destroy)
+ node->destroy (node->data);
node->data = data;
node->destroy = notify;
-
- UNLOCK_PRIVATE_DATA (self);
-
- if (ddestroy)
- ddestroy (ddata);
+ node->owner = private_key;
}
/**
g_static_private_free (GStaticPrivate *private_key)
{
guint idx = private_key->index;
- GRealThread *thread, *next;
- GArray *garbage = NULL;
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);
-
- 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,
- GUINT_TO_POINTER (idx));
+ GUINT_TO_POINTER (idx));
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 -------------------------------------------------------- */
GRealThread* thread = data;
GArray *array;
- LOCK_PRIVATE_DATA (thread);
array = thread->private_data;
thread->private_data = NULL;
- UNLOCK_PRIVATE_DATA (thread);
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);
- }
+ {
+ 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);
+ }
/* We only free the thread structure if it isn't joinable.
* If it is, the structure is freed in g_thread_join()
*/
if (!thread->thread.joinable)
- {
- 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);
-
- /* Just to make sure, this isn't used any more */
- g_system_thread_assign (thread->system_thread, zero_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);
+ /* Just to make sure, this isn't used any more */
+ g_system_thread_assign (thread->system_thread, zero_thread);
g_free (thread);
- }
+ }
}
}
}
/**
- * g_thread_create:
+ * g_thread_new:
+ * @name: a name for the new thread
* @func: a function to execute in the new thread
* @data: an argument to supply to the new thread
* @joinable: should this thread be joinable?
- * @error: return location for error, or %NULL
+ * @error: return location for error
*
* This function creates a new thread.
*
+ * The @name can be useful for discriminating threads in
+ * a debugger. Some systems restrict the length of @name to
+ * 16 bytes.
+ *
* If @joinable is %TRUE, you can wait for this threads termination
* calling g_thread_join(). Otherwise the thread will just disappear
* when it terminates.
* The error is set, if and only if the function returns %NULL.
*
* Returns: the new #GThread on success
+ *
+ * Since: 2.32
*/
GThread *
-g_thread_create (GThreadFunc func,
- gpointer data,
- gboolean joinable,
- GError **error)
+g_thread_new (const gchar *name,
+ GThreadFunc func,
+ gpointer data,
+ gboolean joinable,
+ GError **error)
{
- return g_thread_create_with_stack_size (func, data, joinable, 0, error);
+ return g_thread_new_full (name, func, data, joinable, 0, error);
}
/**
- * g_thread_create_with_stack_size:
+ * g_thread_new_full:
+ * @name: a name for the new thread
* @func: a function to execute in the new thread
* @data: an argument to supply to the new thread
* @joinable: should this thread be joinable?
* @stack_size: a stack size for the new thread
* @error: return location for error
*
- * This function creates a new thread. If the underlying thread
- * implementation supports it, the thread gets a stack size of
- * @stack_size or the default value for the current platform, if
- * @stack_size is 0.
+ * This function creates a new thread.
+ *
+ * The @name can be useful for discriminating threads in
+ * a debugger. Some systems restrict the length of @name to
+ * 16 bytes.
+ *
+ * If the underlying thread implementation supports it, the thread
+ * gets a stack size of @stack_size or the default value for the
+ * current platform, if @stack_size is 0.
*
* If @joinable is %TRUE, you can wait for this threads termination
* calling g_thread_join(). Otherwise the thread will just disappear
* @error can be %NULL to ignore errors, or non-%NULL to report errors.
* The error is set, if and only if the function returns %NULL.
*
- * <note><para>Only use g_thread_create_with_stack_size() if you
- * really can't use g_thread_create() instead. g_thread_create()
+ * <note><para>Only use a non-zero @stack_size if you
+ * really can't use the default instead. g_thread_new()
* does not take @stack_size, as it should only be used in cases
* in which it is unavoidable.</para></note>
*
*
* Since: 2.32
*/
-GThread*
-g_thread_create_with_stack_size (GThreadFunc func,
- gpointer data,
- gboolean joinable,
- gsize stack_size,
- GError **error)
+GThread *
+g_thread_new_full (const gchar *name,
+ GThreadFunc func,
+ gpointer data,
+ gboolean joinable,
+ gsize stack_size,
+ GError **error)
{
GRealThread* result;
GError *local_error = NULL;
result->thread.func = func;
result->thread.data = data;
result->private_data = NULL;
+ result->name = name;
G_LOCK (g_thread);
g_system_thread_create (g_thread_create_proxy, result,
stack_size, joinable,
for (t = g_thread_all_threads, p = NULL; t; p = t, t = t->next)
{
if (t == (GRealThread*) thread)
- {
- if (p)
- p->next = t->next;
- else
- g_thread_all_threads = t->next;
- break;
- }
+ {
+ 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 */
thread->joinable = 0;
g_system_thread_assign (real->system_thread, zero_thread);
g_slice_free (GCond, cond);
}
-/* GPrivate {{{1 ------------------------------------------------------ */
-
-/**
- * g_private_new:
- * @destructor: a function to destroy the data keyed to
- * the #GPrivate when a thread ends
- *
- * Creates a new #GPrivate. If @destructor is non-%NULL, it is a
- * pointer to a destructor function. Whenever a thread ends and the
- * corresponding pointer keyed to this instance of #GPrivate is
- * non-%NULL, the destructor is called with this pointer as the
- * argument.
- *
- * <note><para>
- * #GStaticPrivate is a better choice for most uses.
- * </para></note>
- *
- * <note><para>@destructor is used quite differently from @notify in
- * g_static_private_set().</para></note>
- *
- * <note><para>A #GPrivate cannot be freed. Reuse it instead, if you
- * can, to avoid shortage, or use #GStaticPrivate.</para></note>
- *
- * <note><para>This function will abort if g_thread_init() has not been
- * called yet.</para></note>
- *
- * Returns: a newly allocated #GPrivate
- */
-GPrivate *
-g_private_new (GDestroyNotify notify)
-{
- GPrivate *key;
-
- key = g_slice_new (GPrivate);
- g_private_init (key, notify);
-
- return key;
-}
-
- /* vim: set foldmethod=marker: */
+/* Epilogue {{{1 */
+/* vim: set foldmethod=marker: */