Improve the thread support for VxWorks
authorCorentin Gay <gay@adacore.com>
Thu, 14 Nov 2019 15:58:31 +0000 (15:58 +0000)
committerOlivier Hainque <hainque@gcc.gnu.org>
Thu, 14 Nov 2019 15:58:31 +0000 (15:58 +0000)
2019-11-12  Corentin Gay  <gay@adacore.com>
    Jerome Lambourg  <lambourg@adacore.com>
    Olivier Hainque  <hainque@adacore.com>

libgcc/

* config/t-gthr-vxworks: New file, add all the gthr-vxworks
sources to LIB2ADDEH.
* config/t-vxworks: Remove adjustments to LIB2ADDEH.
* config/t-vxworks7: Likewise.

* config.host: Append a block at the end of the file to add the
t-gthr files to the tmake_file list for VxWorks after everything
else.

* config/vxlib.c: Rename as gthr-vxworks.c.
* config/vxlib-tls.c: Rename as gthr-vxworks-tls.c.

* config/gthr-vxworks.h: Simplify a few comments.  Expose a TAS
API and a basic error checking API, both internal.  Simplify the
__gthread_once_t type definition and initializers.  Add sections
for condition variables support and for the C++0x thread support,
conditioned against Vx653 for the latter.

* config/gthr-vxworks.c (__gthread_once): Simplify comments and
implementation, leveraging the TAS internal API.
* config/gthr-vxworks-tls.c: Introduce an internal TLS data access
API, leveraging the general availability of TLS services in VxWorks7
post SR6xxx.
(__gthread_setspecific, __gthread_setspecific): Use it.
(tls_delete_hook): Likewise, and simplify the enter/leave dtor logic.
* config/gthr-vxworks-cond.c: New file.  GTHREAD_COND variable
support based on VxWorks primitives.
* config/gthr-vxworks-thread.c: New file.  GTHREAD_CXX0X support
based on VxWorks primitives.

Co-Authored-By: Jerome Lambourg <lambourg@adacore.com>
Co-Authored-By: Olivier Hainque <hainque@adacore.com>
From-SVN: r278249

12 files changed:
libgcc/ChangeLog
libgcc/config.host
libgcc/config/gthr-vxworks-cond.c [new file with mode: 0644]
libgcc/config/gthr-vxworks-thread.c [new file with mode: 0644]
libgcc/config/gthr-vxworks-tls.c [new file with mode: 0644]
libgcc/config/gthr-vxworks.c [new file with mode: 0644]
libgcc/config/gthr-vxworks.h
libgcc/config/t-gthr-vxworks [new file with mode: 0644]
libgcc/config/t-vxworks
libgcc/config/t-vxworks7
libgcc/config/vxlib-tls.c [deleted file]
libgcc/config/vxlib.c [deleted file]

index 845dbd0c11a067215bdb1897193045e84894ffa7..21efeb53dbe7bba36215f7a9261c18160e46c820 100644 (file)
@@ -1,3 +1,37 @@
+2019-11-12  Corentin Gay  <gay@adacore.com>
+           Jerome Lambourg  <lambourg@adacore.com>
+           Olivier Hainque  <hainque@adacore.com>
+
+       * config/t-gthr-vxworks: New file, add all the gthr-vxworks
+       sources to LIB2ADDEH.
+       * config/t-vxworks: Remove adjustments to LIB2ADDEH.
+       * config/t-vxworks7: Likewise.
+
+       * config.host: Append a block at the end of the file to add the
+       t-gthr files to the tmake_file list for VxWorks after everything
+       else.
+
+       * config/vxlib.c: Rename as gthr-vxworks.c.
+       * config/vxlib-tls.c: Rename as gthr-vxworks-tls.c.
+
+       * config/gthr-vxworks.h: Simplify a few comments.  Expose a TAS
+       API and a basic error checking API, both internal.  Simplify the
+       __gthread_once_t type definition and initializers.  Add sections
+       for condition variables support and for the C++0x thread support,
+       conditioned against Vx653 for the latter.
+
+       * config/gthr-vxworks.c (__gthread_once): Simplify comments and
+       implementation, leveraging the TAS internal API.
+       * config/gthr-vxworks-tls.c: Introduce an internal TLS data access
+       API, leveraging the general availability of TLS services in VxWorks7
+       post SR6xxx.
+       (__gthread_setspecific, __gthread_setspecific): Use it.
+       (tls_delete_hook): Likewise, and simplify the enter/leave dtor logic.
+       * config/gthr-vxworks-cond.c: New file.  GTHREAD_COND variable
+       support based on VxWorks primitives.
+       * config/gthr-vxworks-thread.c: New file.  GTHREAD_CXX0X support
+       based on VxWorks primitives.
+
 2019-11-06  Jerome Lambourg  <lambourg@adacore.com>
            Olivier Hainque  <hainque@adacore.com>
 
index b2004afb02f76dbea1cd9fe77b49b87025585dae..8a090bdb54a8f7be253d53e7af8e1419b2582206 100644 (file)
@@ -1513,3 +1513,15 @@ aarch64*-*-*)
        tm_file="${tm_file} aarch64/value-unwind.h"
        ;;
 esac
