mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 15:06:14 +01:00
Support for one-time initialization functions. (#69668, Sebastian
2003-07-09 Matthias Clasen <maclas@gmx.de> Support for one-time initialization functions. (#69668, Sebastian Wilhelmi) * configure.in: Check whether double checked locking is safe, define g_once() in glibconfig.h accordingly. * glib/gthread.h: Add GOnce, GOnceStatus, G_ONCE_INIT and g_once_impl. * glib/gthread.c (g_once_impl): Fallback implementation using a mutex if double checked locking is unsafe. * tests/thread-test.c: Add tests for g_once().
This commit is contained in:
parent
238c7c368b
commit
876f907863
11
ChangeLog
11
ChangeLog
@ -1,3 +1,14 @@
|
||||
2003-07-09 Matthias Clasen <maclas@gmx.de>
|
||||
|
||||
Support for one-time initialization functions. (#69668, Sebastian Wilhelmi)
|
||||
|
||||
* configure.in: Check whether double checked locking is safe, define g_once() in
|
||||
glibconfig.h accordingly.
|
||||
* glib/gthread.h: Add GOnce, GOnceStatus, G_ONCE_INIT and g_once_impl.
|
||||
* glib/gthread.c (g_once_impl): Fallback implementation using a mutex if double checked
|
||||
locking is unsafe.
|
||||
* tests/thread-test.c: Add tests for g_once().
|
||||
|
||||
2003-07-02 Matthias Clasen <maclas@gmx.de>
|
||||
|
||||
* glib/gstrfuncs.c (g_strfreev): Move docs inline, document behavior
|
||||
|
@ -1,3 +1,14 @@
|
||||
2003-07-09 Matthias Clasen <maclas@gmx.de>
|
||||
|
||||
Support for one-time initialization functions. (#69668, Sebastian Wilhelmi)
|
||||
|
||||
* configure.in: Check whether double checked locking is safe, define g_once() in
|
||||
glibconfig.h accordingly.
|
||||
* glib/gthread.h: Add GOnce, GOnceStatus, G_ONCE_INIT and g_once_impl.
|
||||
* glib/gthread.c (g_once_impl): Fallback implementation using a mutex if double checked
|
||||
locking is unsafe.
|
||||
* tests/thread-test.c: Add tests for g_once().
|
||||
|
||||
2003-07-02 Matthias Clasen <maclas@gmx.de>
|
||||
|
||||
* glib/gstrfuncs.c (g_strfreev): Move docs inline, document behavior
|
||||
|
@ -1,3 +1,14 @@
|
||||
2003-07-09 Matthias Clasen <maclas@gmx.de>
|
||||
|
||||
Support for one-time initialization functions. (#69668, Sebastian Wilhelmi)
|
||||
|
||||
* configure.in: Check whether double checked locking is safe, define g_once() in
|
||||
glibconfig.h accordingly.
|
||||
* glib/gthread.h: Add GOnce, GOnceStatus, G_ONCE_INIT and g_once_impl.
|
||||
* glib/gthread.c (g_once_impl): Fallback implementation using a mutex if double checked
|
||||
locking is unsafe.
|
||||
* tests/thread-test.c: Add tests for g_once().
|
||||
|
||||
2003-07-02 Matthias Clasen <maclas@gmx.de>
|
||||
|
||||
* glib/gstrfuncs.c (g_strfreev): Move docs inline, document behavior
|
||||
|
@ -1,3 +1,14 @@
|
||||
2003-07-09 Matthias Clasen <maclas@gmx.de>
|
||||
|
||||
Support for one-time initialization functions. (#69668, Sebastian Wilhelmi)
|
||||
|
||||
* configure.in: Check whether double checked locking is safe, define g_once() in
|
||||
glibconfig.h accordingly.
|
||||
* glib/gthread.h: Add GOnce, GOnceStatus, G_ONCE_INIT and g_once_impl.
|
||||
* glib/gthread.c (g_once_impl): Fallback implementation using a mutex if double checked
|
||||
locking is unsafe.
|
||||
* tests/thread-test.c: Add tests for g_once().
|
||||
|
||||
2003-07-02 Matthias Clasen <maclas@gmx.de>
|
||||
|
||||
* glib/gstrfuncs.c (g_strfreev): Move docs inline, document behavior
|
||||
|
@ -1,3 +1,14 @@
|
||||
2003-07-09 Matthias Clasen <maclas@gmx.de>
|
||||
|
||||
Support for one-time initialization functions. (#69668, Sebastian Wilhelmi)
|
||||
|
||||
* configure.in: Check whether double checked locking is safe, define g_once() in
|
||||
glibconfig.h accordingly.
|
||||
* glib/gthread.h: Add GOnce, GOnceStatus, G_ONCE_INIT and g_once_impl.
|
||||
* glib/gthread.c (g_once_impl): Fallback implementation using a mutex if double checked
|
||||
locking is unsafe.
|
||||
* tests/thread-test.c: Add tests for g_once().
|
||||
|
||||
2003-07-02 Matthias Clasen <maclas@gmx.de>
|
||||
|
||||
* glib/gstrfuncs.c (g_strfreev): Move docs inline, document behavior
|
||||
|
@ -1,3 +1,14 @@
|
||||
2003-07-09 Matthias Clasen <maclas@gmx.de>
|
||||
|
||||
Support for one-time initialization functions. (#69668, Sebastian Wilhelmi)
|
||||
|
||||
* configure.in: Check whether double checked locking is safe, define g_once() in
|
||||
glibconfig.h accordingly.
|
||||
* glib/gthread.h: Add GOnce, GOnceStatus, G_ONCE_INIT and g_once_impl.
|
||||
* glib/gthread.c (g_once_impl): Fallback implementation using a mutex if double checked
|
||||
locking is unsafe.
|
||||
* tests/thread-test.c: Add tests for g_once().
|
||||
|
||||
2003-07-02 Matthias Clasen <maclas@gmx.de>
|
||||
|
||||
* glib/gstrfuncs.c (g_strfreev): Move docs inline, document behavior
|
||||
|
49
configure.in
49
configure.in
@ -1170,6 +1170,27 @@ esac
|
||||
AC_MSG_RESULT($GIO)
|
||||
AC_SUBST(GIO)
|
||||
|
||||
dnl check for cpu to enable double checked locking when possible
|
||||
dnl ************************************************************
|
||||
|
||||
if test x"$have_threads" != xno; then
|
||||
AC_MSG_CHECKING(whether double checked locking is safe)
|
||||
# According to glibc/linuxthreads the following platforms do
|
||||
# not have the notion of a read or write memory barrier and
|
||||
# therefore the double checked locking should be safe. Have a
|
||||
# look at pthread_once in glibc/linuxthreads/mutex.c to see,
|
||||
# what this means.
|
||||
case $host_cpu in
|
||||
arm|hppa|i386|i686|ia64|m68k|sh|cris|x86_64)
|
||||
g_use_double_checked_locking=yes
|
||||
;;
|
||||
*)
|
||||
g_use_double_checked_locking=no
|
||||
;;
|
||||
esac
|
||||
AC_MSG_RESULT($g_use_double_checked_locking)
|
||||
fi
|
||||
|
||||
dnl ****************************************
|
||||
dnl *** platform dependent source checks ***
|
||||
dnl ****************************************
|
||||
@ -2134,9 +2155,9 @@ struct _GStaticMutex
|
||||
} static_mutex;
|
||||
};
|
||||
#define G_STATIC_MUTEX_INIT { NULL, { { $g_mutex_contents} } }
|
||||
#define g_static_mutex_get_mutex(mutex) \
|
||||
(g_thread_use_default_impl ? ((GMutex*) &((mutex)->static_mutex)) : \
|
||||
g_static_mutex_get_mutex_impl (&((mutex)->runtime_mutex)))
|
||||
#define g_static_mutex_get_mutex(mutex) \\
|
||||
(g_thread_use_default_impl ? ((GMutex*) &((mutex)->static_mutex)) : \\
|
||||
g_static_mutex_get_mutex_impl_shortcut (&((mutex)->runtime_mutex)))
|
||||
_______EOF
|
||||
else
|
||||
cat >>$outfile <<_______EOF
|
||||
@ -2144,10 +2165,29 @@ $g_enable_threads_def G_THREADS_ENABLED
|
||||
#define G_THREADS_IMPL_$g_threads_impl_def
|
||||
typedef struct _GMutex* GStaticMutex;
|
||||
#define G_STATIC_MUTEX_INIT NULL
|
||||
#define g_static_mutex_get_mutex(mutex) (g_static_mutex_get_mutex_impl (mutex))
|
||||
#define g_static_mutex_get_mutex(mutex) \\
|
||||
(g_static_mutex_get_mutex_impl_shortcut (mutex))
|
||||
_______EOF
|
||||
fi
|
||||
|
||||
if test x$g_use_double_checked_locking = xyes; then
|
||||
cat >>$outfile <<_______EOF
|
||||
/* double checked locking can be used on this platform */
|
||||
#define g_once(once, func, arg) \\
|
||||
((once)->status == G_ONCE_STATUS_READY ? (once)->retval : \\
|
||||
g_once_impl (once, func, arg));
|
||||
#define g_static_mutex_get_mutex_impl_shortcut(mutex) \\
|
||||
(*(mutex) ? *(mutex) : g_static_mutex_get_mutex_impl (mutex))
|
||||
_______EOF
|
||||
else
|
||||
cat >>$outfile <<_______EOF
|
||||
/* double checked locking is unsafe to use on this platform, do full locking */
|
||||
#define g_once(once, func, arg) (g_once_impl(once, func, arg))
|
||||
#define g_static_mutex_get_mutex_impl_shortcut(mutex) \\
|
||||
(g_static_mutex_get_mutex_impl (mutex))
|
||||
_______EOF
|
||||
fi
|
||||
|
||||
cat >>$outfile <<_______EOF
|
||||
/* This represents a system thread as used by the implementation. An
|
||||
* alien implementaion, as loaded by g_thread_init can only count on
|
||||
@ -2426,6 +2466,7 @@ xno) g_enable_threads_def="#undef";;
|
||||
esac
|
||||
|
||||
g_threads_impl_def=$g_threads_impl
|
||||
g_use_double_checked_locking=$g_use_double_checked_locking
|
||||
|
||||
g_mutex_has_default="$mutex_has_default"
|
||||
g_mutex_sizeof="$glib_cv_sizeof_gmutex"
|
||||
|
@ -554,6 +554,12 @@ g_static_private_get
|
||||
g_static_private_set
|
||||
g_static_private_free
|
||||
|
||||
<SUBSECTION>
|
||||
GOnce
|
||||
GOnceStatus
|
||||
G_ONCE_INIT
|
||||
g_once
|
||||
|
||||
<SUBSECTION Private>
|
||||
G_THREAD_ECF
|
||||
G_THREAD_CF
|
||||
@ -569,6 +575,7 @@ g_threads_got_initialized
|
||||
g_thread_functions_for_glib_use
|
||||
g_thread_init_glib
|
||||
g_thread_error_quark
|
||||
g_once_impl
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
|
@ -1606,3 +1606,72 @@ you should also free the #GStaticPrivate.
|
||||
@private_key: a #GStaticPrivate to be freed.
|
||||
|
||||
|
||||
<!-- ##### STRUCT GOnce ##### -->
|
||||
<para>
|
||||
A <structname>GOnce</structname> struct controls a one-time initialization function.
|
||||
Any one-time initialization function must have its own unique <structname>GOnce</structname>
|
||||
struct.
|
||||
</para>
|
||||
|
||||
@Since: 2.4
|
||||
|
||||
<!-- ##### ENUM GOnceStatus ##### -->
|
||||
<para>
|
||||
The possible stati of a one-time initialization function controlled by a #GOnce struct.
|
||||
</para>
|
||||
|
||||
@G_ONCE_STATUS_NOTCALLED: the function has not been called yet.
|
||||
@G_ONCE_STATUS_PROGRESS: the function call is currently in progress.
|
||||
@G_ONCE_STATUS_READY: the function has been called.
|
||||
|
||||
<!-- ##### MACRO G_ONCE_INIT ##### -->
|
||||
<para>
|
||||
A #GOnce must be initialized with this macro, before it can be used.
|
||||
</para>
|
||||
<para>
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
GOnce my_once = G_ONCE_INIT;
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
</para>
|
||||
|
||||
|
||||
|
||||
<!-- ##### MACRO g_once ##### -->
|
||||
<para>
|
||||
The first call to this routine by a process with a given #GOnce struct calls @func with the given
|
||||
argument. Thereafter, subsequent calls to g_once() with the same #GOnce struct do not call @func
|
||||
again, but return the stored result of the first call. On return from g_once(), the status of @once
|
||||
will be %G_ONCE_STATUS_READY.
|
||||
</para>
|
||||
<para>
|
||||
For example, a mutex or a thread-specific data key must be created exactly once. In a threaded
|
||||
environment, calling g_once() ensures that the initialization is serialized across multiple threads.
|
||||
</para>
|
||||
<note><para>
|
||||
Calling g_once() recursively on the same #GOnce struct in @func will lead to a deadlock.
|
||||
</para></note>
|
||||
<para>
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
gpointer
|
||||
get_debug_flags ()
|
||||
{
|
||||
static GOnce my_once = G_ONCE_INIT;
|
||||
|
||||
g_once (&my_once, parse_debug_flags, NULL);
|
||||
|
||||
return my_once.retval;
|
||||
}
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
</para>
|
||||
|
||||
@once: a #GOnce structure
|
||||
@func: the function associated to @once. This function is called only once, regardless of the
|
||||
number of times it and its associated #GOnce struct are passed to g_once() .
|
||||
@arg: data to be passed to @func
|
||||
@Since: 2.4
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* GLIB - Library of useful routines for C programming
|
||||
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
||||
*
|
||||
* gmutex.c: MT safety related functions
|
||||
* gthread.c: MT safety related functions
|
||||
* Copyright 1998 Sebastian Wilhelmi; University of Karlsruhe
|
||||
* Owen Taylor
|
||||
*
|
||||
@ -148,7 +148,8 @@ GThreadFunctions g_thread_functions_for_glib_use = {
|
||||
|
||||
/* Local data */
|
||||
|
||||
static GMutex *g_mutex_protect_static_mutex_allocation = NULL;
|
||||
static GMutex *g_once_mutex = NULL;
|
||||
static GCond *g_once_cond = NULL;
|
||||
static GPrivate *g_thread_specific_private = NULL;
|
||||
static GSList *g_thread_all_threads = NULL;
|
||||
static GSList *g_thread_free_indeces = NULL;
|
||||
@ -167,7 +168,8 @@ g_thread_init_glib (void)
|
||||
*/
|
||||
GRealThread* main_thread = (GRealThread*) g_thread_self ();
|
||||
|
||||
g_mutex_protect_static_mutex_allocation = g_mutex_new ();
|
||||
g_once_mutex = g_mutex_new ();
|
||||
g_once_cond = g_cond_new ();
|
||||
|
||||
_g_convert_thread_init ();
|
||||
_g_rand_thread_init ();
|
||||
@ -198,6 +200,33 @@ g_thread_init_glib (void)
|
||||
}
|
||||
#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)
|
||||
{
|
||||
@ -214,14 +243,23 @@ g_static_mutex_get_mutex_impl (GMutex** mutex)
|
||||
if (!g_thread_supported ())
|
||||
return NULL;
|
||||
|
||||
g_assert (g_mutex_protect_static_mutex_allocation);
|
||||
g_assert (g_once_mutex);
|
||||
|
||||
g_mutex_lock (g_mutex_protect_static_mutex_allocation);
|
||||
g_mutex_lock (g_once_mutex);
|
||||
|
||||
if (!(*mutex))
|
||||
*mutex = g_mutex_new ();
|
||||
{
|
||||
GMutex *new_mutex = g_mutex_new ();
|
||||
|
||||
g_mutex_unlock (g_mutex_protect_static_mutex_allocation);
|
||||
/* 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;
|
||||
}
|
||||
|
@ -284,6 +284,24 @@ 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);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
G_ONCE_STATUS_NOTCALLED,
|
||||
G_ONCE_STATUS_PROGRESS,
|
||||
G_ONCE_STATUS_READY
|
||||
} GOnceStatus;
|
||||
|
||||
typedef struct _GOnce GOnce;
|
||||
struct _GOnce
|
||||
{
|
||||
volatile GOnceStatus status;
|
||||
volatile gpointer retval;
|
||||
};
|
||||
|
||||
#define G_ONCE_INIT { G_ONCE_STATUS_NOTCALLED, NULL }
|
||||
|
||||
gpointer g_once_impl (GOnce *once, GThreadFunc func, gpointer arg);
|
||||
|
||||
/* these are some convenience macros that expand to nothing if GLib
|
||||
* was configured with --disable-threads. for using StaticMutexes,
|
||||
* you define them with G_LOCK_DEFINE_STATIC (name) or G_LOCK_DEFINE (name)
|
||||
|
@ -297,14 +297,92 @@ test_g_static_rw_lock ()
|
||||
g_assert (test_g_static_rw_lock_state == 0);
|
||||
}
|
||||
|
||||
#define G_ONCE_SIZE 100
|
||||
#define G_ONCE_THREADS 10
|
||||
|
||||
G_LOCK_DEFINE (test_g_once);
|
||||
static guint test_g_once_guint_array[G_ONCE_SIZE];
|
||||
static GOnce test_g_once_array[G_ONCE_SIZE];
|
||||
|
||||
static gpointer
|
||||
test_g_once_init_func(gpointer arg)
|
||||
{
|
||||
guint *count = arg;
|
||||
g_usleep (g_random_int_range (20,1000));
|
||||
(*count)++;
|
||||
g_usleep (g_random_int_range (20,1000));
|
||||
return arg;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
test_g_once_thread (gpointer ignore)
|
||||
{
|
||||
guint i;
|
||||
G_LOCK (test_g_once);
|
||||
/* Don't start before all threads are created */
|
||||
G_UNLOCK (test_g_once);
|
||||
for (i = 0; i < 1000; i++)
|
||||
{
|
||||
guint pos = g_random_int_range (0, G_ONCE_SIZE);
|
||||
gpointer ret = g_once (test_g_once_array + pos, test_g_once_init_func,
|
||||
test_g_once_guint_array + pos);
|
||||
g_assert (ret == test_g_once_guint_array + pos);
|
||||
}
|
||||
|
||||
/* Make sure, that all counters are touched at least once */
|
||||
for (i = 0; i < G_ONCE_SIZE; i++)
|
||||
{
|
||||
gpointer ret = g_once (test_g_once_array + i, test_g_once_init_func,
|
||||
test_g_once_guint_array + i);
|
||||
g_assert (ret == test_g_once_guint_array + i);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
test_g_thread_once (void)
|
||||
{
|
||||
static GOnce once_init = G_ONCE_INIT;
|
||||
GThread *threads[G_ONCE_THREADS];
|
||||
guint i;
|
||||
for (i = 0; i < G_ONCE_SIZE; i++)
|
||||
{
|
||||
test_g_once_array[i] = once_init;
|
||||
test_g_once_guint_array[i] = i;
|
||||
}
|
||||
G_LOCK (test_g_once);
|
||||
for (i = 0; i < G_ONCE_THREADS; i++)
|
||||
{
|
||||
threads[i] = g_thread_create (test_g_once_thread, (gpointer)(i%2),
|
||||
TRUE, NULL);
|
||||
}
|
||||
G_UNLOCK (test_g_once);
|
||||
for (i = 0; i < G_ONCE_THREADS; i++)
|
||||
{
|
||||
g_thread_join (threads[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < G_ONCE_SIZE; i++)
|
||||
{
|
||||
g_assert (test_g_once_guint_array[i] == i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* run all the tests */
|
||||
void
|
||||
run_all_tests()
|
||||
{
|
||||
test_g_mutex ();
|
||||
g_print (".");
|
||||
test_g_static_rec_mutex ();
|
||||
g_print (".");
|
||||
test_g_static_private ();
|
||||
g_print (".");
|
||||
test_g_static_rw_lock ();
|
||||
g_print (".");
|
||||
test_g_thread_once ();
|
||||
g_print (".");
|
||||
}
|
||||
|
||||
int
|
||||
@ -323,6 +401,7 @@ main (int argc,
|
||||
|
||||
g_thread_use_default_impl = FALSE;
|
||||
run_all_tests ();
|
||||
g_print ("\n");
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user