mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-23 17:38:54 +02:00
New files from last commit of thread-safety stuff.
This commit is contained in:
144
gmutex.c
Normal file
144
gmutex.c
Normal file
@@ -0,0 +1,144 @@
|
||||
/* GLIB - Library of useful routines for C programming
|
||||
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
||||
*
|
||||
* gmutex.c:
|
||||
* Copyright 1998 Sebastian Wilhelmi; University of Karlsruhe
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* MT safe
|
||||
*/
|
||||
|
||||
#include "glib.h"
|
||||
|
||||
typedef struct _GStaticPrivateNode GStaticPrivateNode;
|
||||
|
||||
struct _GStaticPrivateNode {
|
||||
gpointer data;
|
||||
GDestroyNotify destroy;
|
||||
};
|
||||
|
||||
static void g_static_private_free_data (gpointer data);
|
||||
|
||||
/* Global variables */
|
||||
|
||||
gboolean g_thread_use_default_impl = TRUE;
|
||||
gboolean g_thread_supported = FALSE;
|
||||
GThreadFunctions g_thread_functions_for_glib_use; /* is NULLified as default */
|
||||
|
||||
/* Local data */
|
||||
|
||||
static GMutex *g_mutex_protect_static_mutex_allocation = NULL;
|
||||
static GMutex *g_thread_specific_mutex = NULL;
|
||||
static GPrivate *g_thread_specific_private = NULL;
|
||||
|
||||
/* This must be called only once, before any threads are created.
|
||||
* It will only be called from g_thread_init() in -lgthread.
|
||||
*/
|
||||
void
|
||||
g_mutex_init (void)
|
||||
{
|
||||
g_mutex_protect_static_mutex_allocation = g_mutex_new();
|
||||
g_thread_specific_mutex = g_mutex_new();
|
||||
|
||||
g_thread_specific_private = g_private_new (g_static_private_free_data);
|
||||
}
|
||||
|
||||
GMutex *
|
||||
g_static_mutex_get_mutex_impl (GMutex** mutex)
|
||||
{
|
||||
if (!g_thread_supported)
|
||||
return NULL;
|
||||
|
||||
g_assert (g_mutex_protect_static_mutex_allocation);
|
||||
|
||||
g_mutex_lock (g_mutex_protect_static_mutex_allocation);
|
||||
|
||||
if (!(*mutex))
|
||||
*mutex = g_mutex_new();
|
||||
|
||||
g_mutex_unlock (g_mutex_protect_static_mutex_allocation);
|
||||
|
||||
return *mutex;
|
||||
}
|
||||
|
||||
gpointer
|
||||
g_static_private_get (GStaticPrivate *private)
|
||||
{
|
||||
GArray *array;
|
||||
|
||||
array = g_private_get (g_thread_specific_private);
|
||||
if (!array)
|
||||
return NULL;
|
||||
|
||||
if (!private->index)
|
||||
return NULL;
|
||||
else if (private->index <= array->len)
|
||||
return g_array_index (array, GStaticPrivateNode, (private->index - 1)).data;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
g_static_private_set (GStaticPrivate *private,
|
||||
gpointer data,
|
||||
GDestroyNotify notify)
|
||||
{
|
||||
GArray *array;
|
||||
static guint next_index = 0;
|
||||
|
||||
array = g_private_get (g_thread_specific_private);
|
||||
if (!array)
|
||||
{
|
||||
array = g_array_new (FALSE, FALSE, sizeof(GStaticPrivateNode));
|
||||
g_private_set (g_thread_specific_private, array);
|
||||
}
|
||||
|
||||
if (!private->index)
|
||||
{
|
||||
g_mutex_lock (g_thread_specific_mutex);
|
||||
|
||||
if (!private->index)
|
||||
private->index = ++next_index;
|
||||
|
||||
g_mutex_unlock (g_thread_specific_mutex);
|
||||
}
|
||||
|
||||
if (private->index > array->len)
|
||||
g_array_set_size (array, private->index);
|
||||
|
||||
g_array_index (array, GStaticPrivateNode, (private->index - 1)).data = data;
|
||||
g_array_index (array, GStaticPrivateNode, (private->index - 1)).destroy = notify;
|
||||
}
|
||||
|
||||
static void
|
||||
g_static_private_free_data (gpointer data)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
GArray* array = data;
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < array->len; i++ )
|
||||
{
|
||||
GStaticPrivateNode *node = &g_array_index (array, GStaticPrivateNode, i);
|
||||
if (node->data && node->destroy)
|
||||
node->destroy (node->data);
|
||||
}
|
||||
}
|
||||
}
|
21
gthread/Makefile.am
Normal file
21
gthread/Makefile.am
Normal file
@@ -0,0 +1,21 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/gthread -DG_LOG_DOMAIN=g_log_domain_gthread
|
||||
|
||||
EXTRA_DIST = \
|
||||
gthread-posix.c
|
||||
|
||||
libglib = $(top_builddir)/libglib.la # -lglib
|
||||
|
||||
lib_LTLIBRARIES = libgthread.la
|
||||
|
||||
libgthread_la_SOURCES = gthread.c
|
||||
libgthread_la_LDFLAGS = \
|
||||
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
|
||||
-release $(LT_RELEASE)
|
||||
|
||||
libgthread_la_LIBADD = \
|
||||
@G_THREAD_LIBS@
|
||||
|
||||
noinst_PROGRAMS = testgthread
|
||||
testgthread_LDADD = ../libglib.la libgthread.la
|
2
gthread/gthread-none.c
Normal file
2
gthread/gthread-none.c
Normal file
@@ -0,0 +1,2 @@
|
||||
static GThreadFunctions
|
||||
g_mutex_functions_for_glib_use_default; /* is NULLified */
|
152
gthread/gthread-posix.c
Normal file
152
gthread/gthread-posix.c
Normal file
@@ -0,0 +1,152 @@
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#define posix_print_error( name, num ) \
|
||||
g_error( "file %s: line %d (%s): error %s during %s", \
|
||||
__FILE__, __LINE__, G_GNUC_PRETTY_FUNCTION, \
|
||||
g_strerror((num)), #name )
|
||||
|
||||
#define posix_check_for_error( what ) G_STMT_START{ \
|
||||
int error = (what); \
|
||||
if( error ) { posix_print_error( what, error ); } \
|
||||
}G_STMT_END
|
||||
|
||||
static GMutex*
|
||||
g_mutex_new_posix_impl (void)
|
||||
{
|
||||
/* we can not use g_new and friends, as they might use mutexes by
|
||||
themself */
|
||||
GMutex* result = malloc(sizeof(pthread_mutex_t));
|
||||
posix_check_for_error( pthread_mutex_init( (pthread_mutex_t*)result,
|
||||
NULL ) );
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
g_mutex_free_posix_impl (GMutex* mutex)
|
||||
{
|
||||
posix_check_for_error( pthread_mutex_destroy((pthread_mutex_t*)mutex) );
|
||||
free (mutex);
|
||||
}
|
||||
|
||||
/* pthread_mutex_lock, pthread_mutex_unlock can be taken directly, as
|
||||
signature and semantic are right, but without error check then!!!!,
|
||||
we might want to change this therfore. */
|
||||
|
||||
static gboolean
|
||||
g_mutex_try_lock_posix_impl (GMutex* mutex)
|
||||
{
|
||||
int result;
|
||||
result = pthread_mutex_trylock((pthread_mutex_t*)mutex);
|
||||
if( result == EBUSY )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
posix_check_for_error( result );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GCond*
|
||||
g_cond_new_posix_impl (void)
|
||||
{
|
||||
GCond* result = (GCond*) g_new0 (pthread_cond_t, 1);
|
||||
posix_check_for_error( pthread_cond_init( (pthread_cond_t*)result, NULL ) );
|
||||
return result;
|
||||
}
|
||||
|
||||
/* pthread_cond_signal, pthread_cond_broadcast and pthread_cond_wait
|
||||
can be taken directly, as signature and semantic are right, but
|
||||
without error check then!!!!, we might want to change this
|
||||
therfore. */
|
||||
|
||||
#define G_MICROSEC 1000000
|
||||
#define G_NANOSEC 1000000000
|
||||
|
||||
static gboolean
|
||||
g_cond_timed_wait_posix_impl (GCond* cond,
|
||||
GMutex* entered_mutex,
|
||||
GTimeVal* abs_time)
|
||||
{
|
||||
int result;
|
||||
struct timespec end_time;
|
||||
gboolean timed_out;
|
||||
|
||||
g_return_val_if_fail (cond != NULL, FALSE);
|
||||
g_return_val_if_fail (entered_mutex != NULL, FALSE);
|
||||
|
||||
if (!abs_time)
|
||||
{
|
||||
g_cond_wait(cond, entered_mutex);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
end_time.tv_sec = abs_time->tv_sec;
|
||||
end_time.tv_nsec = abs_time->tv_usec * (G_NANOSEC / G_MICROSEC);
|
||||
g_assert (end_time.tv_nsec < G_NANOSEC);
|
||||
result = pthread_cond_timedwait ((pthread_cond_t*)cond,
|
||||
(pthread_mutex_t*)entered_mutex,
|
||||
&end_time);
|
||||
timed_out = ( result == ETIMEDOUT );
|
||||
if (!timed_out)
|
||||
posix_check_for_error (result);
|
||||
return !timed_out;
|
||||
}
|
||||
|
||||
static void
|
||||
g_cond_free_posix_impl(GCond* cond)
|
||||
{
|
||||
posix_check_for_error (pthread_cond_destroy((pthread_cond_t*)cond));
|
||||
g_free (cond);
|
||||
}
|
||||
|
||||
static GPrivate*
|
||||
g_private_new_posix_impl(GDestroyNotify destructor)
|
||||
{
|
||||
/* we can not use g_new and friends, as they might use mutexes by
|
||||
themself */
|
||||
GPrivate* result = malloc( sizeof(pthread_key_t) );
|
||||
/* FIXME: we shouldn't use g_log here actually */
|
||||
posix_check_for_error( pthread_key_create( (pthread_key_t*)result,
|
||||
destructor) );
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
g_private_set_posix_impl(GPrivate* private, gpointer value)
|
||||
{
|
||||
g_return_if_fail (private != NULL);
|
||||
|
||||
/* FIXME: we shouldn't use g_log here actually */
|
||||
posix_check_for_error( pthread_setspecific(*(pthread_key_t*)private,
|
||||
value) );
|
||||
}
|
||||
|
||||
gpointer
|
||||
g_private_get_posix_impl(GPrivate* private)
|
||||
{
|
||||
g_return_val_if_fail (private != NULL, NULL);
|
||||
|
||||
/* FIXME: we shouldn't use g_log here actually */
|
||||
return pthread_getspecific(*(pthread_key_t*)private );
|
||||
}
|
||||
|
||||
static GThreadFunctions
|
||||
g_thread_functions_for_glib_use_default = {
|
||||
g_mutex_new_posix_impl,
|
||||
(void(*)(GMutex*))pthread_mutex_lock,
|
||||
g_mutex_try_lock_posix_impl,
|
||||
(void(*)(GMutex*))pthread_mutex_unlock,
|
||||
g_mutex_free_posix_impl,
|
||||
g_cond_new_posix_impl,
|
||||
(void(*)(GCond*))pthread_cond_signal,
|
||||
(void(*)(GCond*))pthread_cond_broadcast,
|
||||
(void(*)(GCond*,GMutex*))pthread_cond_wait,
|
||||
g_cond_timed_wait_posix_impl,
|
||||
g_cond_free_posix_impl,
|
||||
g_private_new_posix_impl,
|
||||
g_private_get_posix_impl,
|
||||
g_private_set_posix_impl
|
||||
};
|
||||
|
147
gthread/gthread-solaris.c
Normal file
147
gthread/gthread-solaris.c
Normal file
@@ -0,0 +1,147 @@
|
||||
#include <thread.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define solaris_print_error( name, num ) \
|
||||
g_error( "file %s: line %d (%s): error %s during %s", \
|
||||
__FILE__, __LINE__, G_GNUC_PRETTY_FUNCTION, \
|
||||
g_strerror((num)), #name )
|
||||
|
||||
#define solaris_check_for_error( what ) G_STMT_START{ \
|
||||
int error = (what); \
|
||||
if( error ) { solaris_print_error( what, error ); } \
|
||||
}G_STMT_END
|
||||
|
||||
static GMutex*
|
||||
g_mutex_new_solaris_impl()
|
||||
{
|
||||
/* we can not use g_new and friends, as they might use mutexes by
|
||||
themself */
|
||||
GMutex* result = malloc(sizeof(mutex_t));
|
||||
solaris_check_for_error( mutex_init( (mutex_t*) result, USYNC_PROCESS, 0));
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
g_mutex_free_solaris_impl(GMutex* mutex)
|
||||
{
|
||||
solaris_check_for_error( mutex_destroy((mutex_t*)mutex) );
|
||||
free (mutex);
|
||||
}
|
||||
|
||||
/* mutex_lock, mutex_unlock can be taken directly, as
|
||||
signature and semantic are right, but without error check then!!!!,
|
||||
we might want to change this therfore. */
|
||||
|
||||
static gboolean
|
||||
g_mutex_try_lock_solaris_impl(GMutex* mutex)
|
||||
{
|
||||
int result;
|
||||
result = mutex_trylock((mutex_t*)mutex);
|
||||
if( result == EBUSY )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
solaris_check_for_error( result );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GCond*
|
||||
g_cond_new_solaris_impl()
|
||||
{
|
||||
GCond* result = (GCond*) g_new0 (cond_t, 1);
|
||||
solaris_check_for_error( cond_init( (cond_t*)result, USYNC_THREAD, 0 ) );
|
||||
return result;
|
||||
}
|
||||
|
||||
/* cond_signal, cond_broadcast and cond_wait
|
||||
can be taken directly, as signature and semantic are right, but
|
||||
without error check then!!!!, we might want to change this
|
||||
therfore. */
|
||||
|
||||
#define G_MICROSEC 1000000
|
||||
#define G_NANOSEC 1000000000
|
||||
|
||||
static gboolean
|
||||
g_cond_timed_wait_solaris_impl(GCond* cond, GMutex* entered_mutex,
|
||||
GTimeVal *abs_time)
|
||||
{
|
||||
int result;
|
||||
timestruc_t end_time;
|
||||
gboolean timed_out;
|
||||
|
||||
g_assert( cond );
|
||||
g_assert( entered_mutex );
|
||||
|
||||
if( !abs_time )
|
||||
{
|
||||
g_cond_wait(cond, entered_mutex);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
end_time.tv_sec = abs_time->tv_sec;
|
||||
end_time.tv_nsec = abs_time->tv_usec * ( G_NANOSEC / G_MICROSEC );
|
||||
g_assert( end_time.tv_nsec < G_NANOSEC );
|
||||
result = cond_timedwait( (cond_t*)cond, (mutex_t*)entered_mutex, &end_time );
|
||||
timed_out = ( result == ETIME );
|
||||
if( !timed_out ) solaris_check_for_error( result );
|
||||
return !timed_out;
|
||||
}
|
||||
|
||||
static void
|
||||
g_cond_free_solaris_impl(GCond* cond)
|
||||
{
|
||||
solaris_check_for_error( cond_destroy((cond_t*)cond) );
|
||||
g_free (cond);
|
||||
}
|
||||
|
||||
static GPrivate*
|
||||
g_private_new_solaris_impl(GDestroyNotify destructor)
|
||||
{
|
||||
/* we can not use g_new and friends, as they might use mutexes by
|
||||
themself */
|
||||
GPrivate* result = malloc( sizeof(thread_key_t) );
|
||||
/* FIXME: we shouldn't use g_log here either actually */
|
||||
solaris_check_for_error( thr_keycreate( (thread_key_t*)result, destructor) );
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
g_private_set_solaris_impl(GPrivate* private, gpointer value)
|
||||
{
|
||||
g_assert( private );
|
||||
|
||||
/* FIXME: we shouldn't use g_log here actually */
|
||||
solaris_check_for_error( thr_setspecific( *(thread_key_t*)private, value) );
|
||||
}
|
||||
|
||||
gpointer
|
||||
g_private_get_solaris_impl(GPrivate* private)
|
||||
{
|
||||
gpointer result;
|
||||
|
||||
g_assert( private );
|
||||
|
||||
/* FIXME: we shouldn't use g_log here actually */
|
||||
solaris_check_for_error( thr_getspecific( *(thread_key_t*)private,
|
||||
&result) );
|
||||
return result;
|
||||
}
|
||||
|
||||
static GThreadFunctions
|
||||
g_thread_functions_for_glib_use_default = {
|
||||
g_mutex_new_solaris_impl,
|
||||
(void(*)(GMutex*))mutex_lock,
|
||||
g_mutex_try_lock_solaris_impl,
|
||||
(void(*)(GMutex*))mutex_unlock,
|
||||
g_mutex_free_solaris_impl,
|
||||
g_cond_new_solaris_impl,
|
||||
(void(*)(GCond*))cond_signal,
|
||||
(void(*)(GCond*))cond_broadcast,
|
||||
(void(*)(GCond*,GMutex*))cond_wait,
|
||||
g_cond_timed_wait_solaris_impl,
|
||||
g_cond_free_solaris_impl,
|
||||
g_private_new_solaris_impl,
|
||||
g_private_get_solaris_impl,
|
||||
g_private_set_solaris_impl
|
||||
};
|
60
gthread/gthread.c
Normal file
60
gthread/gthread.c
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
static const char *g_log_domain_gthread = "GThread";
|
||||
static gboolean thread_system_already_initialized = FALSE;
|
||||
|
||||
#include G_THREAD_SOURCE
|
||||
|
||||
gboolean
|
||||
g_thread_try_init(GThreadFunctions* init)
|
||||
{
|
||||
if (thread_system_already_initialized)
|
||||
return FALSE;
|
||||
|
||||
thread_system_already_initialized = TRUE;
|
||||
|
||||
if (init == NULL)
|
||||
{
|
||||
g_thread_use_default_impl = TRUE;
|
||||
init = &g_thread_functions_for_glib_use_default;
|
||||
}
|
||||
|
||||
g_thread_functions_for_glib_use = *init;
|
||||
|
||||
g_thread_supported =
|
||||
init->mutex_new &&
|
||||
init->mutex_lock &&
|
||||
init->mutex_try_lock &&
|
||||
init->mutex_unlock &&
|
||||
init->mutex_free &&
|
||||
init->cond_new &&
|
||||
init->cond_signal &&
|
||||
init->cond_broadcast &&
|
||||
init->cond_wait &&
|
||||
init->cond_timed_wait &&
|
||||
init->cond_free &&
|
||||
init->private_new &&
|
||||
init->private_get &&
|
||||
init->private_get;
|
||||
|
||||
/* if somebody is calling g_thread_init(), it means that he at least wants
|
||||
to have mutex support, so check this */
|
||||
|
||||
if (!g_thread_supported)
|
||||
g_error( "Mutex functions missing." );
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
g_thread_init(GThreadFunctions* init)
|
||||
{
|
||||
/* Make sure, this function is only called once. */
|
||||
if (!g_thread_try_init (init))
|
||||
g_error( "the glib thread system may only be initialized once." );
|
||||
}
|
||||
|
45
gthread/testgthread.c
Normal file
45
gthread/testgthread.c
Normal file
@@ -0,0 +1,45 @@
|
||||
#define main testglib_main
|
||||
#include <testglib.c>
|
||||
#undef main
|
||||
|
||||
void
|
||||
test_mutexes()
|
||||
{
|
||||
GMutex* mutex;
|
||||
GCond* cond;
|
||||
GStaticMutex static_mutex = G_STATIC_MUTEX_INIT;
|
||||
G_LOCK_DEFINE(test_me);
|
||||
|
||||
mutex = g_mutex_new();
|
||||
cond = g_cond_new();
|
||||
|
||||
g_mutex_lock(mutex);
|
||||
g_mutex_unlock(mutex);
|
||||
|
||||
g_static_mutex_lock(static_mutex);
|
||||
g_static_mutex_unlock(static_mutex);
|
||||
|
||||
g_cond_signal(cond);
|
||||
g_cond_broadcast(cond);
|
||||
|
||||
g_lock(test_me);
|
||||
g_unlock(test_me);
|
||||
|
||||
g_cond_free(cond);
|
||||
g_mutex_free(mutex);
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
test_mutexes();
|
||||
|
||||
g_thread_init( NULL );
|
||||
|
||||
test_mutexes();
|
||||
|
||||
/* later we might want to start n copies of that */
|
||||
testglib_main(0,NULL);
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user