+
+# The vxworks threads implementation relies on a few extra sources,
+# which we arrange to add after everything else:
+
+case ${target_thread_file} in
+vxworks)
+       case ${host} in
+       *-*-vxworks*)
+               tmake_file="${tmake_file} t-gthr-vxworks"
+               ;;
+       esac
+esac
diff --git a/libgcc/config/gthr-vxworks-cond.c b/libgcc/config/gthr-vxworks-cond.c
new file mode 100644 (file)
index 0000000..0747a3d
--- /dev/null
@@ -0,0 +1,83 @@
+/* Copyright (C) 2002-2019 Free Software Foundation, Inc.
+
+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 3, 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+/* Threads compatibility routines for libgcc2 for VxWorks.
+
+   This file implements the GTHREAD_HAS_COND part of the interface
+   exposed by gthr-vxworks.h.  */
+
+#include "gthr.h"
+#include <taskLib.h>
+
+/* --------------------------- Condition Variables ------------------------ */
+
+void
+__gthread_cond_init (__gthread_cond_t *cond)
+{
+  if (!cond)
+    return;
+  *cond = semBCreate (SEM_Q_FIFO, SEM_EMPTY);
+}
+
+int
+__gthread_cond_destroy (__gthread_cond_t *cond)
+{
+  if (!cond)
+    return ERROR;
+  return __CHECK_RESULT (semDelete (*cond));
+}
+
+int
+__gthread_cond_broadcast (__gthread_cond_t *cond)
+{
+  if (!cond)
+    return ERROR;
+
+  return __CHECK_RESULT (semFlush (*cond));
+}
+
+int
+__gthread_cond_wait (__gthread_cond_t *cond,
+                    __gthread_mutex_t *mutex)
+{
+  if (!cond)
+    return ERROR;
+
+  if (!mutex)
+    return ERROR;
+
+  __RETURN_ERRNO_IF_NOT_OK (semGive (*mutex));
+
+  __RETURN_ERRNO_IF_NOT_OK (semTake (*cond, WAIT_FOREVER));
+
+  __RETURN_ERRNO_IF_NOT_OK (semTake (*mutex, WAIT_FOREVER));
+
+  return OK;
+}
+
+int
+__gthread_cond_wait_recursive (__gthread_cond_t *cond,
+                              __gthread_recursive_mutex_t *mutex)
+{
+  return __gthread_cond_wait (cond, mutex);
+}
diff --git a/libgcc/config/gthr-vxworks-thread.c b/libgcc/config/gthr-vxworks-thread.c
new file mode 100644 (file)
index 0000000..3c880ba
--- /dev/null
@@ -0,0 +1,349 @@
+/* Copyright (C) 2002-2019 Free Software Foundation, Inc.
+
+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 3, 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+/* Threads compatibility routines for libgcc2 for VxWorks.
+
+   This file implements the GTHREAD_CXX0X part of the interface
+   exposed by gthr-vxworks.h, using APIs exposed by regular (!AE/653)
+   VxWorks kernels.  */
+
+#include "gthr.h"
+#include <taskLib.h>
+
+#define __TIMESPEC_TO_NSEC(timespec) \
+  ((long long)timespec.tv_sec * 1000000000 + (long long)timespec.tv_nsec)
+
+#define __TIMESPEC_TO_TICKS(timespec) \
+  ((long long)(sysClkRateGet() * __TIMESPEC_TO_NSEC(timespec) + 999999999) \
+    / 1000000000)
+
+#ifdef __RTP__
+  void tls_delete_hook ();
+  #define __CALL_DELETE_HOOK(tcb) tls_delete_hook()
+#else
+  /* In kernel mode, we need to pass the TCB to task_delete_hook. The TCB is
+     the pointer to the WIND_TCB structure and is the ID of the task.  */
+  void tls_delete_hook (void *TCB);
+  #define __CALL_DELETE_HOOK(tcb) tls_delete_hook((WIND_TCB *) ((tcb)->task_id))
+#endif
+
+/* -------------------- Timed Condition Variables --------------------- */
+
+int
+__gthread_cond_signal (__gthread_cond_t *cond)
+{
+  if (!cond)
+    return ERROR;
+
+  return __CHECK_RESULT (semGive (*cond));
+}
+
+int
+__gthread_cond_timedwait (__gthread_cond_t *cond,
+                         __gthread_mutex_t *mutex,
+                         const __gthread_time_t *abs_timeout)
+{
+  if (!cond)
+    return ERROR;
+
+  if (!mutex)
+    return ERROR;
+
+  if (!abs_timeout)
+    return ERROR;
+
+  struct timespec current;
+  if (clock_gettime (CLOCK_REALTIME, &current) == ERROR)
+    /* CLOCK_REALTIME is not supported.  */
+    return ERROR;
+
+  const long long abs_timeout_ticks = __TIMESPEC_TO_TICKS ((*abs_timeout));
+  const long long current_ticks = __TIMESPEC_TO_TICKS (current);
+
+  long long waiting_ticks;
+
+  if (current_ticks < abs_timeout_ticks)
+    waiting_ticks = abs_timeout_ticks - current_ticks;
+  else
+    /* The point until we would need to wait is in the past,
+       no need to wait at all.  */
+    waiting_ticks = 0;
+
+  /* We check that waiting_ticks can be safely casted as an int.  */
+  if (waiting_ticks > INT_MAX)
+    waiting_ticks = INT_MAX;
+
+  __RETURN_ERRNO_IF_NOT_OK (semGive (*mutex));
+
+  __RETURN_ERRNO_IF_NOT_OK (semTake (*cond, waiting_ticks));
+
+  __RETURN_ERRNO_IF_NOT_OK (semTake (*mutex, WAIT_FOREVER));
+
+  return OK;
+}
+
+/* --------------------------- Timed Mutexes ------------------------------ */
+
+int
+__gthread_mutex_timedlock (__gthread_mutex_t *m,
+                          const __gthread_time_t *abs_time)
+{
+  if (!m)
+    return ERROR;
+
+  if (!abs_time)
+    return ERROR;
+
+  struct timespec current;
+  if (clock_gettime (CLOCK_REALTIME, &current) == ERROR)
+    /* CLOCK_REALTIME is not supported.  */
+    return ERROR;
+
+  const long long abs_timeout_ticks = __TIMESPEC_TO_TICKS ((*abs_time));
+  const long long current_ticks = __TIMESPEC_TO_TICKS (current);
+  long long waiting_ticks;
+
+  if (current_ticks < abs_timeout_ticks)
+    waiting_ticks = abs_timeout_ticks - current_ticks;
+  else
+    /* The point until we would need to wait is in the past,
+       no need to wait at all.  */
+    waiting_ticks = 0;
+
+  /* Make sure that waiting_ticks can be safely casted as an int.  */
+  if (waiting_ticks > INT_MAX)
+    waiting_ticks = INT_MAX;
+
+  return __CHECK_RESULT (semTake (*m, waiting_ticks));
+}
+
+int
+__gthread_recursive_mutex_timedlock (__gthread_recursive_mutex_t *mutex,
+                                    const __gthread_time_t *abs_timeout)
+{
+  return __gthread_mutex_timedlock ((__gthread_mutex_t *)mutex, abs_timeout);
+}
+
+/* ------------------------------ Threads --------------------------------- */
+
+/* Task control block initialization and destruction functions.  */
+
+int
+__init_gthread_tcb (__gthread_t __tcb)
+{
+  if (!__tcb)
+    return ERROR;
+
+  __gthread_mutex_init (&(__tcb->return_value_available));
+  if (__tcb->return_value_available == SEM_ID_NULL)
+    return ERROR;
+
+  __gthread_mutex_init (&(__tcb->delete_ok));
+  if (__tcb->delete_ok == SEM_ID_NULL)
+    goto return_sem_delete;
+
+  /* We lock the two mutexes used for signaling.  */
+  if (__gthread_mutex_lock (&(__tcb->delete_ok)) != OK)
+    goto delete_sem_delete;
+
+  if (__gthread_mutex_lock (&(__tcb->return_value_available)) != OK)
+    goto delete_sem_delete;
+
+  __tcb->task_id = TASK_ID_NULL;
+  return OK;
+
+delete_sem_delete:
+  semDelete (__tcb->delete_ok);
+return_sem_delete:
+  semDelete (__tcb->return_value_available);
+  return ERROR;
+}
+
+/* Here, we pass a pointer to a tcb to allow calls from
+   cleanup attributes.  */
+void
+__delete_gthread_tcb (__gthread_t* __tcb)
+{
+  semDelete ((*__tcb)->return_value_available);
+  semDelete ((*__tcb)->delete_ok);
+  free (*__tcb);
+}
+
+/* This __gthread_t stores the address of the TCB malloc'ed in
+   __gthread_create.  It is then accessible via __gthread_self().  */
+__thread __gthread_t __local_tcb = NULL;
+
+__gthread_t
+__gthread_self (void)
+{
+  if (!__local_tcb)
+    {
+      /* We are in the initial thread, we need to initialize the TCB.  */
+      __local_tcb = malloc (sizeof (*__local_tcb));
+      if (!__local_tcb)
+       return NULL;
+
+      if (__init_gthread_tcb (__local_tcb) != OK)
+       {
+         __delete_gthread_tcb (&__local_tcb);
+         return NULL;
+       }
+      /* We do not set the mutexes in the structure as a thread is not supposed
+         to join or detach himself.  */
+      __local_tcb->task_id = taskIdSelf ();
+    }
+  return __local_tcb;
+}
+
+int
+__task_wrapper (__gthread_t tcb, FUNCPTR __func, _Vx_usr_arg_t __args)
+{
+  if (!tcb)
+    return ERROR;
+
+  __local_tcb = tcb;
+
+  /* We use this variable to avoid memory leaks in the case where
+     the underlying function throws an exception.  */
+  __attribute__ ((cleanup (__delete_gthread_tcb))) __gthread_t __tmp = tcb;
+
+  void *return_value = (void *) __func (__args);
+  tcb->return_value = return_value;
+
+  /* Call the destructors.  */
+  __CALL_DELETE_HOOK (tcb);
+
+  /* Future calls of join() will be able to retrieve the return value.  */
+  __gthread_mutex_unlock (&tcb->return_value_available);
+
+  /* We wait for the thread to be joined or detached.  */
+  __gthread_mutex_lock (&(tcb->delete_ok));
+  __gthread_mutex_unlock (&(tcb->delete_ok));
+
+  /* Memory deallocation is done by the cleanup attribute of the tmp variable.  */
+
+  return OK;
+}
+
+/* Proper gthreads API.  */
+
+int
+__gthread_create (__gthread_t * __threadid, void *(*__func) (void *),
+                 void *__args)
+{
+  if (!__threadid)
+    return ERROR;
+
+  int priority;
+  __RETURN_ERRNO_IF_NOT_OK (taskPriorityGet (taskIdSelf (), &priority));
+
+  int options;
+  __RETURN_ERRNO_IF_NOT_OK (taskOptionsGet (taskIdSelf (), &options));
+
+#if defined (__SPE__)
+  options |= VX_SPE_TASK;
+#else
+  options |= VX_FP_TASK;
+#endif
+  options &= VX_USR_TASK_OPTIONS;
+
+  int stacksize = 20 * 1024;
+
+  __gthread_t tcb = malloc (sizeof (*tcb));
+  if (!tcb)
+    return ERROR;
+
+  if (__init_gthread_tcb (tcb) != OK)
+    {
+      free (tcb);
+      return ERROR;
+    }
+
+  TASK_ID task_id = taskCreate (NULL,
+                               priority, options, stacksize,
+                               (FUNCPTR) & __task_wrapper,
+                               (_Vx_usr_arg_t) tcb,
+                               (_Vx_usr_arg_t) __func,
+                               (_Vx_usr_arg_t) __args,
+                               0, 0, 0, 0, 0, 0, 0);
+
+  /* If taskCreate succeeds, task_id will be a valid TASK_ID and not zero.  */
+  __RETURN_ERRNO_IF_NOT_OK (!task_id);
+
+  tcb->task_id = task_id;
+  *__threadid = tcb;
+
+  return __CHECK_RESULT (taskActivate (task_id));
+}
+
+int
+__gthread_equal (__gthread_t __t1, __gthread_t __t2)
+{
+  return (__t1 == __t2) ? OK : ERROR;
+}
+
+int
+__gthread_yield (void)
+{
+  return taskDelay (0);
+}
+
+int
+__gthread_join (__gthread_t __threadid, void **__value_ptr)
+{
+  if (!__threadid)
+    return ERROR;
+
+  /* A thread cannot join itself.  */
+  if (__threadid->task_id == taskIdSelf ())
+    return ERROR;
+
+  /* Waiting for the task to set the return value.  */
+  __gthread_mutex_lock (&__threadid->return_value_available);
+  __gthread_mutex_unlock (&__threadid->return_value_available);
+
+  if (__value_ptr)
+    *__value_ptr = __threadid->return_value;
+
+  /* The task will be safely be deleted.  */
+  __gthread_mutex_unlock (&(__threadid->delete_ok));
+
+  __RETURN_ERRNO_IF_NOT_OK (taskWait (__threadid->task_id, WAIT_FOREVER));
+
+  return OK;
+}
+
+int
+__gthread_detach (__gthread_t __threadid)
+{
+  if (!__threadid)
+    return ERROR;
+
+  if (taskIdVerify (__threadid->task_id) != OK)
+    return ERROR;
+
+  /* The task will be safely be deleted.  */
+  __gthread_mutex_unlock (&(__threadid->delete_ok));
+
+  return OK;
+}
diff --git a/libgcc/config/gthr-vxworks-tls.c b/libgcc/config/gthr-vxworks-tls.c
new file mode 100644 (file)
index 0000000..cd5f7ac
--- /dev/null
@@ -0,0 +1,362 @@
+/* Copyright (C) 2002-2018 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 3, 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+/* Threads compatibility routines for libgcc2 for VxWorks.
+   These are out-of-line routines called from gthr-vxworks.h.
+
+   This file provides the TLS related support routines, calling specific
+   VxWorks kernel entry points for this purpose.  */
+
+#include "tconfig.h"
+#include "tsystem.h"
+#include "gthr.h"
+
+#if defined(__GTHREADS)
+
+#include <vxWorks.h>
+#ifndef __RTP__
+#include <vxLib.h>
+#endif
+#include <taskLib.h>
+#ifndef __RTP__
+#include <taskHookLib.h>
+#else
+#include <errno.h>
+#endif
+
+/* Thread-local storage.
+
+   A gthread TLS key is simply an offset in an array, the address of which
+   we store in a single pointer field associated with the current task.
+
+   On VxWorks 7, we have direct support for __thread variables and use
+   such a variable as the pointer "field".  On other versions, we resort
+   to __gthread_get_tls_data and __gthread_set_tls_data functions provided
+   by the 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_tls_dtor_context and __gthread_leave_tls_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.
+
+   The task delete hook is only installed when at least one thread
+   has TLS data.  This is a necessary precaution, to allow this module
+   to be unloaded - a module with a hook can not be removed.
+
+   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_tls_data.  */
+struct tls_data
+{
+  int *owner;
+  void *values[MAX_KEYS];
+  unsigned int generation[MAX_KEYS];
+};
+
+/* To make sure we only delete TLS data associated with this object,
+   include a pointer to a local variable in the TLS data object.  */
+static int self_owner;
+
+/* Flag to check whether the delete hook is installed.  Once installed
+   it is only removed when unloading this module.  */
+static volatile int delete_hook_installed;
+
+/* TLS data access internal API.  A straight __thread variable on VxWorks 7,
+   a pointer returned by kernel provided routines otherwise.  */
+
+#ifdef __VXWORKS7__
+
+static __thread struct tls_data *__gthread_tls_data;
+
+#define VX_GET_TLS_DATA() __gthread_tls_data
+#define VX_SET_TLS_DATA(x) __gthread_tls_data = (x)
+
+#define VX_ENTER_TLS_DTOR()
+#define VX_LEAVE_TLS_DTOR()
+
+#else
+
+extern void *__gthread_get_tls_data (void);
+extern void __gthread_set_tls_data (void *data);
+
+extern void __gthread_enter_tls_dtor_context (void);
+extern void __gthread_leave_tls_dtor_context (void);
+
+#define VX_GET_TLS_DATA() __gthread_get_tls_data()
+#define VX_SET_TLS_DATA(x) __gthread_set_tls_data(x)
+
+#define VX_ENTER_TLS_DTOR() __gthread_enter_tls_dtor_context ()
+#define VX_LEAVE_TLS_DTOR() __gthread_leave_tls_dtor_context ()
+
+#endif /* __VXWORKS7__ */
+
+/* This is a global structure which records all of the active keys.
+
+   A key is potentially valid (i.e. has been handed out by
+   __gthread_key_create) iff its generation count in this structure is
+   even.  In that case, the matching entry in the dtors array is a
+   routine to be called when a thread terminates with a valid,
+   non-NULL specific value for that key.
+
+   A key is actually valid in a thread T iff the generation count
+   stored in this structure is equal to the generation count stored in
+   T's specific-value structure.  */
+
+typedef void (*tls_dtor) (void *);
+
+struct tls_keys
+{
+  tls_dtor dtor[MAX_KEYS];
+  unsigned int generation[MAX_KEYS];
+};
+
+#define KEY_VALID_P(key) !(tls_keys.generation[key] & 1)
+
+/* Note: if MAX_KEYS is increased, this initializer must be updated
+   to match.  All the generation counts begin at 1, which means no
+   key is valid.  */
+static struct tls_keys tls_keys =
+{
+  { NULL, NULL, NULL, NULL },
+  { 1, 1, 1, 1 }
+};
+
+/* This lock protects the tls_keys structure.  */
+static __gthread_mutex_t tls_lock;
+
+static __gthread_once_t tls_init_guard = __GTHREAD_ONCE_INIT;
+
+/* Internal routines.  */
+
+/* The task TCB has just been deleted.  Call the destructor
+   function for each TLS key that has both a destructor and
+   a non-NULL specific value in this thread.
+
+   This routine does not need to take tls_lock; the generation
+   count protects us from calling a stale destructor.  It does
+   need to read tls_keys.dtor[key] atomically.  */
+
+void
+tls_delete_hook (void *tcb ATTRIBUTE_UNUSED)
+{
+  struct tls_data *data;
+  __gthread_key_t key;
+
+  data = VX_GET_TLS_DATA();
+
+  if (data && data->owner == &self_owner)
+    {
+      VX_ENTER_TLS_DTOR();
+      for (key = 0; key < MAX_KEYS; key++)
+       {
+         if (data->generation[key] == tls_keys.generation[key])
+           {
+             tls_dtor dtor = tls_keys.dtor[key];
+
+             if (dtor)
+               dtor (data->values[key]);
+           }
+       }
+      free (data);
+
+      VX_LEAVE_TLS_DTOR();
+      VX_SET_TLS_DATA(NULL);
+    }
+}
+
+/* Initialize global data used by the TLS system.  */
+static void
+tls_init (void)
+{
+  __GTHREAD_MUTEX_INIT_FUNCTION (&tls_lock);
+}
+
+static void tls_destructor (void) __attribute__ ((destructor));
+static void
+tls_destructor (void)
+{
+#ifdef __RTP__
+  /* All threads but this one should have exited by now.  */
+  tls_delete_hook (NULL);
+#endif
+  /* Unregister the hook.  */
+  if (delete_hook_installed)
+    taskDeleteHookDelete ((FUNCPTR)tls_delete_hook);
+
+  if (tls_init_guard.done && __gthread_mutex_lock (&tls_lock) != ERROR)
+    semDelete (tls_lock);
+}
+
+/* External interface */
+
+/* Store in KEYP a value which can be passed to __gthread_setspecific/
+   __gthread_getspecific to store and retrieve 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, tls_dtor dtor)
+{
+  __gthread_key_t key;
+
+  __gthread_once (&tls_init_guard, tls_init);
+
+  if (__gthread_mutex_lock (&tls_lock) == ERROR)
+    return errno;
+
+  for (key = 0; key < MAX_KEYS; key++)
+    if (!KEY_VALID_P (key))
+      goto found_slot;
+
+  /* no room */
+  __gthread_mutex_unlock (&tls_lock);
+  return EAGAIN;
+
+ found_slot:
+  tls_keys.generation[key]++;  /* making it even */
+  tls_keys.dtor[key] = dtor;
+  *keyp = key;
+  __gthread_mutex_unlock (&tls_lock);
+  return 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 (&tls_init_guard, tls_init);
+
+  if (__gthread_mutex_lock (&tls_lock) == ERROR)
+    return errno;
+
+  if (!KEY_VALID_P (key))
+    {
+      __gthread_mutex_unlock (&tls_lock);
+      return EINVAL;
+    }
+
+  tls_keys.generation[key]++;  /* making it odd */
+  tls_keys.dtor[key] = 0;
+
+  __gthread_mutex_unlock (&tls_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 tls_data *data;
+
+  if (key >= MAX_KEYS)
+    return 0;
+
+  data = GET_VX_TLS_DATA();
+
+  if (!data)
+    return 0;
+
+  if (data->generation[key] != tls_keys.generation[key])
+    return 0;
+
+  return data->values[key];
+}
+
+/* Set the thread-specific value for KEY.  If KEY is invalid, or
+   memory allocation fails, returns -1, otherwise 0.
+
+   The generation count protects this function against races with
+   key_create/key_delete; the worst thing that can happen is that a
+   value is successfully stored into a dead generation (and then
+   immediately becomes invalid).  However, we do have to make sure
+   to read tls_keys.generation[key] atomically.  */
+
+int
+__gthread_setspecific (__gthread_key_t key, void *value)
+{
+  struct tls_data *data;
+  unsigned int generation;
+
+  if (key >= MAX_KEYS)
+    return EINVAL;
+
+  data = VX_GET_TLS_DATA();
+
+  if (!data)
+    {
+      if (!delete_hook_installed)
+       {
+         /* Install the delete hook.  */
+         if (__gthread_mutex_lock (&tls_lock) == ERROR)
+           return ENOMEM;
+         if (!delete_hook_installed)
+           {
+             taskDeleteHookAdd ((FUNCPTR)tls_delete_hook);
+             delete_hook_installed = 1;
+           }
+         __gthread_mutex_unlock (&tls_lock);
+       }
+
+      data = malloc (sizeof (struct tls_data));
+      if (!data)
+       return ENOMEM;
+
+      memset (data, 0, sizeof (struct tls_data));
+      data->owner = &self_owner;
+
+      VX_SET_TLS_DATA(data);
+    }
+
+  generation = tls_keys.generation[key];
+
+  if (generation & 1)
+    return EINVAL;
+
+  data->generation[key] = generation;
+  data->values[key] = value;
+
+  return 0;
+}
+#endif /* __GTHREADS */
diff --git a/libgcc/config/gthr-vxworks.c b/libgcc/config/gthr-vxworks.c
new file mode 100644 (file)
index 0000000..ddc3593
--- /dev/null
@@ -0,0 +1,87 @@
+/* Copyright (C) 2002-2019 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 3, 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+/* Threads compatibility routines for libgcc2 for VxWorks.
+
+   This file implements the init-once service exposed by gthr-vxworks.h.  */
+
+#include "tconfig.h"
+#include "tsystem.h"
+#include "gthr.h"
+
+#if defined(__GTHREADS)
+
+#include <vxWorks.h>
+
+#ifndef __RTP__
+# include <vxLib.h>
+# include <taskHookLib.h>
+#else /* __RTP__ */
+# include <errno.h>
+#endif /* __RTP__ */
+
+/* ----------------------------- Init-once ------------------------------- */
+
+static void
+__release (__gthread_once_t ** __guard)
+{
+  (*__guard)->busy = 0;
+}
+
+int
+__gthread_once (__gthread_once_t * __guard, void (*__func) (void))
+{
+  if (__guard->done)
+    return 0;
+
+  /* Busy-wait until we have exclusive access to the state.  Check if
+     another thread managed to perform the init call in the interim.  */
+  
+  while (!__TAS(&__guard->busy))
+    {
+      if (__guard->done)
+       return 0;
+      taskDelay (1);
+    }
+
+  if (!__guard->done)
+    {
+#ifndef __USING_SJLJ_EXCEPTIONS__
+      /* Setup a cleanup to release the guard when __func() throws an
+        exception.  We cannot use this with SJLJ exceptions as
+        Unwind_Register calls __gthread_once, leading to an infinite
+        recursion.  */
+      __attribute__ ((cleanup (__release)))
+       __gthread_once_t *__temp = __guard;
+#endif
+
+      __func ();
+      __guard->done = 1;
+    }
+
+  __release(&__guard);
+  return 0;
+}
+
+#endif /* __GTHREADS */
index c9214a57a9b2a52cb43998300251fc91e09c1a9e..7e3779a010a69cd177ecf66494b7c314c80fa33d 100644 (file)
@@ -1,6 +1,6 @@
 /* Threads compatibility routines for libgcc2 and libobjc for VxWorks.  */
 /* Compile this one with gcc.  */
