Pthreads, part 2

Alex Waugh Alex.Waugh at arm.com
Fri Oct 18 01:38:15 PDT 2002


Here's the next installment of the pthread changes.

The context switching code now uses SFM/LFM rather than STFE/LDFE when
assembled for 32bit code.
All SWP instructions have been replaced by a macro which can optionally
expand to an equivalent ARM2 compatible sequence.
Thread safety for setjmp/longjmp
Thread safety for alloca. alloca lists are stored on a per-thread basis,
to avoid memory leaks if a thread exits with blocks still allocated.
This has meant that two areas in gcc/gcc/config/arm/md that inlined some
code for alloca are no longer valid, so I have commented them out. With
the extra instructions needed there is perhaps less benefit in inlining
them anyway.
errno has been changed to a macro so it expands to a thread-specific
variable, so errno no longer has to be saved and restored on every
context switch.
The error buffer (__ul_errbuf) is now per-thread.
For consistency perror and __unixlib_fatal now call strerror to get the
error string.
Added the strerror_r function
Thread safety for exec and variants, and vfork.
stackalloc is used rather than malloc for storing the cli in exec, to
ensure that it is not allocated in a dynamic area.

I have made some progress with preemtion in taskwindows, but it is not
yet in a stable state, so hasn't been included in this patch.

Alex


