glib/glib/gthread.c
Tim Janik 733b1789c1 new slice allocator implementation.
Thu Dec  1 17:32:46 2005  Tim Janik  <timj@imendio.com>

        * glib/gslice.[hc]: new slice allocator implementation.

        * tests/slice-test.c: added random slice allocation test.

        * glib/gthread.[hc]: removed newly added private thread mem API.

        * glib/gthreadinit.h:
        * glib/gmessages.c:
        * glib/gthread.c:
        * glib/gmem.c: divided glib threading initialisation into three phases,
        initialisation where private keys and messaging are not available (only
        needed by gmem.c), initialisation without messaging but private keys
        available (gslice.c, gmessage.c), and full fledged initialisers that
        server the rest of glib. initialisation functions got renamed to reflect
        the limitations of their corresponding phases.

        * glib/gmem.c: removed memchunk code, defer allocations to
        g_slice_* instead.

        * glib/gmem.[hc]: removed g_slice_* skeletons.

        * glib/glib.symbols: added g_slice_* symbols.

        * configure.in: check for availability of posix_memalign(3), memalign(3)
        and valloc(3).

        * glib/Makefile.am: added gslice.[hc].
2005-12-01 16:34:33 +00:00

923 lines
22 KiB
C

/* GLIB - Library of useful routines for C programming
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* gthread.c: MT safety related functions
* Copyright 1998 Sebastian Wilhelmi; University of Karlsruhe
* Owen Taylor
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
/*
* MT safe
*/
#include "config.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <string.h>
#include "glib.h"
#include "gthreadinit.h"
#include "galias.h"
#if GLIB_SIZEOF_SYSTEM_THREAD == SIZEOF_VOID_P
# define g_system_thread_equal_simple(thread1, thread2) \
((thread1).dummy_pointer == (thread2).dummy_pointer)
# define g_system_thread_assign(dest, src) \
((dest).dummy_pointer = (src).dummy_pointer)
#else /* GLIB_SIZEOF_SYSTEM_THREAD != SIZEOF_VOID_P */
# define g_system_thread_equal_simple(thread1, thread2) \
(memcmp (&(thread1), &(thread2), GLIB_SIZEOF_SYSTEM_THREAD) == 0)
# define g_system_thread_assign(dest, src) \
(memcpy (&(dest), &(src), GLIB_SIZEOF_SYSTEM_THREAD))
#endif /* GLIB_SIZEOF_SYSTEM_THREAD == SIZEOF_VOID_P */
#define g_system_thread_equal(thread1, thread2) \
(g_thread_functions_for_glib_use.thread_equal ? \
g_thread_functions_for_glib_use.thread_equal (&(thread1), &(thread2)) :\
g_system_thread_equal_simple((thread1), (thread2)))
GQuark
g_thread_error_quark (void)
{
static GQuark quark;
if (!quark)
quark = g_quark_from_static_string ("g_thread_error");
return quark;
}
/* Keep this in sync with GRealThread in gmain.c! */
typedef struct _GRealThread GRealThread;
struct _GRealThread
{
GThread thread;
gpointer private_data;
GRealThread *next;
gpointer retval;
GSystemThread system_thread;
};
typedef struct _GStaticPrivateNode GStaticPrivateNode;
struct _GStaticPrivateNode
{
gpointer data;
GDestroyNotify destroy;
};
static void g_thread_cleanup (gpointer data);
static void g_thread_fail (void);
/* Global variables */
static GSystemThread zero_thread; /* This is initialized to all zero */
gboolean g_thread_use_default_impl = TRUE;
gboolean g_threads_got_initialized = FALSE;
GThreadFunctions g_thread_functions_for_glib_use = {
(GMutex*(*)())g_thread_fail, /* mutex_new */
NULL, /* mutex_lock */
NULL, /* mutex_trylock */
NULL, /* mutex_unlock */
NULL, /* mutex_free */
(GCond*(*)())g_thread_fail, /* cond_new */
NULL, /* cond_signal */
NULL, /* cond_broadcast */
NULL, /* cond_wait */
NULL, /* cond_timed_wait */
NULL, /* cond_free */
(GPrivate*(*)(GDestroyNotify))g_thread_fail, /* private_new */
NULL, /* private_get */
NULL, /* private_set */
(void(*)(GThreadFunc, gpointer, gulong,
gboolean, gboolean, GThreadPriority,
gpointer, GError**))g_thread_fail, /* thread_create */
NULL, /* thread_yield */
NULL, /* thread_join */
NULL, /* thread_exit */
NULL, /* thread_set_priority */
NULL /* thread_self */
};
/* Local data */
static GMutex *g_once_mutex = NULL;
static GCond *g_once_cond = NULL;
static GPrivate *g_thread_specific_private = NULL;
static GRealThread *g_thread_all_threads = NULL;
static GSList *g_thread_free_indeces = NULL;
G_LOCK_DEFINE_STATIC (g_thread);
#ifdef G_THREADS_ENABLED
/* This must be called only once, before any threads are created.
* It will only be called from g_thread_init() in -lgthread.
*/
void
g_thread_init_glib (void)
{
/* We let the main thread (the one that calls g_thread_init) inherit
* the static_private data set before calling g_thread_init
*/
GRealThread* main_thread = (GRealThread*) g_thread_self ();
/* mutex and cond creation works without g_threads_got_initialized */
g_once_mutex = g_mutex_new ();
g_once_cond = g_cond_new ();
/* we may only create mutex and cond in here */
_g_mem_thread_init_noprivate_nomessage ();
/* setup the basic threading system */
g_threads_got_initialized = TRUE;
g_thread_specific_private = g_private_new (g_thread_cleanup);
g_private_set (g_thread_specific_private, main_thread);
G_THREAD_UF (thread_self, (&main_thread->system_thread));
/* complete memory system initialization, g_private_*() works now */
_g_slice_thread_init_nomessage ();
/* accomplish log system initialization to enable messaging */
_g_messages_thread_init_nomessage ();
/* we may run full-fledged initializers from here */
_g_convert_thread_init ();
_g_rand_thread_init ();
_g_main_thread_init ();
_g_atomic_thread_init ();
_g_utils_thread_init ();
#ifdef G_OS_WIN32
_g_win32_thread_init ();
#endif
}
#endif /* G_THREADS_ENABLED */
gpointer
g_once_impl (GOnce *once,
GThreadFunc func,
gpointer arg)
{
g_mutex_lock (g_once_mutex);
while (once->status == G_ONCE_STATUS_PROGRESS)
g_cond_wait (g_once_cond, g_once_mutex);
if (once->status != G_ONCE_STATUS_READY)
{
once->status = G_ONCE_STATUS_PROGRESS;
g_mutex_unlock (g_once_mutex);
once->retval = func (arg);
g_mutex_lock (g_once_mutex);
once->status = G_ONCE_STATUS_READY;
g_cond_broadcast (g_once_cond);
}
g_mutex_unlock (g_once_mutex);
return once->retval;
}
void
g_static_mutex_init (GStaticMutex *mutex)
{
static const GStaticMutex init_mutex = G_STATIC_MUTEX_INIT;
g_return_if_fail (mutex);
*mutex = init_mutex;
}
GMutex *
g_static_mutex_get_mutex_impl (GMutex** mutex)
{
if (!g_thread_supported ())
return NULL;
g_assert (g_once_mutex);
g_mutex_lock (g_once_mutex);
if (!(*mutex))
{
GMutex *new_mutex = g_mutex_new ();
/* The following is a memory barrier to avoid the write
* to *new_mutex being reordered to after writing *mutex */
g_mutex_lock (new_mutex);
g_mutex_unlock (new_mutex);
*mutex = new_mutex;
}
g_mutex_unlock (g_once_mutex);
return *mutex;
}
void
g_static_mutex_free (GStaticMutex* mutex)
{
GMutex **runtime_mutex;
g_return_if_fail (mutex);
/* The runtime_mutex is the first (or only) member of GStaticMutex,
* see both versions (of glibconfig.h) in configure.in */
runtime_mutex = ((GMutex**)mutex);
if (*runtime_mutex)
g_mutex_free (*runtime_mutex);
*runtime_mutex = NULL;
}
void
g_static_rec_mutex_init (GStaticRecMutex *mutex)
{
static const GStaticRecMutex init_mutex = G_STATIC_REC_MUTEX_INIT;
g_return_if_fail (mutex);
*mutex = init_mutex;
}
void
g_static_rec_mutex_lock (GStaticRecMutex* mutex)
{
GSystemThread self;
g_return_if_fail (mutex);
if (!g_thread_supported ())
return;
G_THREAD_UF (thread_self, (&self));
if (g_system_thread_equal (self, mutex->owner))
{
mutex->depth++;
return;
}
g_static_mutex_lock (&mutex->mutex);
g_system_thread_assign (mutex->owner, self);
mutex->depth = 1;
}
gboolean
g_static_rec_mutex_trylock (GStaticRecMutex* mutex)
{
GSystemThread self;
g_return_val_if_fail (mutex, FALSE);
if (!g_thread_supported ())
return TRUE;
G_THREAD_UF (thread_self, (&self));
if (g_system_thread_equal (self, mutex->owner))
{
mutex->depth++;
return TRUE;
}
if (!g_static_mutex_trylock (&mutex->mutex))
return FALSE;
g_system_thread_assign (mutex->owner, self);
mutex->depth = 1;
return TRUE;
}
void
g_static_rec_mutex_unlock (GStaticRecMutex* mutex)
{
g_return_if_fail (mutex);
if (!g_thread_supported ())
return;
if (mutex->depth > 1)
{
mutex->depth--;
return;
}
g_system_thread_assign (mutex->owner, zero_thread);
g_static_mutex_unlock (&mutex->mutex);
}
void
g_static_rec_mutex_lock_full (GStaticRecMutex *mutex,
guint depth)
{
GSystemThread self;
g_return_if_fail (mutex);
if (!g_thread_supported ())
return;
if (depth == 0)
return;
G_THREAD_UF (thread_self, (&self));
if (g_system_thread_equal (self, mutex->owner))
{
mutex->depth += depth;
return;
}
g_static_mutex_lock (&mutex->mutex);
g_system_thread_assign (mutex->owner, self);
mutex->depth = depth;
}
guint
g_static_rec_mutex_unlock_full (GStaticRecMutex *mutex)
{
guint depth;
g_return_val_if_fail (mutex, 0);
if (!g_thread_supported ())
return 1;
depth = mutex->depth;
g_system_thread_assign (mutex->owner, zero_thread);
mutex->depth = 0;
g_static_mutex_unlock (&mutex->mutex);
return depth;
}
void
g_static_rec_mutex_free (GStaticRecMutex *mutex)
{
g_return_if_fail (mutex);
g_static_mutex_free (&mutex->mutex);
}
void
g_static_private_init (GStaticPrivate *private_key)
{
private_key->index = 0;
}
gpointer
g_static_private_get (GStaticPrivate *private_key)
{
GRealThread *self = (GRealThread*) g_thread_self ();
GArray *array;
array = self->private_data;
if (!array)
return NULL;
if (!private_key->index)
return NULL;
else if (private_key->index <= array->len)
return g_array_index (array, GStaticPrivateNode,
private_key->index - 1).data;
else
return NULL;
}
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;
array = self->private_data;
if (!array)
{
array = g_array_new (FALSE, TRUE, sizeof (GStaticPrivateNode));
self->private_data = array;
}
if (!private_key->index)
{
G_LOCK (g_thread);
if (!private_key->index)
{
if (g_thread_free_indeces)
{
private_key->index =
GPOINTER_TO_UINT (g_thread_free_indeces->data);
g_thread_free_indeces =
g_slist_delete_link (g_thread_free_indeces,
g_thread_free_indeces);
}
else
private_key->index = ++next_index;
}
G_UNLOCK (g_thread);
}
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)
{
gpointer ddata = node->data;
GDestroyNotify ddestroy = node->destroy;
node->data = data;
node->destroy = notify;
ddestroy (ddata);
}
else
{
node->data = data;
node->destroy = notify;
}
}
void
g_static_private_free (GStaticPrivate *private_key)
{
guint index = private_key->index;
GRealThread *thread;
if (!index)
return;
private_key->index = 0;
G_LOCK (g_thread);
thread = g_thread_all_threads;
while (thread)
{
GArray *array = thread->private_data;
thread = thread->next;
if (array && index <= array->len)
{
GStaticPrivateNode *node = &g_array_index (array,
GStaticPrivateNode,
index - 1);
gpointer ddata = node->data;
GDestroyNotify ddestroy = node->destroy;
node->data = NULL;
node->destroy = NULL;
if (ddestroy)
{
G_UNLOCK (g_thread);
ddestroy (ddata);
G_LOCK (g_thread);
}
}
}
g_thread_free_indeces = g_slist_prepend (g_thread_free_indeces,
GUINT_TO_POINTER (index));
G_UNLOCK (g_thread);
}
static void
g_thread_cleanup (gpointer data)
{
if (data)
{
GRealThread* thread = data;
if (thread->private_data)
{
GArray* array = thread->private_data;
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);
g_free (thread);
}
}
}
static void
g_thread_fail (void)
{
g_error ("The thread system is not yet initialized.");
}
static gpointer
g_thread_create_proxy (gpointer data)
{
GRealThread* thread = data;
g_assert (data);
/* This has to happen before G_LOCK, as that might call g_thread_self */
g_private_set (g_thread_specific_private, 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);
thread->retval = thread->thread.func (thread->thread.data);
return NULL;
}
GThread*
g_thread_create_full (GThreadFunc func,
gpointer data,
gulong stack_size,
gboolean joinable,
gboolean bound,
GThreadPriority priority,
GError **error)
{
GRealThread* result;
GError *local_error = NULL;
g_return_val_if_fail (func, NULL);
g_return_val_if_fail (priority >= G_THREAD_PRIORITY_LOW, NULL);
g_return_val_if_fail (priority <= G_THREAD_PRIORITY_URGENT, NULL);
result = g_new0 (GRealThread, 1);
result->thread.joinable = joinable;
result->thread.priority = priority;
result->thread.func = func;
result->thread.data = data;
result->private_data = NULL;
G_LOCK (g_thread);
G_THREAD_UF (thread_create, (g_thread_create_proxy, result,
stack_size, joinable, bound, priority,
&result->system_thread, &local_error));
result->next = g_thread_all_threads;
g_thread_all_threads = result;
G_UNLOCK (g_thread);
if (local_error)
{
g_propagate_error (error, local_error);
g_free (result);
return NULL;
}
return (GThread*) result;
}
void
g_thread_exit (gpointer retval)
{
GRealThread* real = (GRealThread*) g_thread_self ();
real->retval = retval;
G_THREAD_CF (thread_exit, (void)0, ());
}
gpointer
g_thread_join (GThread* thread)
{
GRealThread* real = (GRealThread*) thread;
GRealThread *p, *t;
gpointer retval;
g_return_val_if_fail (thread, NULL);
g_return_val_if_fail (thread->joinable, NULL);
g_return_val_if_fail (!g_system_thread_equal (real->system_thread,
zero_thread), NULL);
G_THREAD_UF (thread_join, (&real->system_thread));
retval = real->retval;
G_LOCK (g_thread);
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;
}
}
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);
/* the thread structure for non-joinable threads is freed upon
thread end. We free the memory here. This will leave a loose end,
if a joinable thread is not joined. */
g_free (thread);
return retval;
}
void
g_thread_set_priority (GThread* thread,
GThreadPriority priority)
{
GRealThread* real = (GRealThread*) thread;
g_return_if_fail (thread);
g_return_if_fail (!g_system_thread_equal (real->system_thread, zero_thread));
g_return_if_fail (priority >= G_THREAD_PRIORITY_LOW);
g_return_if_fail (priority <= G_THREAD_PRIORITY_URGENT);
thread->priority = priority;
G_THREAD_CF (thread_set_priority, (void)0,
(&real->system_thread, priority));
}
GThread*
g_thread_self (void)
{
GRealThread* thread = g_private_get (g_thread_specific_private);
if (!thread)
{
/* If no thread data is available, provide and set one. This
can happen for the main thread and for threads, that are not
created by GLib. */
thread = g_new0 (GRealThread, 1);
thread->thread.joinable = FALSE; /* This is a save guess */
thread->thread.priority = G_THREAD_PRIORITY_NORMAL; /* This is
just a guess */
thread->thread.func = NULL;
thread->thread.data = NULL;
thread->private_data = NULL;
if (g_thread_supported ())
G_THREAD_UF (thread_self, (&thread->system_thread));
g_private_set (g_thread_specific_private, thread);
G_LOCK (g_thread);
thread->next = g_thread_all_threads;
g_thread_all_threads = thread;
G_UNLOCK (g_thread);
}
return (GThread*)thread;
}
void
g_static_rw_lock_init (GStaticRWLock* lock)
{
static const GStaticRWLock init_lock = G_STATIC_RW_LOCK_INIT;
g_return_if_fail (lock);
*lock = init_lock;
}
inline static void
g_static_rw_lock_wait (GCond** cond, GStaticMutex* mutex)
{
if (!*cond)
*cond = g_cond_new ();
g_cond_wait (*cond, g_static_mutex_get_mutex (mutex));
}
inline static void
g_static_rw_lock_signal (GStaticRWLock* lock)
{
if (lock->want_to_write && lock->write_cond)
g_cond_signal (lock->write_cond);
else if (lock->want_to_read && lock->read_cond)
g_cond_broadcast (lock->read_cond);
}
void
g_static_rw_lock_reader_lock (GStaticRWLock* lock)
{
g_return_if_fail (lock);
if (!g_threads_got_initialized)
return;
g_static_mutex_lock (&lock->mutex);
lock->want_to_read++;
while (lock->have_writer || lock->want_to_write)
g_static_rw_lock_wait (&lock->read_cond, &lock->mutex);
lock->want_to_read--;
lock->read_counter++;
g_static_mutex_unlock (&lock->mutex);
}
gboolean
g_static_rw_lock_reader_trylock (GStaticRWLock* lock)
{
gboolean ret_val = FALSE;
g_return_val_if_fail (lock, FALSE);
if (!g_threads_got_initialized)
return TRUE;
g_static_mutex_lock (&lock->mutex);
if (!lock->have_writer && !lock->want_to_write)
{
lock->read_counter++;
ret_val = TRUE;
}
g_static_mutex_unlock (&lock->mutex);
return ret_val;
}
void
g_static_rw_lock_reader_unlock (GStaticRWLock* lock)
{
g_return_if_fail (lock);
if (!g_threads_got_initialized)
return;
g_static_mutex_lock (&lock->mutex);
lock->read_counter--;
if (lock->read_counter == 0)
g_static_rw_lock_signal (lock);
g_static_mutex_unlock (&lock->mutex);
}
void
g_static_rw_lock_writer_lock (GStaticRWLock* lock)
{
g_return_if_fail (lock);
if (!g_threads_got_initialized)
return;
g_static_mutex_lock (&lock->mutex);
lock->want_to_write++;
while (lock->have_writer || lock->read_counter)
g_static_rw_lock_wait (&lock->write_cond, &lock->mutex);
lock->want_to_write--;
lock->have_writer = TRUE;
g_static_mutex_unlock (&lock->mutex);
}
gboolean
g_static_rw_lock_writer_trylock (GStaticRWLock* lock)
{
gboolean ret_val = FALSE;
g_return_val_if_fail (lock, FALSE);
if (!g_threads_got_initialized)
return TRUE;
g_static_mutex_lock (&lock->mutex);
if (!lock->have_writer && !lock->read_counter)
{
lock->have_writer = TRUE;
ret_val = TRUE;
}
g_static_mutex_unlock (&lock->mutex);
return ret_val;
}
void
g_static_rw_lock_writer_unlock (GStaticRWLock* lock)
{
g_return_if_fail (lock);
if (!g_threads_got_initialized)
return;
g_static_mutex_lock (&lock->mutex);
lock->have_writer = FALSE;
g_static_rw_lock_signal (lock);
g_static_mutex_unlock (&lock->mutex);
}
void
g_static_rw_lock_free (GStaticRWLock* lock)
{
g_return_if_fail (lock);
if (lock->read_cond)
{
g_cond_free (lock->read_cond);
lock->read_cond = NULL;
}
if (lock->write_cond)
{
g_cond_free (lock->write_cond);
lock->write_cond = NULL;
}
g_static_mutex_free (&lock->mutex);
}
/**
* 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-existant 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);
}
}
#define __G_THREAD_C__
#include "galiasdef.c"