-/* Copyright (C) 1997-2019 Free Software Foundation, Inc.
+/* Copyright (C) 1997-2018 Free Software Foundation, Inc.
    Contributed by Mike Stump <mrs@wrs.com>.
 
 This file is part of GCC.
@@ -33,139 +33,295 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 #include "gthr-posix.h"
 
 #else
-#ifdef __cplusplus
-#define UNUSED(x)
-#else
-#define UNUSED(x) x __attribute__((__unused__))
+
+#include <vxWorks.h>
+#include <version.h>
+
+/* Conditional compilation directives are easier to read when they fit on a
+   single line, which is helped by macros with shorter names.  */
+#define _VXW_MAJOR _WRS_VXWORKS_MAJOR
+#define _VXW_MINOR _WRS_VXWORKS_MINOR
+#define _VXW_PRE_69 (_VXW_MAJOR  < 6 || (_VXW_MAJOR == 6 && _VXW_MINOR < 9))
+
+/* Some VxWorks headers profusely use typedefs of a pointer to a function with
+   undefined number of arguments.  */
+#pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wstrict-prototypes"
+  #include <semLib.h>
+#pragma GCC diagnostic pop
+
+#include <errnoLib.h>
+
+
+/* --------------------- Test & Set/Swap internal API --------------------- */
+
+/* We use a bare atomic primitive with busy loops to handle mutual exclusion.
+   Inefficient, but reliable.  The actual primitive used depends on the mode
+   (RTP vs Kernel) and the version of VxWorks.  We define a macro and a type
+   here, for reuse without conditionals cluttering in the code afterwards.  */
+
+/* RTP, pre 6.9.  */
+
+#if defined(__RTP__) && _VXW_PRE_69
+
+#define __TAS(x) vxCas ((x), 0, 1)
+typedef volatile unsigned char __vx_tas_t;
+
+#endif
+
+/* RTP, 6.9 and beyond.  */
+
+#if defined(__RTP__) && !_VXW_PRE_69
+
+#define __TAS(x) vxAtomicCas ((x), 0, 1)
+typedef atomic_t __vx_tas_t;
+
+#include <vxAtomicLib.h>
+
+#endif
+
+/* Kernel */
+
+#if !defined(__RTP__)
+
+#define __TAS(x) vxTas (x)
+typedef volatile unsigned char __vx_tas_t;
+
 #endif
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/* ------------------------ Base __GTHREADS support ----------------------- */
+
 #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;
 /* All VxWorks mutexes are recursive.  */
