This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[basic-improvements] VxWorks thread support
- From: Zack Weinberg <zack at codesourcery dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Mon, 4 Nov 2002 21:42:41 -0800
- Subject: [basic-improvements] VxWorks thread support
This patch reinstates support for threading under VxWorks. It is not
complete - one must also add a chunk of code to the operating system.
More on that in a separate message.
I am open to better suggestions for how to deal with t-vxworks and
t-ppccomm fighting over LIB2FUNCS_EXTRA.
Tested by building a i686-linux -> powerpc-vxworks cross compiler, C
and C++ only, including a build of libgcc.a. I haven't yet verified
that the binutils powerpc-vxworks is sane, so the assembler and linker
were configured for a powerpc-eabisim target. (That verification is
next on my list.)
Applied to basic-improvements branch.
zw
* gthr-vxworks.h: Rewritten from scratch.
* config/vxlib.c: New file.
* config/t-vxworks: Add config/vxlib.c to LIB2FUNCS_EXTRA.
* config/rs6000/t-vxworks: Add config/vxlib.c to
LIB2FUNCS_EXTRA here too, because of clash with
config/rs6000/t-ppccomm.
===================================================================
Index: gthr-vxworks.h
--- gthr-vxworks.h 29 Oct 2002 05:08:20 -0000 1.10.20.1
+++ gthr-vxworks.h 5 Nov 2002 05:38:29 -0000
@@ -30,7 +30,74 @@ Software Foundation, 59 Temple Place - S
#ifndef GCC_GTHR_VXWORKS_H
#define GCC_GTHR_VXWORKS_H
-/* Temporarily on hiatus. */
-#include "gthr-single.h"
+#ifdef _LIBOBJC
-#endif
+/* libobjc requires the optional pthreads component. */
+#include "gthr-posix.h"
+
+#else
+
+#define __GTHREADS 1
+#define __gthread_active_p() 1
+
+/* Mutexes are easy, except that they need to be initialized at runtime. */
+
+#include <semLib.h>
+
+typedef SEM_ID __gthread_mutex_t;
+#define __GTHREAD_MUTEX_INIT_FUNCTION __gthread_mutex_init_function
+
+static inline void
+__gthread_mutex_init_function (__gthread_mutex_t *mutex)
+{
+ *mutex = semMCreate (SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE);
+}
+
+static inline int
+__gthread_mutex_lock (__gthread_mutex_t *mutex)
+{
+ return semTake (*mutex, WAIT_FOREVER);
+}
+
+static inline int
+__gthread_mutex_trylock (__gthread_mutex_t *mutex)
+{
+ return semTake (*mutex, NO_WAIT);
+}
+
+static inline int
+__gthread_mutex_unlock (__gthread_mutex_t *mutex)
+{
+ return semGive (*mutex);
+}
+
+/* pthread_once is complicated enough that it's implemented
+ out-of-line. See config/vxlib.c. */
+
+typedef struct
+{
+ volatile unsigned char busy;
+ volatile unsigned char done;
+}
+__gthread_once_t;
+
+#define __GTHREAD_ONCE_INIT { 0, 0 }
+
+extern int __gthread_once (__gthread_once_t *once, void (*func)(void));
+
+/* Thread-specific data requires a great deal of effort, since VxWorks
+ is not really set up for it. See config/vxlib.c for the gory
+ details. All the TSD routines are sufficiently complex that they
+ need to be implemented out of line. */
+
+typedef unsigned int __gthread_key_t;
+
+extern int __gthread_key_create (__gthread_key_t *keyp, void (*dtor)(void *));
+extern int __gthread_key_delete (__gthread_key_t key);
+
+extern void *__gthread_getspecific (__gthread_key_t key);
+extern int __gthread_setspecific (__gthread_key_t key, void *ptr);
+
+#endif /* not _LIBOBJC */
+
+#endif /* gthr-vxworks.h */
===================================================================
Index: config/t-vxworks
--- config/t-vxworks 29 Oct 2002 08:09:20 -0000 1.1.2.1
+++ config/t-vxworks 5 Nov 2002 05:38:29 -0000
@@ -15,6 +15,9 @@ TARGET_LIBGCC2_CFLAGS =
# Don't build libgcc.a with debug info
LIBGCC2_DEBUG_CFLAGS =
+# Extra libgcc2 module used by gthr-vxworks.h functions
+LIB2FUNCS_EXTRA = $(srcdir)/config/vxlib.c
+
# This ensures that the correct target headers are used; some
# VxWorks system headers have names that collide with GCC's
# internal (host) headers, e.g. regs.h.
===================================================================
Index: config/vxlib.c
--- config/vxlib.c 1 Jan 1970 00:00:00 -0000
+++ config/vxlib.c 5 Nov 2002 05:38:29 -0000
@@ -0,0 +1,310 @@
+/* Copyright (C) 2002 Free Software Foundation, Inc.
+ Contributed by Zack Weinberg <zack@codesourcery.com>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+GCC 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 General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+/* Threads compatibility routines for libgcc2 for VxWorks.
+ These are out-of-line routines called from gthr-vxworks.h. */
+
+/* As a special exception, if you link this library with other files,
+ some of which are compiled with GCC, to produce an executable,
+ this library does not by itself cause the resulting executable
+ to be covered by the GNU General Public License.
+ This exception does not however invalidate any other reasons why
+ the executable file might be covered by the GNU General Public License. */
+
+#include "tconfig.h"
+#include "tsystem.h"
+#include "gthr.h"
+
+#include <vxWorks.h>
+#include <vxLib.h>
+#include <taskLib.h>
+#include <taskHookLib.h>
+
+/* Init-once operation.
+
+ This would be a clone of the implementation from gthr-solaris.h,
+ except that we have a bootstrap problem - the whole point of this
+ exercise is to prevent double initialization, but if two threads
+ are racing with each other, once->mutex is liable to be initialized
+ by both. Then each thread will lock its own mutex, and proceed to
+ call the initialization routine.
+
+ So instead we use a bare atomic primitive (vxTas()) to handle
+ mutual exclusion. Threads losing the race then busy-wait, calling
+ taskDelay() to yield the processor, until the initialization is
+ completed. Inefficient, but reliable. */
+
+int
+__gthread_once (__gthread_once_t *guard, void (*func)(void))
+{
+ if (guard->done)
+ return 0;
+
+ while (!vxTas ((void *)&guard->busy))
+ taskDelay (1);
+
+ /* Only one thread at a time gets here. Check ->done again, then
+ go ahead and call func() if no one has done it yet. */
+ if (!guard->done)
+ {
+ func ();
+ guard->done = 1;
+ }
+
+ guard->busy = 0;
+ return 0;
+}
+
+/* Thread-specific data.
+
+ We reserve a field in the TCB to point to a dynamically allocated
+ array which is used to store TSD values. A TSD key is simply an
+ offset in this array. The exact location of the TCB field is not
+ known to this code nor to vxlib.c -- all access to it indirects
+ through the routines __gthread_get_tsd_data and
+ __gthread_set_tsd_data, which are provided by the VxWorks kernel.
+
+ There is also a global array which records which keys are valid and
+ which have destructors.
+
+ A task delete hook is installed to execute key destructors. The
+ routines __gthread_enter_tsd_dtor_context and
+ __gthread_leave_tsd_dtor_context, which are also provided by the
+ kernel, ensure that it is safe to call free() on memory allocated
+ by the task being deleted. (This is a no-op on VxWorks 5, but
+ a major undertaking on AE.)
+
+ Since this interface is used to allocate only a small number of
+ keys, the table size is small and static, which simplifies the
+ code quite a bit. Revisit this if and when it becomes necessary. */
+
+#define MAX_KEYS 4
+
+/* This is the structure pointed to by the pointer returned
+ by __gthread_get_tsd_data. It must match the definition
+ in the kernel file defining __gthread_get_tsd_data. */
+struct tsd_data
+{
+ void *values[MAX_KEYS];
+};
+
+
+/* kernel provided routines */
+extern void *__gthread_get_tsd_data (WIND_TCB *tcb);
+extern void __gthread_set_tsd_data (WIND_TCB *tcb, void *data);
+
+extern void __gthread_enter_tsd_dtor_context (WIND_TCB *tcb);
+extern void __gthread_leave_tsd_dtor_context (WIND_TCB *tcb);
+
+typedef void (*fet_callback_t) (WIND_TCB *, unsigned int);
+extern void __gthread_for_all_tasks (fet_callback_t fun, unsigned int number);
+
+/* This is a global structure which records all of the active keys. A
+ key with NULL dtor is valid; a key with dtor == (void (*)(void))-1
+ is valid, but has no dtor. */
+
+typedef void (*tsd_dtor) (void *);
+static tsd_dtor tsd_dtors[MAX_KEYS];
+#define KEY_INVALID ((tsd_dtor) 0)
+#define KEY_NO_DTOR ((tsd_dtor) -1)
+
+static __gthread_once_t tsd_init_guard = __GTHREAD_ONCE_INIT;
+static __gthread_mutex_t tsd_lock;
+
+/* Internal routines. */
+
+/* The task TCB has just been deleted. Call the destructor
+ function for each TSD key that has both a destructor and
+ a non-NULL specific value in this thread. */
+
+static void
+tsd_delete_hook (WIND_TCB *tcb)
+{
+ struct tsd_data *data = __gthread_get_tsd_data (tcb);
+ __gthread_key_t key;
+
+ if (data)
+ {
+ __gthread_enter_tsd_dtor_context (tcb);
+ for (key = 0; key < MAX_KEYS; key++)
+ {
+ if (tsd_dtors[key] == KEY_INVALID || tsd_dtors[key] == KEY_NO_DTOR)
+ continue;
+ if (data->values[key])
+ tsd_dtors[key] (data->values[key]);
+ }
+ free (data);
+ __gthread_set_tsd_data (tcb, 0);
+ __gthread_leave_tsd_dtor_context (tcb);
+ }
+}
+
+/* Initialize global data used by the TSD system. */
+static void
+tsd_init (void)
+{
+ taskDeleteHookAdd ((FUNCPTR)tsd_delete_hook);
+ __GTHREAD_MUTEX_INIT_FUNCTION (&tsd_lock);
+}
+
+/* External interface */
+
+/* Store in KEYP a value which can be passed to __gthread_setspecific/
+ __gthread_getspecific to store and retrive a value which is
+ specific to each calling thread. If DTOR is not NULL, it will be
+ called when a thread terminates with a non-NULL specific value for
+ this key, with the value as its sole argument. */
+
+int
+__gthread_key_create (__gthread_key_t *keyp, tsd_dtor dtor)
+{
+ __gthread_key_t key;
+
+ __gthread_once (&tsd_init_guard, tsd_init);
+
+ if (__gthread_mutex_lock (&tsd_lock) == ERROR)
+ return errno;
+
+ for (key = 0; key < MAX_KEYS; key++)
+ if (tsd_dtors[key] == KEY_INVALID)
+ goto found_slot;
+
+ /* no room */
+ __gthread_mutex_unlock (&tsd_lock);
+ return EAGAIN;
+
+ found_slot:
+ tsd_dtors[key] = dtor ? dtor : KEY_NO_DTOR;
+ *keyp = key;
+ __gthread_mutex_unlock (&tsd_lock);
+ return 0;
+}
+
+/* Subroutine of __gthread_key_delete. Clear the thread-specific
+ value associated with key INDEX in TCB. */
+
+static void
+key_delete_iter (WIND_TCB *tcb, unsigned int index)
+{
+ struct tsd_data *data = __gthread_get_tsd_data (tcb);
+ if (data)
+ data->values[index] = 0;
+}
+
+/* Invalidate KEY; it can no longer be used as an argument to
+ setspecific/getspecific. Note that this does NOT call destructor
+ functions for any live values for this key. */
+int
+__gthread_key_delete (__gthread_key_t key)
+{
+ if (key >= MAX_KEYS)
+ return EINVAL;
+
+ __gthread_once (&tsd_init_guard, tsd_init);
+
+ if (__gthread_mutex_lock (&tsd_lock) == ERROR)
+ return errno;
+
+ if (tsd_dtors[key] == KEY_INVALID)
+ {
+ __gthread_mutex_unlock (&tsd_lock);
+ return EINVAL;
+ }
+
+ __gthread_for_all_tasks (key_delete_iter, key);
+
+ tsd_dtors[key] = KEY_INVALID;
+
+ __gthread_mutex_unlock (&tsd_lock);
+ return 0;
+}
+
+/* Retrieve the thread-specific value for KEY. If it has never been
+ set in this thread, or KEY is invalid, returns NULL.
+
+ It does not matter if this function races with key_create or
+ key_delete; the worst that can happen is you get a value other than
+ the one that a serialized implementation would have provided. */
+
+void *
+__gthread_getspecific (__gthread_key_t key)
+{
+ struct tsd_data *data;
+
+ if (key >= MAX_KEYS || tsd_dtors[key] == KEY_INVALID)
+ return 0;
+
+ data = __gthread_get_tsd_data (taskTcb (taskIdSelf ()));
+
+ if (data)
+ return data->values[key];
+ else
+ return 0;
+}
+
+/* Set the thread-specific value for KEY. If KEY is invalid, or
+ memory allocation fails, returns -1, otherwise 0.
+
+ It does matter if this function races with key_create/key_delete,
+ so we have to take the lock, which should be okay since this is
+ called much less often than getspecific (usually once per key
+ per thread). */
+
+int
+__gthread_setspecific (__gthread_key_t key, void *value)
+{
+ struct tsd_data *data;
+ WIND_TCB *tcb;
+
+ if (key >= MAX_KEYS)
+ return EINVAL;
+
+ __gthread_once (&tsd_init_guard, tsd_init);
+
+ if (__gthread_mutex_lock (&tsd_lock) == ERROR)
+ return errno;
+
+ if (tsd_dtors[key] == KEY_INVALID)
+ {
+ __gthread_mutex_unlock (&tsd_lock);
+ return EINVAL;
+ }
+
+ tcb = taskTcb (taskIdSelf ());
+ data = __gthread_get_tsd_data (tcb);
+ if (!data)
+ {
+ data = calloc (MAX_KEYS, sizeof (void *));
+ if (!data)
+ {
+ __gthread_mutex_unlock (&tsd_lock);
+ return ENOMEM;
+ }
+ __gthread_set_tsd_data (tcb, data);
+ }
+
+ data->values[key] = value;
+
+ __gthread_mutex_unlock (&tsd_lock);
+ return 0;
+}
+
+
+
===================================================================
Index: config/rs6000/t-vxworks
--- config/rs6000/t-vxworks 29 Oct 2002 08:09:20 -0000 1.1.2.1
+++ config/rs6000/t-vxworks 5 Nov 2002 05:38:29 -0000
@@ -5,3 +5,6 @@ MULTILIB_DIRNAMES = PPC403gnu PPC405gnu
PPC603gnu PPC604gnu PPC860gnu
MULTILIB_MATCHES = t604=
+
+# Put vxlib.c back in LIB2FUNCS_EXTRA (t-ppccomm clobbers it).
+LIB2FUNCS_EXTRA += $(srcdir)/config/vxlib.c