Index: gcc/gcc/config/arm/md
===================================================================
--- gcc/gcc/config/arm/md
+++ tmp.59320.00001	Thu Oct 17 18:22:17 2002
@@ -6305,7 +6305,7 @@
    "TARGET_APCS_STACK"
    "
  {
-#if 1
+#if 0  /* Inline version is only valid when pthreads are not in use */
    /* Inline version of __arm_alloca_block_init.  */
    rtx scratch_reg = gen_reg_rtx (SImode);
    rtx label = gen_rtx_SYMBOL_REF (Pmode, \"__arm_alloca_st\");
@@ -6331,7 +6331,7 @@
    "TARGET_APCS_STACK"
    "
  {
-#if 1
+#if 0  /* Inline version is only valid when pthreads are not in use */
    /* Inline version of __arm_alloca_block_init.  */
    rtx scratch_reg = gen_reg_rtx (SImode);
    rtx label = gen_rtx_SYMBOL_REF (Pmode, \"__arm_alloca_st\");
Index: gcc/gcc/config/arm/lib1aof.s
===================================================================
--- gcc/gcc/config/arm/lib1aof.s
+++ tmp.59328.00001	Thu Oct 17 18:22:18 2002
@@ -77,6 +77,10 @@
  #define RETCOND
  #endif

+/* Keep these in sync with unixlib/asm_dec.s and features.h */
+__FEATURE_PTHREADS	EQU	1
+__PTHREAD_ALLOCA_OFFSET	EQU	8
+
  	AREA	|C$$code|, CODE, READONLY

  #ifdef L_ashldi3
@@ -1180,12 +1184,19 @@
    allocation.  This method had extremely bad run-time performance and
    had a memory leak with this saved linkage information persisting
    not being deallocated (not possible without more hacks to the main
-  compiler). */
+  compiler).
+
+  Additionally, if threads are in use then the list of chunks is stored
+  on a per-thread basis, and ___arm_alloca_thread_free_all is called
+  when the thread exits, to ensure all allocations for that thread get
+  released. */

  /* Version 2.13 (06 Nov 1999)  */

  XOS_Write0	*	&20002

+	[ __FEATURE_PTHREADS = 0
+	; When pthreads are in use this is stored per-thread
  	AREA	|C$$data|, DATA
  	ALIGN	4
  	EXPORT	|__arm_alloca_st|
@@ -1194,6 +1205,7 @@
  |__arm_alloca_st|
  	DCD	0	/* unsigned int level */
  	DCD	0	/* chunk *list */
+	]

  /* Structures used within this source:

@@ -1233,8 +1245,10 @@
  	AREA	|C$$code|, CODE, READONLY

  	ALIGN	4
+	[ __FEATURE_PTHREADS = 0
  |arm_alloca_ptr|
  	DCD	|__arm_alloca_st|
+	]

  	/* please ensure all routines are preceded by their own
  	   register usage.  While ObjASM will complain if the same
@@ -1300,11 +1314,18 @@

  	IMPORT	|free|
  	IMPORT	|abort|
+	IMPORT	|__pthread_running_thread|

  	ALIGN	4
  |___arm_alloca_free_all|
  	STMFD	sp!, {a1, a2, a3, a4, arm_alloca_v, chunks, old_chunks, return_addr, alloca_level, fp, ip}
+	[ __FEATURE_PTHREADS = 1
+	LDR	arm_alloca_v, =|__pthread_running_thread|
+	LDR	arm_alloca_v, [arm_alloca_v]
+	ADD	arm_alloca_v, arm_alloca_v, #__PTHREAD_ALLOCA_OFFSET
+	|
  	LDR	arm_alloca_v, [pc, #|arm_alloca_ptr| - . -8]
+	]
  	MOV	return_addr, #0

  	/* alloca_level = arm_alloca.level; chunks = arm_allocal.list; */
@@ -1364,6 +1385,43 @@


  	/* -----------------------------------------------------------------
+	   ___arm_alloca_thread_free_all
+
+	   register usage  */
+/*
+arm_alloca_v		RN	5
+chunks			RN	9
+*/
+
+	IMPORT	|free|
+	IMPORT	|__pthread_running_thread|
+
+	ALIGN	4
+	EXPORT	|___arm_alloca_thread_free_all|
+|___arm_alloca_thread_free_all|
+	STMFD	sp!, {arm_alloca_v, chunks, lr}
+	[ __FEATURE_PTHREADS = 1
+	LDR	arm_alloca_v, =|__pthread_running_thread|
+	LDR	arm_alloca_v, [arm_alloca_v]
+	ADD	arm_alloca_v, arm_alloca_v, #__PTHREAD_ALLOCA_OFFSET
+	|
+	LDR	arm_alloca_v, [pc, #|arm_alloca_ptr| - . -8]
+	]
+
+	LDR	chunks, [arm_alloca_v, #list_off]
+	MOV	a1, #0
+	STR	a1, [arm_alloca_v, #list_off]
+	STR	a1, [arm_alloca_v, #level_off]
+|L..18|					/* while (chunks != NULL) { */
+	MOVS	a1, chunks		/*    temp = chunks; */
+	LDMEQFD	sp!, {arm_alloca_v, chunks, pc}RETCOND
+	LDR	chunks, [chunks, #chunks_prev_off]
+
+	BL	|free|
+	B	|L..18|
+
+
+	/* -----------------------------------------------------------------
  	   ___arm_alloca_alloc

  	  register usage  */
@@ -1409,7 +1467,13 @@
  	BLEQ	abort			/* abort never returns */

  	/*  get pointer to chunk list and level number */
+	[ __FEATURE_PTHREADS = 1
+	LDR	arm_alloca_a, =|__pthread_running_thread|
+	LDR	arm_alloca_a, [arm_alloca_a]
+	ADD	arm_alloca_a, arm_alloca_a, #__PTHREAD_ALLOCA_OFFSET
+	|
  	LDR	arm_alloca_a, [pc, #|arm_alloca_ptr|-.-8]
+	]
  	LDMIA	arm_alloca_a, {gbaa_t2,gbaa_t3}

  	/*  store the frame pointer, chunk list ptr and level number
@@ -1421,7 +1485,11 @@
  	   field in our __chunk structure.  If not already done so,
  	   set the return address of the frame to __arm_alloca_free_all.  */
  	LDR	gbaa_t2, [fp, #-4]
+	[ {CONFIG} = 26
  	BIC	gbaa_t1, gbaa_t2, #&FC000003
+	|
+	MOV	gbaa_t1, gbaa_t2
+	]
  	ADR	gbaa_t3, |___arm_alloca_free_all|
  	CMP	gbaa_t1, gbaa_t3
  	MOVEQ	gbaa_t2, #0
@@ -1462,7 +1530,13 @@
  	STMFD	sp!, {lev, chunks, arm_alloca_v, old_chunks, return_addr, all_allocations_freed, fp, ip, lr}
  	MOV	lev, a1
  	MOV	return_addr, #0
+	[ __FEATURE_PTHREADS = 1
+	LDR	arm_alloca_v, =|__pthread_running_thread| ; should be conditional on __FEATURE_PTHREADS
+	LDR	arm_alloca_v, [arm_alloca_v]
+	ADD	arm_alloca_v, arm_alloca_v, #__PTHREAD_ALLOCA_OFFSET
+	|
  	LDR	arm_alloca_v, [pc, #|arm_alloca_ptr|-.-8]
+	]
  	MOV	all_allocations_freed, #1
  	LDR	chunks, [arm_alloca_v, #list_off]
  	MOV	old_chunks, #0
@@ -1535,7 +1609,13 @@
  	ALIGN	4
  	EXPORT	|___arm_alloca_block_init|
  |___arm_alloca_block_init|
+	[ __FEATURE_PTHREADS = 1
+	LDR	arm_alloca_a, =|__pthread_running_thread|
+	LDR	arm_alloca_a, [arm_alloca_a]
+	ADD	arm_alloca_a, arm_alloca_a, #__PTHREAD_ALLOCA_OFFSET
+	|
  	LDR	arm_alloca_a, [pc, #|arm_alloca_ptr|-.-8]
+	]
  	LDR	a1, [arm_alloca_a, #level_off]
  	ADD	a1, a1, #1
  	STR	a1, [arm_alloca_a, #level_off]
@@ -1555,7 +1635,13 @@
  	/* Store within the setjmp buffer a pointer to the linked
  	   list of alloca chunks. longjmp will use this to free any
  	   memory allocated with `alloca' between the setjmp and longjmp */
+	[ __FEATURE_PTHREADS = 1
+	LDR	arm_alloca_a, =|__pthread_running_thread| ; should be conditional on __FEATURE_PTHREADS
+	LDR	arm_alloca_a, [arm_alloca_a]
+	ADD	arm_alloca_a, arm_alloca_a, #__PTHREAD_ALLOCA_OFFSET
+	|
  	LDR	arm_alloca_a, [pc, #|arm_alloca_ptr|-.-8]
+	]
  	LDR	a4, [arm_alloca_a, #list_off]
  	STR	a4, [a1, #setjmp_save]
  	B	|setjmp|
@@ -1579,7 +1665,13 @@
  |___arm_alloca_longjmp|
  	MOV	jmpbuf, a1
  	MOV	num_save, a2
+	[ __FEATURE_PTHREADS = 1
+	LDR	arm_alloca_v, =|__pthread_running_thread|
+	LDR	arm_alloca_v, [arm_alloca_v]
+	ADD	arm_alloca_v, arm_alloca_v, #__PTHREAD_ALLOCA_OFFSET
+	|
  	LDR	arm_alloca_v, [pc, #|arm_alloca_ptr|-.-8]
+	]
  	LDR	chunks, [arm_alloca_v, #list_off]
  	LDR	search_value, [jmpbuf, #setjmp_save]
  	B	|L..48|
@@ -1620,7 +1712,13 @@
  	ALIGN	4
  	EXPORT	|___arm_alloca_nonlocal_save|
  |___arm_alloca_nonlocal_save|
+	[ __FEATURE_PTHREADS = 1
+	LDR	arm_alloca_a, =|__pthread_running_thread|
+	LDR	arm_alloca_a, [arm_alloca_a]
+	ADD	arm_alloca_a, arm_alloca_a, #__PTHREAD_ALLOCA_OFFSET
+	|
  	LDR	arm_alloca_a, [pc, #|arm_alloca_ptr|-.-8]
+	]
  	LDR	a1, [arm_alloca_a, #list_off]
  	RETURN(pc, lr)

@@ -1640,7 +1738,13 @@
  |___arm_alloca_nonlocal_restore|
  	STMFD	sp!, {chunks, arm_alloca_v, search_value, fp, ip, lr}
  	MOV	search_value, a1
+	[ __FEATURE_PTHREADS = 1
+	LDR	arm_alloca_v, =|__pthread_running_thread|
+	LDR	arm_alloca_v, [arm_alloca_v]
+	ADD	arm_alloca_v, arm_alloca_v, #__PTHREAD_ALLOCA_OFFSET
+	|
  	LDR	arm_alloca_v, [pc, #|arm_alloca_ptr|-.-8]
+	]
  	LDR	chunks, [arm_alloca_v, #list_off]
  	B	|L..48.1|
  |L..49.1|
Index: riscos-aof/as/os.h
===================================================================
--- riscos-aof/as/os.h
+++ tmp.4904.00001	Thu Oct 17 18:22:23 2002
@@ -19,7 +19,7 @@
  /* Acorn/RISC OS specific information.  */
  typedef struct os_error
  {
-  int errno;
+  int errnum;
    char errmess[256];
  }
  os_error;
Index: unixlib/source/pthread/newnode.c
===================================================================
--- unixlib/source/pthread/newnode.c
+++ tmp.45736.00001	Thu Oct 17 18:22:27 2002
@@ -0,0 +1,101 @@
+/****************************************************************************
+ *
+ * $Source: $
+ * $Date: $
+ * $Revision: $
+ * $State: $
+ * $Author: $
+ *
+ ***************************************************************************/
+
+#ifdef EMBED_RCSID
+static const char rcs_id[] = "$Id: $";
+#endif
+
+/* Written by Martin Piper and Alex Waugh */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unixlib/os.h>
+#include <unixlib/unix.h>
+#include <pthread.h>
+
+
+/* Initialise an new thread node to hold all the details of the thread */
+/* If node is NULL, then allocate memory for the node as well */
+pthread_t
+__pthread_new_node (pthread_t node)
+{
+
+  if (node == NULL)
+    {
+      node = malloc (sizeof (struct __pthread_thread));
+      if (node == NULL)
+        {
+#ifdef DEBUG_PTHREAD
+          __os_print("-- __pthread_new_node: Unable to allocate thread node\r\n");
+#endif
+          return NULL;
+        }
+
+      node->saved_context = malloc (sizeof (struct __pthread_saved_context));
+      if (node->saved_context == NULL)
+        {
+#ifdef DEBUG_PTHREAD
+          __os_print ("-- _pthread_new_node: Unable to allocate thread saved_context\r\n");
+#endif
+          free (node);
+          return NULL;
+        }
+    }
+
+  node->magic = 0; /* Ensure the magic number is invalid until the thread is ready to run */
+  node->alloca[0] = 0;
+  node->alloca[1] = 0;
+  node->alloca[2] = 0;
+  node->thread_errno = 0;
+  node->errbuf.errnum = 0;
+  node->errbuf.errmess[0] = '\0';
+  node->next = NULL;
+  node->stack = NULL;
+  node->keys = NULL;
+  node->cleanupfns = NULL;
+  node->state = STATE_RUNNING;
+  node->cancelstate = PTHREAD_CANCEL_ENABLE;
+  node->canceltype = PTHREAD_CANCEL_DEFERRED;
+  node->cancelpending = 0;
+  node->detachstate = PTHREAD_CREATE_JOINABLE;
+  node->joined = NULL;
+  node->ret = NULL;
+  node->mutex = NULL;
+  node->cond = NULL;
+  node->nextwait = NULL;
+
+  return node;
+}
+
+/* Allocate and initialise a new stack chunk */
+struct __stack_chunk *
+__pthread_new_stack (void)
+{
+  struct __stack_chunk *stack;
+
+  stack = __stackalloc (PTHREAD_STACK_MIN);
+  if (stack == NULL)
+    {
+#ifdef DEBUG_PTHREAD
+      __os_print("-- __pthread_new_stack: Unable to allocate thread stack\r\n");
+#endif
+      return NULL;
+    }
+
+  stack->magic = STACK_CHUNK_MAGIC;
+  stack->next = NULL ;
+  stack->prev = NULL ;
+  stack->size = PTHREAD_STACK_MIN;
+  stack->dealloc = NULL;
+  stack->returnaddr = NULL;
+
+  return stack;
+}
+
Index: unixlib/source/pthread/_context.s
===================================================================
--- unixlib/source/pthread/_context.s
+++ tmp.45736.00001	Thu Oct 17 18:22:28 2002
@@ -154,7 +154,7 @@
  ; Save regs to thread's save area
  	LDR	a1, =|__pthread_running_thread|
  	LDR	a1, [a1]
-	LDR	a1, [a1, #4]	; __pthread_running_thread->saved_context
+	LDR	a1, [a1, #__PTHREAD_CONTEXT_OFFSET]	; __pthread_running_thread->saved_context

  ; Copy integer regs
  	LDR	a2, =|__cbreg|
@@ -168,6 +168,7 @@
  	LDR	a4, [a4]
  	CMP	a4, #0
  	BEQ	no_fp1
+	[ {CONFIG} = 26
  	STFE	f0, [a1], #12
  	STFE	f1, [a1], #12
  	STFE	f2, [a1], #12
@@ -176,6 +177,9 @@
  	STFE	f5, [a1], #12
  	STFE	f6, [a1], #12
  	STFE	f7, [a1], #12
+	|
+	SFM	f0, 8, [a1], #96
+	]
  	RFS	a2	; Read floating status
  	STR	a2, [a1], #12
  no_fp1
@@ -192,13 +196,14 @@
  ; Now reload the registers from the new thread's save area
  	LDR	a1, =|__pthread_running_thread|
  	LDR	a1, [a1]
-	LDR	a2, [a1, #4]	; __pthread_running_thread->saved_context
+	LDR	a2, [a1, #__PTHREAD_CONTEXT_OFFSET]	; __pthread_running_thread->saved_context

  	LDR	a4, =|__fpflag|
  	LDR	a4, [a4]
  	CMP	a4, #0
  	BEQ	no_fp2
  	ADD	a2, a2, #16*4
+	[ {CONFIG} = 26
  	LDFE	f0, [a2], #12
  	LDFE	f1, [a2], #12
  	LDFE	f2, [a2], #12
@@ -207,6 +212,9 @@
  	LDFE	f5, [a2], #12
  	LDFE	f6, [a2], #12
  	LDFE	f7, [a2], #12
+	|
+	LFM	f0, 8, [a2], #96
+	]
  	LDR	a1, [a2], #12
  	WFS	a1	; Write floating status
  no_fp2
@@ -220,7 +228,7 @@

  	LDR	a1, =|__pthread_running_thread|
  	LDR	a1, [a1]
-	LDR	a1, [a1, #4]	; __pthread_running_thread->saved_context
+	LDR	a1, [a1, #__PTHREAD_CONTEXT_OFFSET]	; __pthread_running_thread->saved_context

  |callback_return|	; a1 = address of regs to load
  	[ {CONFIG} = 26
@@ -260,6 +268,7 @@
  	CMP	a4, #0
  	return	EQ, pc, lr
  	ADD	a2, a1, #16*4
+	[ {CONFIG} = 26
  	STFE	f0, [a2], #12
  	STFE	f1, [a2], #12
  	STFE	f2, [a2], #12
@@ -268,6 +277,9 @@
  	STFE	f5, [a2], #12
  	STFE	f6, [a2], #12
  	STFE	f7, [a2], #12
+	|
+	SFM	f0, 8, [a2], #96
+	]
  	RFS	a1	; Read floating status
  	STR	a1, [a2], #12
  	return	AL, pc, lr
Index: unixlib/source/pthread/init.c
===================================================================
--- unixlib/source/pthread/init.c
+++ tmp.45728.00001	Thu Oct 17 18:22:29 2002
@@ -16,8 +16,32 @@

  #include <unixlib/os.h>
  #include <unixlib/unix.h>
+#include <stdlib.h>
  #include <pthread.h>

+struct __pthread_thread mainthread;
+
+/* Called once, at program initialisation */
+void
+__pthread_prog_init (void)
+{
+#ifdef PTHREAD_DEBUG
+  __os_print ("-- __pthread_prog_init: Program initialisation\r\n");
+#endif
+
+  /* Create a node for the main program */
+  __pthread_running_thread = __pthread_new_node (&mainthread);
+
+  __pthread_thread_list = __pthread_running_thread;
+
+  __pthread_running_threads = 1;
+
+  __pthread_running_thread->magic = PTHREAD_MAGIC;
+}
+
+
+
+
  /* Called once, the first time a pthread call is made */
  void
  __pthread_init (void)
@@ -31,17 +55,12 @@
    __os_print ("-- __pthread_init: Initialising\r\n");
  #endif

-  /* Create a node for the main program */
-  __pthread_running_thread = __pthread_new_node ();
-  if (__pthread_running_thread == NULL)
-    __pthread_fatal_error ("-- __pthread_init: Unable to start threads (Out of memory)\r\n");
-
-  __pthread_running_thread->state = STATE_RUNNING;
-  __pthread_thread_list = __pthread_running_thread;
-
-  __pthread_running_threads = 1;
+  if (__pthread_invalid (__pthread_running_thread))
+    __pthread_fatal_error ("-- __pthread_init: Main thread not initialised\r\n");

-  __pthread_running_thread->magic = PTHREAD_MAGIC;
+  __pthread_running_thread->saved_context = malloc (sizeof (struct __pthread_saved_context));
+  if (__pthread_running_thread->saved_context == NULL)
+    __pthread_fatal_error ("-- __pthread_init: Unable to start threads (Out of memory)\r\n");

    /* Set up a stack for the context switcher */
    context_stack = __pthread_new_stack ();
Index: unixlib/source/pthread/create.c
===================================================================
--- unixlib/source/pthread/create.c
+++ tmp.45728.00001	Thu Oct 17 18:22:30 2002
@@ -23,75 +23,6 @@
  #include <pthread.h>


-/* Allocate and initialise an new thread node to hold all the details of the thread */
-pthread_t
-__pthread_new_node (void)
-{
-  pthread_t node;
-
-  node = malloc (sizeof (struct __pthread_thread));
-  if (node == NULL)
-    {
-#ifdef DEBUG_PTHREAD
-      __os_print("-- __pthread_new_node: Unable to allocate thread node\r\n");
-#endif
-      return NULL;
-    }
-
-  node->magic = 0; /* Ensure the magic number is invalid until the thread is ready to run */
-  node->next = NULL;
-  node->stack = NULL;
-  node->keys = NULL;
-  node->cleanupfns = NULL;
-  node->state = STATE_RUNNING;
-  node->cancelstate = PTHREAD_CANCEL_ENABLE;
-  node->canceltype = PTHREAD_CANCEL_DEFERRED;
-  node->cancelpending = 0;
-  node->detachstate = PTHREAD_CREATE_JOINABLE;
-  node->joined = NULL;
-  node->ret = NULL;
-  node->mutex = NULL;
-  node->cond = NULL;
-  node->nextwait = NULL;
-
-  node->saved_context = malloc (sizeof (struct __pthread_saved_context));
-  if (node->saved_context == NULL)
-    {
-#ifdef DEBUG_PTHREAD
-      __os_print ("-- _pthread_new_node: Unable to allocate thread saved_context\r\n");
-#endif
-      free (node);
-      return NULL;
-    }
-
-  return node;
-}
-
-/* Allocate and initialise a new stack chunk */
-struct __stack_chunk *
-__pthread_new_stack (void)
-{
-  struct __stack_chunk *stack;
-
-  stack = __stackalloc (PTHREAD_STACK_MIN);
-  if (stack == NULL)
-    {
-#ifdef DEBUG_PTHREAD
-      __os_print("-- __pthread_new_stack: Unable to allocate thread stack\r\n");
-#endif
-      return NULL;
-    }
-
-  stack->magic = STACK_CHUNK_MAGIC;
-  stack->next = NULL ;
-  stack->prev = NULL ;
-  stack->size = PTHREAD_STACK_MIN;
-  stack->dealloc = NULL;
-  stack->returnaddr = NULL;
-
-  return stack;
-}
-
  /* __pthread_create is the first thing called when a new thread is switched to */
  static void
  __pthread_create (pthread_t thread)
@@ -136,7 +67,7 @@
  #endif

    /* Allocate without actually adding the node into any lists */
-  thread = __pthread_new_node ();
+  thread = __pthread_new_node (NULL);

    if (thread == NULL)
      {
@@ -179,7 +110,6 @@
       __pthread_create handles the real starting up of this new
       thread when the context is reached */
    thread->saved_context->r[15] = (int)__pthread_create; /* set pc */
-  thread->saved_context->errno = 0;

    thread->start_routine = start_routine;
    thread->arg = arg;
Index: unixlib/source/pthread/exit.c
===================================================================
--- unixlib/source/pthread/exit.c
+++ tmp.45728.00001	Thu Oct 17 18:22:31 2002
@@ -65,10 +65,12 @@

    thread = __pthread_running_thread;

+  __pthread_exit ();
+
  #ifdef PTHREAD_DEBUG
    __os_print ("-- pthread_exit: Thread ");
    __os_prhex ((int)thread);
-  __os_print (" now exit idle\n\r");
+  __os_print (" now exit idle\r\n");
  #endif

    __pthread_running_threads--;
Index: unixlib/source/pthread/cond.c
===================================================================
--- unixlib/source/pthread/cond.c
+++ tmp.45728.00001	Thu Oct 17 18:22:32 2002
@@ -137,9 +137,16 @@
    thread = cond->waiting;
    if (thread != NULL)
      {
+#ifdef PTHREAD_DEBUG
+      printf ("-- pthread_cond_signal: Signalling thread %x, next waiting = %x\n", thread, thread->nextwait);
+#endif
        thread->state = STATE_RUNNING;
        cond->waiting = thread->nextwait;
      }
+#ifdef PTHREAD_DEBUG
+  else
+    printf ("No thread to signal\n");
+#endif

    __pthread_enable_ints ();
    return 0;
Index: unixlib/source/pthread/context.c
===================================================================
--- unixlib/source/pthread/context.c
+++ tmp.45736.00001	Thu Oct 17 18:22:33 2002
@@ -82,15 +82,13 @@
    __os_print ("\r\n");
  #endif

-  __pthread_running_thread->saved_context->errno = errno; /* save the current value of errno */
-
    next = __pthread_running_thread->next;

    /* Loop around the list looking for a thread that is running */
    do
      {
  #ifdef PTHREAD_DEBUG_CONTEXT
-      __os_print ("-- __pthread_context_switch: Looking for non idle thread\r\n");
+/*      __os_print ("-- __pthread_context_switch: Looking for non idle thread\r\n");*/
  #endif
        if (next != NULL)
          {
@@ -124,6 +122,13 @@
                __pthread_running_thread->state = STATE_RUNNING;
              }
          }
+#ifdef PTHREAD_DEBUG_CONTEXT
+      __os_print ("-- __pthread_context_switch: thread = ");
+      __os_prhex ((int)__pthread_running_thread);
+      __os_print (" State = ");
+      __os_prdec (__pthread_running_thread->state);
+      __os_print ("\r\n");
+#endif

      }
    while (__pthread_running_thread->state < STATE_RUNNING);
@@ -133,8 +138,6 @@
        /* Asynchronously cancel the thread */
        __pthread_running_thread->saved_context->r[15] = (int)pthread_testcancel;
      }
-
-  errno = __pthread_running_thread->saved_context->errno;

  #ifdef PTHREAD_DEBUG_CONTEXT
    __os_print ("-- __pthread_context_switch: New __pthread_running_thread  = ");
Index: unixlib/source/pthread/_yield.s
===================================================================
--- unixlib/source/pthread/_yield.s
+++ tmp.45728.00001	Thu Oct 17 18:22:34 2002
@@ -52,8 +52,8 @@
  	; Set the semaphore to prevent anyone else setting a callback untill this one has completed
  	LDR	a1, =|__pthread_callback_semaphore|
  	MOV	a2, #0
-	SWP	a2, a2, [a1]
-	CMP	a2, #1
+	swp_arm2	a3, a2, a1, a3
+	CMP	a3, #1
  	BNE	skip_callback	; Don't set the callback if someone else already has (this shouldn't ever occur)

  	; Record that we are expecting a callback
Index: unixlib/source/pthread/lock.c
===================================================================
--- unixlib/source/pthread/lock.c
+++ tmp.45728.00001	Thu Oct 17 18:22:35 2002
@@ -225,7 +225,7 @@
    __pthread_disable_ints ();

  #ifdef PTHREAD_DEBUG
-  printf ("-- __pthread_locak_unlock: %p for %p\n", mutex, __pthread_running_thread);
+  printf ("-- __pthread_lock_unlock: %p for %p\n", mutex, __pthread_running_thread);
  #endif

    if ((mutex->type != LOCK_READ && mutex->owner != __pthread_running_thread) || mutex->count == 0)
Index: unixlib/source/pthread/_exit.s
===================================================================
--- unixlib/source/pthread/_exit.s
+++ tmp.45728.00001	Thu Oct 17 18:22:35 2002
@@ -0,0 +1,38 @@
+;----------------------------------------------------------------------------
+;
+; $Source: $
+; $Date: $
+; $Revision: $
+; $State: $
+; $Author: $
+;
+;----------------------------------------------------------------------------
+
+; Written by Alex Waugh
+
+	GET	clib/unixlib/asm_dec.s
+
+	AREA |C$$code|, CODE, READONLY
+
+	IMPORT	|__alloca_thread_free_all|,WEAK
+	IMPORT	|___arm_alloca_thread_free_all|,WEAK
+
+	EXPORT	|__pthread_exit|
+
+;
+; Call alloca thread free functions as necessary
+|__pthread_exit|
+	STMFD	sp!, {lr}
+	LDR	a1, =|__alloca_thread_free_all|
+	ADR	lr, |__pthread_exit_l1|
+	CMP	a1, #0
+	MOVNE	pc, a1
+|__pthread_exit_l1|
+	LDR	a1, =|___arm_alloca_thread_free_all|
+	LDMFD	sp!, {lr}
+	CMP	a1, #0
+	MOVNE	pc, a1
+	return	AL, pc, lr
+
+
+ END
Index: unixlib/source/pthread/_ints.s
===================================================================
--- unixlib/source/pthread/_ints.s
+++ tmp.45728.00001	Thu Oct 17 18:22:36 2002
@@ -33,8 +33,8 @@
  ; May be called from USR or SVC mode
  |__pthread_disable_ints|
  	LDR	a1, =|__pthread_worksemaphore|
-	MOV	a2, #1
-	SWP	a2, a2, [a1]	; From this point onwards we will not be interrupted by the callback
+	MOV	a3, #1
+	swp_arm2	a2, a3, a1, a2	; From this point onwards we will not be interrupted by the callback
  	ADD	a2, a2, #1
  	STR	a2, [a1]
  	return	AL, pc, lr
@@ -72,9 +72,9 @@
  	]
  	LDR	a1, =|__pthread_worksemaphore|
  	MOV	a2, #1
-	SWP	a2, a2, [a1]	; From this point onwards we cannot be interrupted by the callback
-	CMP	a2, #0
-	STRNE	a2, [a1]	; Restore original value
+	swp_arm2	a3, a2, a1, a3	; From this point onwards we cannot be interrupted by the callback
+	CMP	a3, #0
+	STRNE	a3, [a1]	; Restore original value
  	; Return, as if ints are disabled on entry to the
  	; calling function then they should not be reenabled
  	; until the calling function has returned
@@ -110,7 +110,7 @@


  ; Similar to __pthread_enable_ints, but return to the saved __pthread_return_address
-; Can corrupt a2-a4,lr but NOT a1
+; Can corrupt a2-a4,ip,lr but NOT a1
  |__pthread_unprotect_unsafe|
  	LDR	a2, =|__pthread_return_address|
  	LDR	lr, [a2]
Index: unixlib/source/Makefile
===================================================================
--- unixlib/source/Makefile
+++ tmp.62416.00001	Thu Oct 17 18:22:38 2002
@@ -303,6 +303,7 @@
  	$(libunixobj)/pthread/lock.o \
  	$(libunixobj)/pthread/mutex.o \
  	$(libunixobj)/pthread/mutexattr.o \
+	$(libunixobj)/pthread/newnode.o \
  	$(libunixobj)/pthread/once.o \
  	$(libunixobj)/pthread/rwlock.o \
  	$(libunixobj)/pthread/rwlockattr.o \
@@ -311,6 +312,7 @@
  	$(libunixobj)/pthread/testcancel.o

  PTHREAD_ASM = $(libunixobj)/pthread/_context.o \
+	$(libunixobj)/pthread/_exit.o \
  	$(libunixobj)/pthread/_ints.o \
  	$(libunixobj)/pthread/_yield.o

@@ -998,6 +1000,7 @@
  $(libunixobj)/pthread/lock.o: pthread/lock.c clib/pthread.h
  $(libunixobj)/pthread/mutex.o: pthread/mutex.c clib/pthread.h
  $(libunixobj)/pthread/mutexattr.o: pthread/mutexattr.c clib/pthread.h
+$(libunixobj)/pthread/newnode.o: pthread/newnode.c clib/pthread.h
  $(libunixobj)/pthread/once.o: pthread/once.c clib/pthread.h
  $(libunixobj)/pthread/rwlock.o: pthread/rwlock.c clib/pthread.h
  $(libunixobj)/pthread/rwlockattr.o: pthread/rwlockattr.c clib/pthread.h
@@ -1006,6 +1009,7 @@
  $(libunixobj)/pthread/testcancel.o: pthread/testcancel.c clib/pthread.h

  $(libunixobj)/pthread/_context.o: pthread/_context.s
+$(libunixobj)/pthread/_exit.o: pthread/_exit.s
  $(libunixobj)/pthread/_ints.o: pthread/_ints.s
  $(libunixobj)/pthread/_yield.o: pthread/_yield.s

Index: unixlib/source/signal/post.c
===================================================================
--- unixlib/source/signal/post.c
+++ tmp.35848.00001	Thu Oct 17 18:22:40 2002
@@ -24,6 +24,8 @@
  #include <unixlib/unix.h>
  #include <unixlib/sigstate.h>

+#include <pthread.h>
+
  /* #define DEBUG 1 */

  /* This function chooses a suitable execution environment
@@ -338,6 +340,9 @@
  void
  __unixlib_raise_signal (struct unixlib_sigstate *ss, int signo)
  {
+/*  PTHREAD_UNSAFE*/
+  __pthread_disable_ints();
+
    if (ss == NULL)
      ss = &__u->sigstate;

@@ -361,4 +366,5 @@
        __os_print (" is pending for delivery\r\n");
      }
  #endif
+  __pthread_enable_ints();
  }
Index: unixlib/source/signal/_signal.s
===================================================================
--- unixlib/source/signal/_signal.s
+++ tmp.35848.00001	Thu Oct 17 18:22:41 2002
@@ -22,13 +22,12 @@

  	AREA	|C$$code|, CODE, READONLY

-	IMPORT	errno
-	IMPORT	sys_errlist
  	IMPORT	|__unixlib_raise_signal|
  	IMPORT	|__pthread_system_running|
  	IMPORT	|__pthread_disable_ints|
  	IMPORT	|__pthread_enable_ints|
  	IMPORT	|__pthread_callback_pending|
+	IMPORT	|__pthread_running_thread|


  ;-----------------------------------------------------------------------
@@ -79,14 +78,16 @@
  	MOVEQS	pc, lr

  	STMFD	sp!, {v1-v5,lr}		; Stack working registers
-	LDR	a2, =|errno|		; Set errno = EOPSYS
  	MOV	a3, #EOPSYS
-	STR	a3, [a2, #0]
+	__set_errno	a3, a2 ; Set errno = EOPSYS

-	LDR     a4, =sys_errlist
+	[ __FEATURE_PTHREADS = 1
+	LDR	a2, =|__pthread_running_thread|
+	LDR	a2, [a2]
+	ADD	a2, a2, #__PTHREAD_ERRBUF_OFFSET
+	|
  	LDR     a2, =|__ul_errbuf_errblock|
-	ADD     a3, a2, #4
-	STR     a3, [a4, #(EOPSYS:SHL:2)]
+	]

  	; Copy the error to UnixLib's buffer.
  	MOV	a3, #|__ul_errbuf__size|
@@ -114,7 +115,13 @@
  	EXPORT	|_kernel_last_oserror|
  	NAME	_kernel_last_oserror
  |_kernel_last_oserror|
+	[ __FEATURE_PTHREADS = 1
+	LDR	a1, =|__pthread_running_thread|
+	LDR	a1, [a1]
+	ADD	a1, a1, #__PTHREAD_ERRBUF_OFFSET
+	|
  	LDR	a1, =|__ul_errbuf_errblock|
+	]
  	LDR	a2, [a1, #0]
  	CMP	a2, #0
  	MOVNES	pc, lr
@@ -312,19 +319,25 @@
  	]

  	; Set errno to EOPSYS
-	LDR	a1, =|errno|
  	MOV	a2, #EOPSYS
-	STR	a2, [a1, #0]
+	__set_errno	a2, a1

-	; sys_errlist[EOPSYS] = __ul_errbuf_errblock+4
-	LDR	a1, =sys_errlist
-	LDR	a3, =|__ul_errbuf_errblock|
-	ADD	a2, a3, #4
-	STR	a2, [a1, #(EOPSYS:SHL:2)]
+	[ __FEATURE_PTHREADS = 1
+	IMPORT	|memcpy|
+	; Copy error buffer into thread specific storage
+	LDR	a1, =|__pthread_running_thread|
+	LDR	a1, [a1]
+	ADD	a1, a1, #__PTHREAD_ERRBUF_OFFSET
+	LDR	a2, =|__ul_errbuf_errblock|
+	LDR	a3, =|__ul_errbuf__size|
+	LDR	a3, [a3]
+	BL	|memcpy|
+	]

  	; Check the error number. Its value will determine the
  	; appropriate signal to call.
-	LDR	a3, [a3, #0]
+	LDR	a2, =|__ul_errbuf_errblock|
+	LDR	a3, [a2, #0]

  	; Check bit 31 of the error number.  If it is set, then it
  	; indicates a serious error, usually a hardware exception e.g.
@@ -351,7 +364,7 @@
  	; Print the error
  	ADR	a1, unrecoverable_error_msg
  	SWI	XOS_Write0
-	MOV	a1, a2		; Write out __ul_errbuf_errblock+4
+	ADD	a1, a2, #4		; Write out __ul_errbuf_errblock+4
  	SWI	XOS_Write0
  	SWI	XOS_NewLine

@@ -506,6 +519,7 @@
  |__h_cback|
  	[ __FEATURE_PTHREADS = 1
  	; Check if the pthreads code was expecting a callback
+
  	LDR	a1, =|__pthread_callback_pending|
  	LDR	a2, [a1]
  	CMP	a2, #0
@@ -516,7 +530,7 @@
  	LDR	a1, =|__pthread_system_running|
  	LDR	a1, [a1]
  	CMP	a1, #0
-	BL	|__pthread_disable_ints|
+	BL	|__pthread_disable_ints|
  	]

  	TEQP	pc, #IFlag	; USR mode IntOff (irq off, fiq on)
@@ -755,7 +769,8 @@

  ;-----------------------------------------------------------------------
  ; UnixLib's error buffer.
-; Note, sys_errlist[EOPSYS] points to this buffer at __ul_errbuf.
+; If threads are in use then it is only temporary, as the error handler
+; copies it into the thread's error buffer.
  ;
  ; According to 1-289 the error handler must provide an error buffer of
  ; size 256 bytes, the address of which should be set along with the
Index: unixlib/source/sys/_jmp.s
===================================================================
--- unixlib/source/sys/_jmp.s
+++ tmp.52248.00001	Thu Oct 17 18:22:47 2002
@@ -23,12 +23,16 @@
  setjmp
  	; record the current allocation pointer for use with longjmp
  	; Note, alloca_list is a weak symbol so may not be set
-	; Warning!!!
-	; Do not change alloca_list so that it is not weak
-	; because it is weak in _syslib.s and it would confuse the linker
+	[ __FEATURE_PTHREADS = 1
+	IMPORT	|__pthread_running_thread|
+	LDR	a4, =|__pthread_running_thread|
+	LDR	a4, [a4]
+	LDR	a4, [a4, #|__PTHREAD_ALLOCA_OFFSET| + 8]
+	|
  	LDR	a4, =|__alloca_list|
  	CMP	a4, #0
  	LDRNE	a4, [a4]
+	]
  	STR	a4, [a1], #4

  	LDR	a4, =|__fpflag|
@@ -65,7 +69,13 @@
  	; safely restored to their current values.


+	[ __FEATURE_PTHREADS = 1
+	LDR	v5, =|__pthread_running_thread|
+	LDR	v5, [v5]
+	ADD	v5, v5, #|__PTHREAD_ALLOCA_OFFSET| + 8
+	|
  	LDR	v5, =|__alloca_list|
+	]
  	LDR	v4, [a1], #4
  	CMP	v5, #0
  	LDRNE	v3, [v5]
Index: unixlib/source/sys/_vfork.s
===================================================================
--- unixlib/source/sys/_vfork.s
+++ tmp.52248.00001	Thu Oct 17 18:22:47 2002
@@ -16,6 +16,13 @@
  	IMPORT	|__vexit|
  	IMPORT	|__dynamic_num|
  	IMPORT	|__real_break|
+	IMPORT	|__pthread_system_running|
+	IMPORT	|__pthread_atfork_callprepare|
+	IMPORT	|__pthread_atfork_callparentchild|
+	IMPORT	|__pthread_disable_ints|
+	IMPORT	|__pthread_enable_ints|
+	IMPORT	|__pthread_start_ticker|
+	IMPORT	|__pthread_stop_ticker|

  	IMPORT	setjmp
  	IMPORT	longjmp
@@ -23,24 +30,61 @@
  	EXPORT	vfork
  	NAME	vfork
  vfork
-	STMFD	sp!, {v1, lr}
+	STMFD	sp!, {lr}
+	[ __FEATURE_PTHREADS = 1
+	LDR	a1, =|__pthread_system_running|
+	LDR	a1, [a1]
+	CMP	a1, #0
+	BEQ	pthread_skip1
+	BL	|__pthread_atfork_callprepare|
+	; Child process only contins a single thread
+	BL	|__pthread_stop_ticker|
+pthread_skip1
+	]
  	BL	|__vfork|
  	; If zero was returned, we will return -1
  	CMP	a1, #0
-	MVNEQ	a1, #0
-	stackreturn	EQ, "v1, pc"
-	LDMFD	sp!, {v1, lr}
+	BEQ	return_fail
+	LDMFD	sp!, {lr}
  	; save lr for use when setjmp returns either immediately or
  	; via longjmp as spawned program exits and current program
  	; starts running again.
  	LDR	a2, =|__saved_lr|
  	STR	lr, [a2]
+	[ __FEATURE_PTHREADS = 1
+	; save __pthread_system_running, as the child process
+	; will always set it to 0 on exit
+	LDR	a3, =|__pthread_system_running|
+	LDR	a2, =|__saved_pthread_system_running|
+	LDR	a3, [a3]
+	STR	a3, [a2]
+	]
  	BL	setjmp
+	[ __FEATURE_PTHREADS = 1
+	LDR	a2, =|__saved_pthread_system_running|
+	LDR	a3, =|__pthread_system_running|
+	LDR	a2, [a2]
+	STR	a2, [a3]
+	]
  	LDR	a2, =|__saved_lr|
  	TEQ	a1, #0
  	LDR	lr, [a2]
-	return	EQ, pc, lr
+	BNE	return_parent
+	; Return from the vfork in the child process
+	STMFD	sp!, {lr}
+	[ __FEATURE_PTHREADS = 1
+	LDR	a1, =|__pthread_system_running|
+	LDR	a1, [a1]
+	CMP	a1, #0
+	BEQ	pthread_skip2
+	MOV	a1, #0
+	BL	|__pthread_atfork_callparentchild|
+pthread_skip2
+	]
+	MOV	a1, #0
+	stackreturn	AL, "pc"

+return_parent	; Return from the vfork in the parent process
  	; Restore dynamic area size to size indicated by __real_break
  	; which was the size of the area before the forked program
  	; ran and extended the dynamic area. We can't do this anywhere
@@ -50,7 +94,7 @@
  	LDR	a2, =|__dynamic_num|
  	LDR	a2, [a2]
  	CMN	a2, #1
-	stackreturn	EQ, "a1,a2,a3,a4,v1,v2,v3,v4,v5,pc"
+	BEQ	return_parent2
  	MOV	a1, #2
  	SWI	XOS_DynamicArea
  	MOVVC	a1, a2
@@ -59,8 +103,37 @@
  	LDRVC	a2, [a2]
  	SUBVC	a2, a2, a3
  	SWIVC	XOS_ChangeDynamicArea
+return_parent2
+	[ __FEATURE_PTHREADS = 1
+	LDR	a1, =|__pthread_system_running|
+	LDR	a1, [a1]
+	CMP	a1, #0
+	BEQ	pthread_skip3
+	BL	|__pthread_start_ticker|
+	MOV	a1, #1
+	BL	|__pthread_atfork_callparentchild|
+pthread_skip3
+	]
+	SWI	XOS_WriteS
+	DCB	"exiting to parent",10,13,0
+	ALIGN
  	stackreturn	AL, "a1,a2,a3,a4,v1,v2,v3,v4,v5,pc"

+return_fail
+	[ __FEATURE_PTHREADS = 1
+	LDR	a1, =|__pthread_system_running|
+	LDR	a1, [a1]
+	CMP	a1, #0
+	BEQ	pthread_skip4
+	BL	|__pthread_start_ticker|
+	MOV	a1, #1
+	BL	|__pthread_atfork_callparentchild|
+pthread_skip4
+	]
+	MVN	a1, #0
+	stackreturn	AL, "pc"
+
+
  	EXPORT	|__vret|
  |__vret|
  	BL	|__vexit|
@@ -70,5 +143,9 @@
  	AREA	|C$$zidata|, DATA, NOINIT
  |__saved_lr|
  	%	4
+	[ __FEATURE_PTHREADS = 1
+|__saved_pthread_system_running|
+	%	4
+	]

  	END
Index: unixlib/source/sys/_alloca.s
===================================================================
--- unixlib/source/sys/_alloca.s
+++ tmp.52248.00001	Thu Oct 17 18:22:48 2002
@@ -47,7 +47,14 @@
  	; + 4 : return link value of caller (i.e. [fp, #-4])
  	; + 8 : start contents block returned from alloca()

+	[ |__FEATURE_PTHREADS| = 1
+	IMPORT  |__pthread_running_thread|
+	LDR	a3, =|__pthread_running_thread|
+	LDR	a3, [a3]
+	ADD	a3, a3, #|__PTHREAD_ALLOCA_OFFSET| + 8
+	|
  	LDR	a3, =|__alloca_list|
+	]
  	LDR	a2, [fp, #-4]
  	LDR	a4, [a3]
  	STR	a2, [a1, #4]
@@ -67,7 +74,13 @@
  	stackreturn	AL, "pc"

  |__alloca_free|
+	[ |__FEATURE_PTHREADS| = 1
+	LDR	a3, =|__pthread_running_thread|
+	LDR	a3, [a3]
+	ADD	a3, a3, #|__PTHREAD_ALLOCA_OFFSET| + 8
+	|
  	LDR	a3, =|__alloca_list|	; StrongARM order
+	]
  	STMFD	sp!, {a1, a2, v1}
  	LDR	a1, [a3]
  	LDMIA	a1, {a4, v1}
@@ -100,11 +113,33 @@
  	SWI	XOS_Write0
  	B	abort		; never returns

+; Free all alloca blocks for the current thread
+	[ |__FEATURE_PTHREADS| = 1
+	EXPORT	|__alloca_thread_free_all|
+|__alloca_thread_free_all|
+	STMFD	sp!, {v1, lr}
+	LDR	a1, =|__pthread_running_thread|
+	LDR	a1, [a1]
+	LDR	v1, [a1, #|__PTHREAD_ALLOCA_OFFSET| + 8]
+	MOV	a2, #0
+	STR	a2, [a1, #|__PTHREAD_ALLOCA_OFFSET| + 8]
+__alloca_thread_free_all_l1
+	CMP	v1, #0
+	stackreturn	EQ, "v1, pc"
+	MOV	a1, v1
+	LDR	v1, [v1]
+	BL	free
+	B	__alloca_thread_free_all_l1
+	]

-	AREA	|C$$zidata|, DATA, NOINIT
+
+	[ __FEATURE_PTHREADS = 0
+	; If pthreads are in use then __alloca_list is stored per-thread
+	AREA	|C$$data|, DATA

  	EXPORT  |__alloca_list|
  |__alloca_list|
-	%	4
+	DCD	0
+	]

  	END
Index: unixlib/source/sys/_syslib.s
===================================================================
--- unixlib/source/sys/_syslib.s
+++ tmp.52248.00001	Thu Oct 17 18:22:50 2002
@@ -76,7 +76,6 @@
  	IMPORT	|_main|
  	IMPORT	|__dynamic_no_da|, WEAK
  	IMPORT	|__dynamic_da_name|, WEAK
-	IMPORT	|__alloca_list|, WEAK

  |rmensure|
  	DCB "RMEnsure SharedUnixLibrary 1.00 RMLoad System:Modules.SharedULib", 0
@@ -323,11 +322,8 @@
  	STR	v5, [v3, #0]

  no_dynamic_area
-	LDR	a1, =|__alloca_list|

  	MOV	fp, #0
-	CMP	a1, #0
-	STRNE	fp, [a1, #0]

  	; This line is needed for the GNU Objective-C runtime system.
  	; We need a safe 128 bytes of stack space to prevent a program
Index: unixlib/source/sys/exec.c
===================================================================
--- unixlib/source/sys/exec.c
+++ tmp.52248.00001	Thu Oct 17 18:22:50 2002
@@ -27,6 +27,7 @@
  #include <sys/wait.h>
  #include <unixlib/local.h>
  #include <unixlib/features.h>
+#include <pthread.h>

  /* #define DEBUG 1 */

@@ -252,6 +253,16 @@
    __os_print ("\r\n");
  #endif

+#if __FEATURE_PTHREADS
+  /* All threads are terminated on an exec call.
+     Destructor functions are not called. */
+  if (__pthread_system_running)
+    {
+      __pthread_stop_ticker ();
+      __pthread_system_running = 0;
+    }
+#endif
+
  #if __FEATURE_ITIMERS
    /* Stop any interval timers that might be running.  Technically
       the new process should inherit the pending alarms.  */
@@ -301,8 +312,9 @@
    __os_print ("\r\n");
  #endif

-  /* Hmmm.  This malloc *must not* be in a dynamic area.  */
-  cli = (char *) malloc (cli_length + 1);
+  /* This malloc *must not* be in a dynamic area.  */
+  cli = (char *) __stackalloc (cli_length + 1);
+  __u->cli = cli;
    if (cli == NULL)
      return __set_errno (ENOMEM);

@@ -497,6 +509,7 @@
       is guaranteed to minimise the malloc heap as best as possible
       before running the child program.  */
    malloc_trim (0);
+  __stackalloc_trim ();

    if (((unsigned int) __base & ~0xff) == 0x8000)
      {
@@ -662,6 +675,8 @@
  #ifdef DEBUG
    __debug ("-- __exret: process table has just been relocated");
  #endif
+
+  __stackfree (__u->cli);

    __u->status.has_parent = 1;
    if (__exerr)
Index: unixlib/source/sys/errlist.c
===================================================================
--- unixlib/source/sys/errlist.c
+++ tmp.52248.00001	Thu Oct 17 18:22:51 2002
@@ -120,10 +120,41 @@
  };

  char *
-strerror (register int e)
+strerror (register int errnum)
  {
-  if (e < 0 || e >= sys_nerr)
-    return (0);
+  if (errnum < 0 || errnum >= sys_nerr)
+    {
+      __set_errno (EINVAL);
+      return (char *) "Unknown Error";
+    }
+  if (errnum == EOPSYS)
+    {
+#if __FEATURE_PTHREADS
+      return __pthread_running_thread->errbuf.errmess;
+#else
+      return __ul_errbuf + 8;
+#endif
+    }

-  return (char *) sys_errlist[e];
+  return (char *) sys_errlist[errnum];
+}
+
+int
+strerror_r (int errnum, char *strerrbuf, size_t buflen)
+{
+  size_t len;
+  const char *errmess;
+
+  if (errnum < 0 || errnum >= sys_nerr)
+    return EINVAL;
+
+  errmess = strerror (errnum);
+
+  len = strlen (errmess);
+  if (len >= buflen)
+    return ERANGE;
+
+  memcpy (strerrbuf, errmess, len + 1);
+
+  return 0;
  }
Index: unixlib/source/stdio/perror.c
===================================================================
--- unixlib/source/stdio/perror.c
+++ tmp.20816.00001	Thu Oct 17 18:22:54 2002
@@ -14,14 +14,15 @@

  #include <errno.h>
  #include <stdio.h>
+#include <string.h>

  __STDIOLIB__

  void
  perror (const char *string)
  {
-  const char *error = (errno < sys_nerr) ? sys_errlist[errno]
-					 : "Unknown Error";
+  const char *error = strerror (errno);
+
    if (string)
      fprintf (stderr, "%s: %s\n", string, error);
    else
Index: unixlib/source/unix/unix.c
===================================================================
--- unixlib/source/unix/unix.c
+++ tmp.60968.00001	Thu Oct 17 18:22:58 2002
@@ -78,7 +78,7 @@
  __unixlib_fatal (const char *message)
  {
    if (message == NULL)
-    message = sys_errlist[errno];
+    message = strerror (errno);
    __os_nl ();
    __os_print (message);
    __os_nl ();
@@ -139,6 +139,10 @@
        if (__u == NULL)
          __unixlib_fatal ("cannot allocate memory for process structure");
        initialise_process_structure (__u);
+#if __FEATURE_PTHREADS
+      /* Initialise the pthread system */
+      __pthread_prog_init ();
+#endif
        __resource_initialise (__u);
        __unixlib_signal_initialise (__u);
        /* Initialise ctype tables to the C locale.  */
@@ -149,6 +153,10 @@
      }
    else
      {
+#if __FEATURE_PTHREADS
+      /* Initialise the pthread system */
+      __pthread_prog_init ();
+#endif
        __resource_initialise (__u);
        __unixlib_signal_initialise (__u);
        /* Initialise ctype tables to the C locale.  */
Index: unixlib/source/clib/string.h
===================================================================
--- unixlib/source/clib/string.h
+++ tmp.32048.00001	Thu Oct 17 18:23:02 2002
@@ -131,6 +131,9 @@
  /* Return the descriptive error message string for an error code.  */
  extern char *strerror (int __errnum);

+/* Re-entrant version of strerror */
+extern int strerror_r (int __errnum, char *__strerrbuf, size_t __buflen);
+
  __END_DECLS

  #endif
Index: unixlib/source/clib/unixlib/asm_dec.s
===================================================================
--- unixlib/source/clib/unixlib/asm_dec.s
+++ tmp.40504.00001	Thu Oct 17 18:23:04 2002
@@ -21,6 +21,9 @@
  DYNAMIC_AREA	EQU	1
  ; Align things to 4K boundaries for exec
  |__4K_BOUNDARY|	EQU	0
+; Emulate the SWP instruction for ARM2 compatibility
+|__SWP_ARM2|	EQU	0
+
  |__INTEGRITY_CHECK|	EQU	1
  |__FEATURE_ITIMERS|	EQU	1
  |__FEATURE_SOCKET|	EQU	1
@@ -28,6 +31,14 @@
  |__FEATURE_PTHREADS|	EQU	1
  USEFILEPATH		EQU	0

+; The offset of various members of the __pthread_thread structure
+; This should be kept in sync with pthread.h and lib1aof.s
+__PTHREAD_MAGIC_OFFSET	EQU	0
+__PTHREAD_CONTEXT_OFFSET	EQU	4
+__PTHREAD_ALLOCA_OFFSET	EQU	8
+__PTHREAD_ERRNO_OFFSET	EQU	20
+__PTHREAD_ERRBUF_OFFSET	EQU	24
+
  ; registers

  r0	RN	0
@@ -126,16 +137,32 @@
  	MACRO
  	__set_errno	$val,$Rerrno
  	ASSERT	$val <> $Rerrno
+	[ __FEATURE_PTHREADS = 1
+	IMPORT	|__pthread_running_thread|
+	LDR	$Rerrno, =|__pthread_running_thread|
+	LDR	$Rerrno, [$Rerrno]
+	STR	$val, [$Rerrno, #__PTHREAD_ERRNO_OFFSET]
+	|
+	IMPORT	|errno|
  	LDR	$Rerrno,=|errno|
  	STR	$val,[$Rerrno]
+	]
  	MOV	$val,#-1
  	MEND

  	MACRO
  	__get_errno	$val,$Rerrno
  	ASSERT	$val <> $Rerrno
+	[ __FEATURE_PTHREADS = 1
+	IMPORT	|__pthread_running_thread|
+	LDR	$Rerrno, =|__pthread_running_thread|
+	LDR	$Rerrno, [$Rerrno]
+	LDR	$val, [$Rerrno, #__PTHREAD_ERRNO_OFFSET]
+	|
+	IMPORT	|errno|
  	LDR	$Rerrno,=|errno|
  	LDR	$val,[$Rerrno]
+	]
  	MEND

  	; NetSWI macro to call a networking (tcp/ip) swi.
@@ -225,6 +252,27 @@
  	MOVVCS	pc, ip			; return, restore mode, flags
  	B	|__net_error_simple_entry|
  	; branch to error routine still in SVC mode, with return in ip
+
+	MEND
+
+	; Macro to implement SWP instruction
+	; srcreg and dstreg can be the same register, provided scratch is a different register
+	; if srcreg and dstreg are different registers then scratch can be the same as dstreg
+	MACRO
+	swp_arm2	$dstreg, $srcreg, $addr, $scratch
+	[ __SWP_ARM2 = 0
+	SWP	$dstreg, $srcreg, [$addr]
+	|
+	STMFD	sp!, {lr}	; Could be called in USR or SVC mode
+	SWI	XOS_IntOff
+	LDR	$scratch, [$addr]
+	STR	$srcreg, [$addr]
+	[ $dstreg <> $scratch
+	MOV	$dstreg, $scratch
+	]
+	SWI	XOS_IntOn
+	LDMFD	sp!, {lr}
+	]

  	MEND

Index: unixlib/source/clib/pthread.h
===================================================================
--- unixlib/source/clib/pthread.h
+++ tmp.32048.00001	Thu Oct 17 18:23:05 2002
@@ -19,6 +19,9 @@
  #define __need_size_t
  #include <stddef.h>

+#define __need_clock_t
+#include <time.h>
+
  #include <sched.h> /* for struct sched_param */

  #if !defined __pthread_t_defined && (defined __PTHREAD_H || defined __need_pthread_t)
@@ -84,6 +87,73 @@

  typedef int pthread_once_t; /* Type for pthread_once */

+/* Current schedulability state of the thread */
+enum __pthread_state
+{
+  STATE_IDLE,
+  STATE_MUTEX_WAIT, /* Waiting for a mutex to become available */
+  STATE_COND_WAIT, /* Waiting for a condition variable to be signalled */
+  STATE_COND_TIMED_WAIT, /* Waiting for a condition variable with a timeout */
+  STATE_JOIN,
+  STATE_UNALLOCED,
+
+  /* All states below this point are for threads that may be switched in by the scheduler */
+  STATE_RUNNING, /* A normally running thread */
+  STATE_CLEANUP  /* A (possibly cancelled) thread that is running its cleanup handlers */
+};
+
+/* Hold all details about the thread. A pthread_t points to one of these structures */
+struct __pthread_thread
+{
+  int magic; /* Magic number */
+
+  struct __pthread_saved_context *saved_context; /* Space for registers on the context switch */
+
+  int alloca[3]; /* Storage for alloca */
+
+  int thread_errno; /* Value of errno for this thread */
+
+  struct
+  {
+    int errnum;
+    char errmess[252];
+  } errbuf; /* Last RISC OS error from error handler */
+
+  /* WARNING: Various assembly files refer to this structure, using
+     offsets defined in asm_dec.s
+     If you change the ordering of any of the elements above this
+     comment then make sure the offsets are kept in sync. */
+
+
+  struct __pthread_thread *next; /* Linked list of all threads */
+  enum __pthread_state state; /* Running/blocked/idle/etc. */
+  void *ret; /* Value returned from thread */
+
+  void *(*start_routine)(void *); /* Function to call as the new thread */
+  void *arg; /* Argument to pass to the start_routine */
+
+  struct __stack_chunk *stack; /* Initial stack chunk allocated to thread */
+
+  struct __pthread_thread *joined; /* Thread that wishes to join with this thread */
+
+  struct __pthread_key *keys; /* Linked list of keys associated with this thread */
+
+  pthread_mutex_t *mutex; /* Mutex that the thread is waiting for */
+  enum __pthread_locktype mutextype; /* Type of mutex that the thread is waiting for */
+  pthread_cond_t *cond; /* Condition var that the thread is waiting for */
+  clock_t condtimeout; /* Timeout value for condition var */
+  struct __pthread_thread *nextwait; /* Next thread that is waiting on the same mutex/condition var */
+
+  struct __pthread_cleanup *cleanupfns; /* Linked list of cleanup functions to call when this thread exits */
+
+  unsigned int cancelstate : 1; /* Cancelability state of this thread (enabled or disabled) */
+  unsigned int canceltype : 1; /* Cancelability type (asynchronous or deferred) */
+  unsigned int cancelpending : 1; /* Should this thread be cancelled when it next reaches a cancellation point */
+  unsigned int detachstate : 1; /* Is the thread detached of can it still be joined to */
+};
+
+extern pthread_t __pthread_running_thread; /* Currently running thread */
+
  #endif /* !defined __pthread_t_defined && (defined __PTHREAD_H || defined __need_pthread_t) */
  #undef __need_pthread_t

@@ -357,67 +427,19 @@
  #ifdef __UNIXLIB_INTERNALS

  /* Print lots of general debugging info */
-/*#define PTHREAD_DEBUG */
+/*#define PTHREAD_DEBUG*/
  /* Print debug info for the context switcher */
-/*#define PTHREAD_DEBUG_CONTEXT */
+/*#define PTHREAD_DEBUG_CONTEXT*/

+#ifndef __UNIXLIB_FEATURES_H
  #include <unixlib/features.h>
+#endif

  /* Magic number to check a pthread_t is valid */
  #define PTHREAD_MAGIC 0x52485450

  #define __pthread_invalid(thread) (thread == NULL || thread->magic != PTHREAD_MAGIC)

-/* Current schedulability state of the thread */
-enum __pthread_state
-{
-  STATE_IDLE,
-  STATE_MUTEX_WAIT, /* Waiting for a mutex to become available */
-  STATE_COND_WAIT, /* Waiting for a condition variable to be signalled */
-  STATE_COND_TIMED_WAIT, /* Waiting for a condition variable with a timeout */
-  STATE_JOIN,
-  STATE_UNALLOCED,
-
-  /* All states below this point are for threads that may be switched in by the scheduler */
-  STATE_RUNNING, /* A normally running thread */
-  STATE_CLEANUP  /* A (possibly cancelled) thread that is running its cleanup handlers */
-};
-
-/* Hold all details about the thread. A pthread_t points to one of these structures */
-struct __pthread_thread
-{
-  int magic; /* Magic number */
-
-  struct __pthread_saved_context *saved_context; /* Space for registers on the context switch */
-  /* WARNING: saved_context should remain an offset of 4 bytes from the start of the struct for __pthread_callback */
-
-  struct __pthread_thread *next; /* Linked list of all threads */
-  enum __pthread_state state; /* Running/blocked/idle/etc. */
-  void *ret; /* Value returned from thread */
-
-  void *(*start_routine)(void *); /* Function to call as the new thread */
-  void *arg; /* Argument to pass to the start_routine */
-
-  struct __stack_chunk *stack; /* Initial stack chunk allocated to thread */
-
-  struct __pthread_thread *joined; /* Thread that wishes to join with this thread */
-
-  struct __pthread_key *keys; /* Linked list of keys associated with this thread */
-
-  pthread_mutex_t *mutex; /* Mutex that the thread is waiting for */
-  enum __pthread_locktype mutextype; /* Type of mutex that the thread is waiting for */
-  pthread_cond_t *cond; /* Condition var that the thread is waiting for */
-  clock_t condtimeout; /* Timeout value for condition var */
-  struct __pthread_thread *nextwait; /* Next thread that is waiting on the same mutex/condition var */
-
-  struct __pthread_cleanup *cleanupfns; /* Linked list of cleanup functions to call when this thread exits */
-
-  unsigned int cancelstate : 1; /* Cancelability state of this thread (enabled or disabled) */
-  unsigned int canceltype : 1; /* Cancelability type (asynchronous or deferred) */
-  unsigned int cancelpending : 1; /* Should this thread be cancelled when it next reaches a cancellation point */
-  unsigned int detachstate : 1; /* Is the thread detached of can it still be joined to */
-};
-
  /* Holds data about a thread specific key */
  struct __pthread_key
  {
@@ -433,23 +455,23 @@
    int r[16]; /* User mode integer registers */
    char fpregs[12*8]; /* Floating point registers */
    int fpstatus; /* Floatingpoint status register */
-  int errno; /* Value of errno */
  };

-extern pthread_t __pthread_thread_list; /* Lined list of all threads */
-extern pthread_t __pthread_running_thread; /* Currently running thread */
+extern pthread_t __pthread_thread_list; /* Linked list of all threads */
  extern int __pthread_system_running; /* Has the thread system been initialised yet */
  extern int __pthread_running_threads; /* Number of threads currently running or blocked */
  extern void *__pthread_context_stack_sp; /* Stack pointer to use for the scheduler */
  extern void *__pthread_context_stack_sl; /* Stack limit to use for the scheduler */

-/* Called once to initialise the thread system */
+/* Called once early in program initialisation */
+extern void __pthread_prog_init (void);
+/* Called once on first call to a pthread function, to initialise the thread system */
  extern void __pthread_init (void);
  /* Called if a non recoverable error is detected */
  extern void __pthread_fatal_error (const char *msg);

-/* Allocate a new node for a thread's details */
-extern pthread_t __pthread_new_node (void);
+/* Initialise a new node, allocating it if node is NULL */
+extern pthread_t __pthread_new_node (pthread_t node);

  /* Common routines used by mutex/rwlock/cond functions */
  extern int __pthread_lock_lock (pthread_mutex_t *mutex, const enum __pthread_locktype, const int trylock);
@@ -484,6 +506,9 @@
  extern int __pthread_enable_ints (void);
  /* Initialise a context save area */
  extern void __pthread_init_save_area (struct __pthread_saved_context *);
+/* Calls alloca cleanup functions on thread exit */
+extern void __pthread_exit (void);
+

  /* Prevent the callevery interrupt from initialising a context switch,
     and register a function to reenable them when the current function returns */
Index: unixlib/source/clib/errno.h
===================================================================
--- unixlib/source/clib/errno.h
+++ tmp.32048.00001	Thu Oct 17 18:23:06 2002
@@ -20,12 +20,25 @@
  __BEGIN_DECLS

  #ifndef errno
+
+#if __FEATURE_PTHREADS
+
+#define __need_pthread_t
+#include <pthread.h>
+
+#define errno (__pthread_running_thread->thread_errno)
+
+#else
  extern int errno;
+#endif /* __FEATURE_PTHREADS */
+
  #define __errno errno
-#endif
+
+#endif /* errno */

  extern const char *sys_errlist[];
  extern int sys_nerr;
+extern char *__ul_errbuf;

  #define EPERM           1 /* Operation not permitted.  */
  #define ENOENT		2 /* No such file or directory.  */
@@ -130,7 +143,11 @@
     threads ever appear.  We also give a return value so we can use
     return __set_errno () which can allow function tail calling.  */
  #ifndef __set_errno
+#if __FEATURE_PTHREADS
+#define __set_errno(val) (__pthread_running_thread->thread_errno = (val), -1)
+#else
  #define __set_errno(val) (errno = (val), -1)
+#endif
  #endif

  __END_DECLS
Index: unixlib/source/errno.c
===================================================================
--- unixlib/source/errno.c
+++ tmp.62416.00001	Thu Oct 17 18:23:07 2002
@@ -14,4 +14,7 @@

  #include <errno.h>

+#if !__FEATURE_PTHREADS
  int errno = 0;
+#endif
+




More information about the gcc mailing list