+typedef SEM_ID __gthread_mutex_t;
 typedef SEM_ID __gthread_recursive_mutex_t;
-#define __GTHREAD_MUTEX_INIT_FUNCTION __gthread_mutex_init_function
-#define __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION __gthread_recursive_mutex_init_function
+#define __GTHREAD_MUTEX_INIT_FUNCTION __gthread_mutex_init
+#define __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION __gthread_recursive_mutex_init
+
+#define __CHECK_RESULT(result) (((result) == OK) ? OK : errnoGet())
+
+/* If a call to the VxWorks API fails, we must propagate the errno value.  */
+#define __RETURN_ERRNO_IF_NOT_OK(exp) if ((exp) != OK) return errnoGet()
+
+/* Non re-entrant mutex implementation. Libstdc++ expects the default
+   gthread mutex to be non reentrant.  */
 
 static inline void
-__gthread_mutex_init_function (__gthread_mutex_t *mutex)
+__gthread_mutex_init (__gthread_mutex_t * __mutex)
 {
-  *mutex = semMCreate (SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE);
+  if (!__mutex)
+    return;
+  *__mutex = semBCreate (SEM_Q_PRIORITY, SEM_FULL);
 }
 
 static inline int
-__gthread_mutex_destroy (__gthread_mutex_t *mutex)
+__gthread_mutex_destroy (__gthread_mutex_t * __mutex)
 {
-  semDelete(*mutex);
-  return 0;
+  if (!__mutex)
+    return ERROR;
+  return __CHECK_RESULT (semDelete (*__mutex));
 }
 
 static inline int
