mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-06 01:13:40 +02:00
2000-11-21 Sebastian Wilhelmi <wilhelmi@ira.uka.de> * configure.in: Add a surrogate for thread priorities using PID niceness for systems with no thread priorities and different PIDs for threads of the same process (most notably: Linux). Define G_THREAD_USE_PID_SURROGATE in that case, as used by gthread-posix.c. Also make the system thread bigger by sizeof (long) to contain the thread's PID. * gfileutils.c: Include stdlib.h for mkstemp prototype. * gthread.c: Add priority range checks to the affected functions. * gthreadpool.c: Remove unused variable. * gthread-impl.c, gthread-posix.c, gthread-solaris.c: Removed g_thread_map_priority function in favour of the g_thread_priority_map array. Initialize the array with PRIORITY_{...}_VALUE, if available and interpolate beetween the bounds if .._NORMAL_.. and .._HIGH_.. are not available. * gthread-posix.c: If we should use the PID niceness as a surrogate for thread priorities (G_THREAD_USE_PID_SURROGATE is defined), then disable normal priority handling and use PIDs and setpriority() instead. Depends on the thread to write its PID into the place after the thread id right after thread creation.
627 lines
16 KiB
C
627 lines
16 KiB
C
/* 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
|
|
* 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"
|
|
#include "glib.h"
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#if GLIB_SIZEOF_SYSTEM_THREAD == SIZEOF_VOID_P
|
|
# define g_system_thread_equal(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(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 */
|
|
|
|
GQuark
|
|
g_thread_error_quark()
|
|
{
|
|
static GQuark quark;
|
|
if (!quark)
|
|
quark = g_quark_from_static_string ("g_thread_error");
|
|
return quark;
|
|
}
|
|
|
|
typedef struct _GRealThread GRealThread;
|
|
struct _GRealThread
|
|
{
|
|
GThread thread;
|
|
GThreadFunc func;
|
|
gpointer arg;
|
|
gpointer private_data;
|
|
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;
|
|
|
|
#if defined(G_OS_WIN32) && defined(__GNUC__)
|
|
__declspec(dllexport)
|
|
#endif
|
|
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_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)
|
|
{
|
|
GRealThread* main_thread;
|
|
|
|
/* We let the main thread (the one that calls g_thread_init) inherit
|
|
* the data, that it set before calling g_thread_init
|
|
*/
|
|
main_thread = (GRealThread*) g_thread_self ();
|
|
|
|
g_thread_specific_private = g_private_new (g_thread_cleanup);
|
|
G_THREAD_UF (private_set, (g_thread_specific_private, main_thread));
|
|
G_THREAD_UF (thread_self, (&main_thread->system_thread));
|
|
|
|
g_mutex_protect_static_mutex_allocation = g_mutex_new();
|
|
g_thread_specific_mutex = g_mutex_new();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
g_return_if_fail (mutex);
|
|
|
|
if (!g_thread_supported ())
|
|
return;
|
|
|
|
g_static_mutex_lock (&mutex->mutex);
|
|
G_THREAD_UF (thread_self, (&mutex->owner));
|
|
mutex->depth = depth;
|
|
}
|
|
|
|
guint
|
|
g_static_rec_mutex_unlock_full (GStaticRecMutex *mutex)
|
|
{
|
|
gint 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;
|
|
}
|
|
|
|
|
|
gpointer
|
|
g_static_private_get (GStaticPrivate *private_key)
|
|
{
|
|
return g_static_private_get_for_thread (private_key, g_thread_self ());
|
|
}
|
|
|
|
gpointer
|
|
g_static_private_get_for_thread (GStaticPrivate *private_key,
|
|
GThread *thread)
|
|
{
|
|
GArray *array;
|
|
GRealThread *self = (GRealThread*) thread;
|
|
|
|
g_return_val_if_fail (thread, NULL);
|
|
|
|
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)
|
|
{
|
|
g_static_private_set_for_thread (private_key, g_thread_self (),
|
|
data, notify);
|
|
}
|
|
|
|
void
|
|
g_static_private_set_for_thread (GStaticPrivate *private_key,
|
|
GThread *thread,
|
|
gpointer data,
|
|
GDestroyNotify notify)
|
|
{
|
|
GArray *array;
|
|
GRealThread *self =(GRealThread*) thread;
|
|
static guint next_index = 0;
|
|
GStaticPrivateNode *node;
|
|
|
|
g_return_if_fail (thread);
|
|
|
|
array = self->private_data;
|
|
if (!array)
|
|
{
|
|
array = g_array_new (FALSE, TRUE, sizeof (GStaticPrivateNode));
|
|
self->private_data = array;
|
|
}
|
|
|
|
if (!private_key->index)
|
|
{
|
|
g_mutex_lock (g_thread_specific_mutex);
|
|
|
|
if (!private_key->index)
|
|
private_key->index = ++next_index;
|
|
|
|
g_mutex_unlock (g_thread_specific_mutex);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
/* 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.");
|
|
}
|
|
|
|
G_LOCK_DEFINE_STATIC (g_thread_create);
|
|
|
|
static void
|
|
g_thread_create_proxy (gpointer data)
|
|
{
|
|
GRealThread* thread = data;
|
|
|
|
g_assert (data);
|
|
|
|
/* the lock makes sure, that thread->system_thread is written,
|
|
before thread->func is called. See g_thread_create */
|
|
|
|
G_LOCK (g_thread_create);
|
|
g_private_set (g_thread_specific_private, data);
|
|
G_UNLOCK (g_thread_create);
|
|
|
|
thread->func (thread->arg);
|
|
}
|
|
|
|
GThread*
|
|
g_thread_create (GThreadFunc thread_func,
|
|
gpointer arg,
|
|
gulong stack_size,
|
|
gboolean joinable,
|
|
gboolean bound,
|
|
GThreadPriority priority,
|
|
GError **error)
|
|
{
|
|
GRealThread* result = g_new (GRealThread, 1);
|
|
GError *local_error = NULL;
|
|
g_return_val_if_fail (thread_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->thread.joinable = joinable;
|
|
result->thread.bound = bound;
|
|
result->thread.priority = priority;
|
|
result->func = thread_func;
|
|
result->arg = arg;
|
|
result->private_data = NULL;
|
|
G_LOCK (g_thread_create);
|
|
G_THREAD_UF (thread_create, (g_thread_create_proxy, result,
|
|
stack_size, joinable, bound, priority,
|
|
&result->system_thread, &local_error));
|
|
G_UNLOCK (g_thread_create);
|
|
|
|
if (local_error)
|
|
{
|
|
g_propagate_error (error, local_error);
|
|
g_free (result);
|
|
return NULL;
|
|
}
|
|
|
|
return (GThread*) result;
|
|
}
|
|
|
|
void
|
|
g_thread_join (GThread* thread)
|
|
{
|
|
GRealThread* real = (GRealThread*) thread;
|
|
|
|
|
|
g_return_if_fail (thread);
|
|
g_return_if_fail (thread->joinable);
|
|
g_return_if_fail (!g_system_thread_equal (real->system_thread, zero_thread));
|
|
|
|
G_THREAD_UF (thread_join, (&real->system_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 loose end,
|
|
if a joinable thread is not joined. */
|
|
|
|
g_free (thread);
|
|
}
|
|
|
|
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()
|
|
{
|
|
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_new (GRealThread, 1);
|
|
thread->thread.joinable = FALSE; /* This is a save guess */
|
|
thread->thread.bound = TRUE; /* This isn't important at all */
|
|
thread->thread.priority = G_THREAD_PRIORITY_NORMAL; /* This is
|
|
just a guess */
|
|
thread->func = NULL;
|
|
thread->arg = 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);
|
|
}
|
|
|
|
return (GThread*)thread;
|
|
}
|
|
|
|
static void inline 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));
|
|
}
|
|
|
|
static void inline g_static_rw_lock_signal (GStaticRWLock* lock)
|
|
{
|
|
if (lock->want_to_write && lock->write_cond)
|
|
g_cond_signal (lock->write_cond);
|
|
else if (lock->read_cond)
|
|
g_cond_signal (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);
|
|
while (lock->write || lock->want_to_write)
|
|
g_static_rw_lock_wait (&lock->read_cond, &lock->mutex);
|
|
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->write && !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--;
|
|
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->write || lock->read_counter)
|
|
g_static_rw_lock_wait (&lock->write_cond, &lock->mutex);
|
|
lock->want_to_write--;
|
|
lock->write = 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->write && !lock->read_counter)
|
|
{
|
|
lock->write = 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->write = 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);
|
|
if (lock->write_cond)
|
|
g_cond_free (lock->write_cond);
|
|
|
|
}
|
|
|