-__gthread_mutex_lock (__gthread_mutex_t *mutex)
+__gthread_mutex_lock (__gthread_mutex_t * __mutex)
 {
-  return semTake (*mutex, WAIT_FOREVER);
+  if (!__mutex)
+    return ERROR;
+  return __CHECK_RESULT (semTake(*__mutex, WAIT_FOREVER));
 }
 
 static inline int
-__gthread_mutex_trylock (__gthread_mutex_t *mutex)
+__gthread_mutex_trylock (__gthread_mutex_t * __mutex)
 {
-  return semTake (*mutex, NO_WAIT);
+  if (!__mutex)
+    return ERROR;
+  return __CHECK_RESULT (semTake (*__mutex, NO_WAIT));
 }
 
 static inline int
-__gthread_mutex_unlock (__gthread_mutex_t *mutex)
+__gthread_mutex_unlock (__gthread_mutex_t * __mutex)
 {
-  return semGive (*mutex);
+  if (!__mutex)
+    return ERROR;
+  return __CHECK_RESULT (semGive (*__mutex));
 }
 
+/* Recursive mutex implementation. The only change is that we use semMCreate()
+   instead of semBCreate().  */
+
 static inline void
-__gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *mutex)
+__gthread_recursive_mutex_init (__gthread_recursive_mutex_t * __mutex)
 {
-  __gthread_mutex_init_function (mutex);
+  if (!__mutex)
+    return;
+  *__mutex =
+    semMCreate (SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE);
 }
 
 static inline int
-__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *mutex)
+__gthread_recursive_mutex_destroy (__gthread_recursive_mutex_t * __mutex)
 {
-  return __gthread_mutex_lock (mutex);
+  return __gthread_mutex_destroy (__mutex);
 }
 
 static inline int
-__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *mutex)
+__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t * __mutex)
 {
-  return __gthread_mutex_trylock (mutex);
+  return __gthread_mutex_lock (__mutex);
 }
 
 static inline int
-__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex)
+__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t * __mutex)
 {
-  return __gthread_mutex_unlock (mutex);
+  return __gthread_mutex_trylock (__mutex);
 }
 
 static inline int
-__gthread_recursive_mutex_destroy (__gthread_recursive_mutex_t *__mutex)
+__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t * __mutex)
 {
-  return __gthread_mutex_destroy (__mutex);
+  return __gthread_mutex_unlock (__mutex);
 }
 
-/* pthread_once is complicated enough that it's implemented
-   out-of-line.  See config/vxlib.c.  */
-
 typedef struct
 {
-#if !defined(__RTP__)
+  /* PPC's test-and-set kernel mode implementation requires a pointer aligned
+     object, of which it only sets the first byte.  We use padding in addition
+     to an alignment request here to maxmise the factors leading to the
+     desired actual alignment choice by the compiler.  */
 #if defined(__PPC__)
-  __attribute ((aligned (__alignof (unsigned))))
-#endif
-  volatile unsigned char busy;
+  __attribute ((aligned (__alignof__ (void *))))
 #endif
+
+  __vx_tas_t busy;
   volatile unsigned char done;
+
 #if !defined(__RTP__) && defined(__PPC__)
-  /* PPC's test-and-set implementation requires a 4 byte aligned
-     object, of which it only sets the first byte.  We use padding
-     here, in order to maintain some amount of backwards
-     compatibility.  Without this padding, gthread_once objects worked
-     by accident because they happen to be static objects and the ppc
-     port automatically increased their alignment to 4 bytes.  */
   unsigned char pad1;
   unsigned char pad2;
 #endif
-}
-__gthread_once_t;
-
-#if defined (__RTP__)
-# define __GTHREAD_ONCE_INIT { 0 }
-#elif defined (__PPC__)
-# define __GTHREAD_ONCE_INIT { 0, 0, 0, 0 }
-#else
-# define __GTHREAD_ONCE_INIT { 0, 0 }
+#if !defined(__RTP__) && defined(__PPC64__)
+  unsigned char pad3;
+  unsigned char pad4;
+  unsigned char pad5;
+  unsigned char pad6;
 #endif
+} __gthread_once_t;
+
+#define __GTHREAD_ONCE_INIT { 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
+/* 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_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);
 
-#undef UNUSED
+/* ------------------ Base condition variables support ------------------- */
+
+#define __GTHREAD_HAS_COND 1
+
+typedef SEM_ID __gthread_cond_t;
+
+#define __GTHREAD_COND_INIT_FUNCTION __gthread_cond_init
+
+/* Condition variable declarations.  */
+
+extern void __gthread_cond_init (__gthread_cond_t *cond);
+
+extern int __gthread_cond_destroy (__gthread_cond_t *cond);
+
+extern int __gthread_cond_broadcast (__gthread_cond_t *cond);
+
+extern int __gthread_cond_wait (__gthread_cond_t *cond,
+                               __gthread_mutex_t *mutex);
+
+extern int __gthread_cond_wait_recursive (__gthread_cond_t *cond,
+                                         __gthread_recursive_mutex_t *mutex);
+
+/* -----------------------  C++0x thread support ------------------------- */
+
+/* We do not support C++0x threads on that VxWorks 653, which we can
+   recognize by VTHREADS being defined.  */
+
+#ifndef VTHREADS
+
+#define __GTHREADS_CXX0X 1
+
+#include <limits.h>
+#include <time.h>
+#include <tickLib.h>
+#include <sysLib.h>
+#include <version.h>
+
+typedef struct
+{
+  TASK_ID task_id;
+  void *return_value;
+
+  /* This mutex is used to block in join() while the return value is
+     unavailable.  */
+  __gthread_mutex_t return_value_available;
+
+  /* Before freeing the structure in the task wrapper, we need to wait until
+     join() or detach() are called on that thread.   */
+  __gthread_mutex_t delete_ok;
+} __gthread_tcb;
+
+typedef __gthread_tcb *__gthread_t;
+
+/* Typedefs specific to different vxworks versions.  */
+#if _VXW_PRE_69
+  typedef int _Vx_usr_arg_t;
+  #define TASK_ID_NULL ((TASK_ID)NULL)
+  #define SEM_ID_NULL ((SEM_ID)NULL)
+#endif
+
+typedef struct timespec __gthread_time_t;
+
+/* Timed mutex lock declarations.  */
+
+extern int __gthread_mutex_timedlock (__gthread_mutex_t *m,
+                                     const __gthread_time_t *abs_time);
+
+extern int __gthread_recursive_mutex_timedlock
+  (__gthread_recursive_mutex_t *mutex,
+   const __gthread_time_t *abs_timeout);
+
+/* Timed condition variable declarations.  */
+
+extern int __gthread_cond_signal (__gthread_cond_t *cond);
+extern int __gthread_cond_timedwait (__gthread_cond_t *cond,
+                                    __gthread_mutex_t *mutex,
+                                    const __gthread_time_t *abs_timeout);
+
+/* gthreads declarations.  */
+
+extern int __gthread_equal (__gthread_t t1, __gthread_t t2);
+extern int __gthread_yield (void);
+extern int __gthread_create (__gthread_t *__threadid,
+                            void *(*__func) (void*),
+                            void *__args);
+extern int __gthread_join (__gthread_t thread, void **value_ptr);
+extern int __gthread_detach (__gthread_t thread);
+
+extern __gthread_t __gthread_self (void);
+
+#endif
 
 #ifdef __cplusplus
 }
diff --git a/libgcc/config/t-gthr-vxworks b/libgcc/config/t-gthr-vxworks
new file mode 100644 (file)
index 0000000..455d0b3
--- /dev/null
@@ -0,0 +1,5 @@
+# Extra libgcc2 modules used by gthr-vxworks.h functions
+LIB2ADDEH += $(srcdir)/config/gthr-vxworks.c\
+            $(srcdir)/config/gthr-vxworks-cond.c\
+            $(srcdir)/config/gthr-vxworks-thread.c\
+            $(srcdir)/config/gthr-vxworks-tls.c
\ No newline at end of file
index 2db8e05966e1056083712af255fdc9da6e0b95b8..757cead6724a2f495f6174976e233b736da4783b 100644 (file)
@@ -6,9 +6,6 @@ LIBGCC2_DEBUG_CFLAGS =
 LIB2FUNCS_EXCLUDE += _clear_cache
 LIB2ADD += $(srcdir)/config/vxcache.c
 
-# Extra libgcc2 modules used by gthr-vxworks.h functions
-LIB2ADDEH += $(srcdir)/config/vxlib.c $(srcdir)/config/vxlib-tls.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. Make sure the local libgcc headers still
index 054ab7c4091299b3828d7bbf5144aec9f4166ee3..f2cc904ac08beb880db4d06bda1aefde763f89c2 100644 (file)
@@ -6,9 +6,6 @@ LIBGCC2_DEBUG_CFLAGS =
 LIB2FUNCS_EXCLUDE += _clear_cache
 LIB2ADD += $(srcdir)/config/vxcache.c
 
-# Extra libgcc2 modules used by gthr-vxworks.h functions
-LIB2ADDEH += $(srcdir)/config/vxlib.c $(srcdir)/config/vxlib-tls.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. Make sure the local libgcc headers still
@@ -21,4 +18,3 @@ LIBGCC2_INCLUDES = -nostdinc -I. \
       */mrtp*) echo -I$(VSB_DIR)/usr/h/public -I$(VSB_DIR)/usr/h ;; \
       *) echo -I$(VSB_DIR)/krnl/h/system -I$(VSB_DIR)/krnl/h/public ;; \
    esac`
-
diff --git a/libgcc/config/vxlib-tls.c b/libgcc/config/vxlib-tls.c
deleted file mode 100644 (file)
index b8d6907..0000000
+++ /dev/null
@@ -1,370 +0,0 @@
-/* Copyright (C) 2002-2019 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 3, 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.
-
-Under Section 7 of GPL version 3, you are granted additional
-permissions described in the GCC Runtime Library Exception, version
-3.1, as published by the Free Software Foundation.
-
-You should have received a copy of the GNU General Public License and
-a copy of the GCC Runtime Library Exception along with this program;
-see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
-<http://www.gnu.org/licenses/>.  */
-
-/* Threads compatibility routines for libgcc2 for VxWorks.
-   These are out-of-line routines called from gthr-vxworks.h. 
-
-   This file provides the TLS related support routines, calling specific
-   VxWorks kernel entry points for this purpose.  The base VxWorks 5.x kernels
-   don't feature these entry points, and we provide gthr_supp_vxw_5x.c as an
-   option to fill this gap.  Asking users to rebuild a kernel is not to be
-   taken lightly, still, so we have isolated these routines from the rest of
-   vxlib to ensure that the kernel dependencies are only dragged when really
-   necessary.  */
-
-#include "tconfig.h"
-#include "tsystem.h"
-#include "gthr.h"
-
-#if defined(__GTHREADS)
-#include <vxWorks.h>
-#ifndef __RTP__
-#include <vxLib.h>
-#endif
-#include <taskLib.h>
-#ifndef __RTP__
-#include <taskHookLib.h>
-#else
-# include <errno.h>
-#endif
-
-/* Thread-local storage.
-
-   We reserve a field in the TCB to point to a dynamically allocated
-   array which is used to store TLS values.  A TLS 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_tls_data and
-   __gthread_set_tls_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_tls_dtor_context and
-   __gthread_leave_tls_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.)
-
-   The task delete hook is only installed when at least one thread
-   has TLS data.  This is a necessary precaution, to allow this module
-   to be unloaded - a module with a hook cannot be removed.
-
-   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_tls_data.  */
-struct tls_data
-{
-  int *owner;
-  void *values[MAX_KEYS];
-  unsigned int generation[MAX_KEYS];
-};
-
-/* To make sure we only delete TLS data associated with this object,
-   include a pointer to a local variable in the TLS data object.  */
-static int self_owner;
-
-/* Flag to check whether the delete hook is installed.  Once installed
-   it is only removed when unloading this module.  */
-static volatile int delete_hook_installed;
-
-/* kernel provided routines */
-extern void *__gthread_get_tls_data (void);
-extern void __gthread_set_tls_data (void *data);
-
-extern void __gthread_enter_tls_dtor_context (void);
-extern void __gthread_leave_tls_dtor_context (void);
-
-#ifndef __RTP__
-
-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);
-
-#endif /* __RTP__ */
-
-/* This is a global structure which records all of the active keys.
-
-   A key is potentially valid (i.e. has been handed out by
-   __gthread_key_create) iff its generation count in this structure is
-   even.  In that case, the matching entry in the dtors array is a
-   routine to be called when a thread terminates with a valid,
-   non-NULL specific value for that key.
-
-   A key is actually valid in a thread T iff the generation count
-   stored in this structure is equal to the generation count stored in
-   T's specific-value structure.  */
-
-typedef void (*tls_dtor) (void *);
-
-struct tls_keys
-{
-  tls_dtor dtor[MAX_KEYS];
-  unsigned int generation[MAX_KEYS];
-};
-
-#define KEY_VALID_P(key) !(tls_keys.generation[key] & 1)
-
-/* Note: if MAX_KEYS is increased, this initializer must be updated
-   to match.  All the generation counts begin at 1, which means no
-   key is valid.  */
-static struct tls_keys tls_keys =
-{
-  { 0, 0, 0, 0 },
-  { 1, 1, 1, 1 }
-};
-
-/* This lock protects the tls_keys structure.  */
-static __gthread_mutex_t tls_lock;
-
-static __gthread_once_t tls_init_guard = __GTHREAD_ONCE_INIT;
-
-/* Internal routines.  */
-
-/* The task TCB has just been deleted.  Call the destructor
-   function for each TLS key that has both a destructor and
-   a non-NULL specific value in this thread.
-
-   This routine does not need to take tls_lock; the generation
-   count protects us from calling a stale destructor.  It does
-   need to read tls_keys.dtor[key] atomically.  */
-
-static void
-tls_delete_hook (void *tcb ATTRIBUTE_UNUSED)
-{
-  struct tls_data *data;
-  __gthread_key_t key;
-
-#ifdef __RTP__
-  data = __gthread_get_tls_data ();
-#else
-  /* In kernel mode, we can be called in the context of the thread
-     doing the killing, so must use the TCB to determine the data of
-     the thread being killed.  */
-  data = __gthread_get_tsd_data (tcb);
-#endif
-  
-  if (data && data->owner == &self_owner)
-    {
-#ifdef __RTP__
-      __gthread_enter_tls_dtor_context ();
-#else
-      __gthread_enter_tsd_dtor_context (tcb);
-#endif
-      for (key = 0; key < MAX_KEYS; key++)
-       {
-         if (data->generation[key] == tls_keys.generation[key])
-           {
-             tls_dtor dtor = tls_keys.dtor[key];
-
-             if (dtor)
-               dtor (data->values[key]);
-           }
-       }
-      free (data);
-#ifdef __RTP__
-      __gthread_leave_tls_dtor_context ();
-#else
-      __gthread_leave_tsd_dtor_context (tcb);
-#endif
-
-#ifdef __RTP__
-      __gthread_set_tls_data (0);
-#else
-      __gthread_set_tsd_data (tcb, 0);
-#endif
-    }
-} 
-
-/* Initialize global data used by the TLS system.  */
-static void
-tls_init (void)
-{
-  __GTHREAD_MUTEX_INIT_FUNCTION (&tls_lock);
-}
-
-static void tls_destructor (void) __attribute__ ((destructor));
-static void
-tls_destructor (void)
-{
-#ifdef __RTP__
-  /* All threads but this one should have exited by now.  */
-  tls_delete_hook (NULL);
-#endif
-  /* Unregister the hook.  */
-  if (delete_hook_installed)
-    taskDeleteHookDelete ((FUNCPTR)tls_delete_hook);
-
-  if (tls_init_guard.done && __gthread_mutex_lock (&tls_lock) != ERROR)
-    semDelete (tls_lock);
-}
-
-/* External interface */
-
-/* Store in KEYP a value which can be passed to __gthread_setspecific/
-   __gthread_getspecific to store and retrieve 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, tls_dtor dtor)
-{
-  __gthread_key_t key;
-
-  __gthread_once (&tls_init_guard, tls_init);
-
-  if (__gthread_mutex_lock (&tls_lock) == ERROR)
-    return errno;
-
-  for (key = 0; key < MAX_KEYS; key++)
-    if (!KEY_VALID_P (key))
-      goto found_slot;
-
-  /* no room */
-  __gthread_mutex_unlock (&tls_lock);
-  return EAGAIN;
-
- found_slot:
-  tls_keys.generation[key]++;  /* making it even */
-  tls_keys.dtor[key] = dtor;
-  *keyp = key;
-  __gthread_mutex_unlock (&tls_lock);
-  return 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 (&tls_init_guard, tls_init);
-
-  if (__gthread_mutex_lock (&tls_lock) == ERROR)
-    return errno;
-
-  if (!KEY_VALID_P (key))
-    {
-      __gthread_mutex_unlock (&tls_lock);
-      return EINVAL;
-    }
-
-  tls_keys.generation[key]++;  /* making it odd */
-  tls_keys.dtor[key] = 0;
-
-  __gthread_mutex_unlock (&tls_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 tls_data *data;
-
-  if (key >= MAX_KEYS)
-    return 0;
-
-  data = __gthread_get_tls_data ();
-
-  if (!data)
-    return 0;
-
-  if (data->generation[key] != tls_keys.generation[key])
-    return 0;
-
-  return data->values[key];
-}
-
-/* Set the thread-specific value for KEY.  If KEY is invalid, or
-   memory allocation fails, returns -1, otherwise 0.
-
-   The generation count protects this function against races with
-   key_create/key_delete; the worst thing that can happen is that a
-   value is successfully stored into a dead generation (and then
-   immediately becomes invalid).  However, we do have to make sure
-   to read tls_keys.generation[key] atomically.  */
-
-int
-__gthread_setspecific (__gthread_key_t key, void *value)
-{
-  struct tls_data *data;
-  unsigned int generation;
-
-  if (key >= MAX_KEYS)
-    return EINVAL;
-
-  data = __gthread_get_tls_data ();
-  if (!data)
-    {
-      if (!delete_hook_installed)
-       {
-         /* Install the delete hook.  */
-         if (__gthread_mutex_lock (&tls_lock) == ERROR)
-           return ENOMEM;
-         if (!delete_hook_installed)
-           {
-             taskDeleteHookAdd ((FUNCPTR)tls_delete_hook);
-             delete_hook_installed = 1;
-           }
-         __gthread_mutex_unlock (&tls_lock);
-       }
-
-      data = malloc (sizeof (struct tls_data));
-      if (!data)
-       return ENOMEM;
-
-      memset (data, 0, sizeof (struct tls_data));
-      data->owner = &self_owner;
-      __gthread_set_tls_data (data);
-    }
-
-  generation = tls_keys.generation[key];
-
-  if (generation & 1)
-    return EINVAL;
-
-  data->generation[key] = generation;
-  data->values[key] = value;
-
-  return 0;
-}
-#endif /* __GTHREADS */
diff --git a/libgcc/config/vxlib.c b/libgcc/config/vxlib.c
deleted file mode 100644 (file)
index 78b6776..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/* Copyright (C) 2002-2019 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 3, 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.
-
-Under Section 7 of GPL version 3, you are granted additional
-permissions described in the GCC Runtime Library Exception, version
-3.1, as published by the Free Software Foundation.
-
-You should have received a copy of the GNU General Public License and
-a copy of the GCC Runtime Library Exception along with this program;
-see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
-<http://www.gnu.org/licenses/>.  */
-
-/* Threads compatibility routines for libgcc2 for VxWorks.
-   These are out-of-line routines called from gthr-vxworks.h.  */
-
-#include "tconfig.h"
-#include "tsystem.h"
-#include "gthr.h"
-
-#if defined(__GTHREADS)
-#include <vxWorks.h>
-#ifndef __RTP__
-#include <vxLib.h>
-#endif
-#include <taskLib.h>
-#ifndef __RTP__
-#include <taskHookLib.h>
-#else
-# include <errno.h>
-#endif
-
-/* 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;
-
-#ifdef __RTP__
-  __gthread_lock_library ();
-#else
-  while (!vxTas ((void *)&guard->busy))
-    {
-#ifdef __PPC__
-      /* This can happen on powerpc, which is using all 32 bits
-        of the gthread_once_t structure.  */
-      if (guard->done)
-       return 0;
-#endif
-      taskDelay (1);
-    }
-#endif
-
-  /* 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;
-    }
-
-#ifdef __RTP__
-  __gthread_unlock_library ();
-#else
-  guard->busy = 0;
-#endif
-  return 0;
-}
-
-#endif /* __GTHREADS */