]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - sysdeps/mach/hurd/fork.c
Add script to update copyright notices and reformat some to facilitate its use.
[thirdparty/glibc.git] / sysdeps / mach / hurd / fork.c
index 8d730ae4fb8a2c2655a01281ecd4b8dfd5437a98..d690a0685670c19d41f6642f8feda6430fc5ac22 100644 (file)
@@ -1,31 +1,33 @@
-/* Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
-This file is part of the GNU C Library.
+/* Copyright (C) 1994-2012 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
 
-The GNU C Library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Library General Public License as
-published by the Free Software Foundation; either version 2 of the
-License, or (at your option) any later version.
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
 
-The GNU C Library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-Library General Public License for more details.
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
 
-You should have received a copy of the GNU Library General Public
-License along with the GNU C Library; see the file COPYING.LIB.  If
-not, write to the Free Software Foundation, Inc., 675 Mass Ave,
-Cambridge, MA 02139, USA.  */
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 #include <errno.h>
 #include <unistd.h>
 #include <hurd.h>
 #include <hurd/signal.h>
 #include <setjmp.h>
-#include "thread_state.h"
+#include <thread_state.h>
 #include <sysdep.h>            /* For stack growth direction.  */
 #include "set-hooks.h"
 #include <assert.h>
 #include "hurdmalloc.h"                /* XXX */
+#include <tls.h>
+
+#undef __fork
 
 
 /* Things that want to be locked while forking.  */
@@ -61,12 +63,10 @@ __fork (void)
   struct hurd_sigstate *volatile ss;
 
   ss = _hurd_self_sigstate ();
-  __spin_lock (&ss->lock);
-  ss->critical_section = 1;
-  __spin_unlock (&ss->lock);
+  __spin_lock (&ss->critical_section_lock);
 
 #undef LOSE
-#define LOSE assert_perror (err) /* XXX */
+#define LOSE do { assert_perror (err); goto lose; } while (0) /* XXX */
 
   if (! setjmp (env))
     {
@@ -126,8 +126,8 @@ __fork (void)
         so nothing changes.  */
       err = __proc_dostop (_hurd_ports[INIT_PORT_PROC].port, ss->thread);
       if (!err)
-        {
-          stopped = 1;
+       {
+         stopped = 1;
 
 #define XXX_KERNEL_PAGE_FAULT_BUG /* XXX work around page fault bug in mk */
 
@@ -149,8 +149,12 @@ __fork (void)
          __thread_abort (_hurd_msgport_thread);
 #endif
          /* Create the child task.  It will inherit a copy of our memory.  */
-         err = __task_create (__mach_task_self (), 1, &newtask);
-        }
+         err = __task_create (__mach_task_self (),
+#ifdef KERN_INVALID_LEDGER
+                              NULL, 0, /* OSF Mach */
+#endif
+                              1, &newtask);
+       }
 
       /* Unlock the global signal state lock, so we do not
         block the signal thread any longer than necessary.  */
@@ -254,9 +258,21 @@ __fork (void)
                        __mach_port_deallocate (__mach_task_self (), old);
                      /* The new task will receive its own exceptions
                         on its message port.  */
-                     if (err = __task_set_special_port (newtask,
-                                                        TASK_EXCEPTION_PORT,
-                                                        port))
+                     if (err =
+#ifdef TASK_EXCEPTION_PORT
+                         __task_set_special_port (newtask,
+                                                  TASK_EXCEPTION_PORT,
+                                                  port)
+#elif defined (EXC_MASK_ALL)
+                         __task_set_exception_ports
+                         (newtask, EXC_MASK_ALL & ~(EXC_MASK_SYSCALL
+                                                    | EXC_MASK_MACH_SYSCALL
+                                                    | EXC_MASK_RPC_ALERT),
+                          port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE)
+#else
+# error task_set_exception_port?
+#endif
+                         )
                        LOSE;
                    }
                  if (err = __mach_port_insert_right (newtask,
@@ -295,10 +311,11 @@ __fork (void)
            {
              /* This is a send right or a dead name.
                 Give the child as many references for it as we have.  */
-             mach_port_urefs_t refs, *record_refs = NULL;
+             mach_port_urefs_t refs = 0, *record_refs = NULL;
              mach_port_t insert;
-             if (portnames[i] == newtask)
-               /* Skip the name we use for the child's task port.  */
+             mach_msg_type_name_t insert_type = MACH_MSG_TYPE_COPY_SEND;
+             if (portnames[i] == newtask || portnames[i] == newproc)
+               /* Skip the name we use for the child's task or proc ports.  */
                continue;
              if (portnames[i] == __mach_task_self ())
                /* For the name we use for our own task port,
@@ -306,9 +323,9 @@ __fork (void)
                insert = newtask;
              else if (portnames[i] == _hurd_ports[INIT_PORT_PROC].port)
                {
-                 /* Get the proc server port for the new task.  */
-                 if (err = __proc_task2proc (portnames[i], newtask, &insert))
-                   LOSE;
+                 /* Use the proc server port for the new task.  */
+                 insert = newproc;
+                 insert_type = MACH_MSG_TYPE_COPY_SEND;
                }
              else if (portnames[i] == ss->thread)
                {
@@ -318,9 +335,9 @@ __fork (void)
                  insert = MACH_PORT_NULL;
                  record_refs = &thread_refs;
                  /* Allocate a dead name right for this name as a
-                     placeholder, so the kernel will not chose this name
-                     for any other new port (it might use it for one of the
-                     rights created when a thread is created).  */
+                    placeholder, so the kernel will not chose this name
+                    for any other new port (it might use it for one of the
+                    rights created when a thread is created).  */
                  if (err = __mach_port_allocate_name
                      (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i]))
                    LOSE;
@@ -347,6 +364,7 @@ __fork (void)
                  if (j < nthreads)
                    continue;
 
+                 /* Copy our own send right.  */
                  insert = portnames[i];
                }
              /* Find out how many user references we have for
@@ -370,8 +388,7 @@ __fork (void)
                /* Insert the chosen send right into the child.  */
                err = __mach_port_insert_right (newtask,
                                                portnames[i],
-                                               insert,
-                                               MACH_MSG_TYPE_COPY_SEND);
+                                               insert, insert_type);
              switch (err)
                {
                case KERN_NAME_EXISTS:
@@ -420,7 +437,7 @@ __fork (void)
       ports_locked = 0;
 
       /* All state has now been copied from the parent.  It is safe to
-         resume other parent threads.  */
+        resume other parent threads.  */
       resume_threads ();
 
       /* Create the child main user thread and signal thread.  */
@@ -429,9 +446,9 @@ __fork (void)
        LOSE;
 
       /* Insert send rights for those threads.  We previously allocated
-         dead name rights with the names we want to give the thread ports
-         in the child as placeholders.  Now deallocate them so we can use
-         the names.  */
+        dead name rights with the names we want to give the thread ports
+        in the child as placeholders.  Now deallocate them so we can use
+        the names.  */
       if ((err = __mach_port_deallocate (newtask, ss->thread)) ||
          (err = __mach_port_insert_right (newtask, ss->thread,
                                           thread, MACH_MSG_TYPE_COPY_SEND)))
@@ -480,9 +497,27 @@ __fork (void)
                                    (natural_t *) &state, &statecount))
        LOSE;
 #if STACK_GROWTH_UP
-      state.SP = __hurd_sigthread_stack_base;
+#define THREADVAR_SPACE (__hurd_threadvar_max \
+                        * sizeof *__hurd_sightread_variables)
+      if (__hurd_sigthread_stack_base == 0)
+       {
+         state.SP &= __hurd_threadvar_stack_mask;
+         state.SP += __hurd_threadvar_stack_offset + THREADVAR_SPACE;
+       }
+      else
+       state.SP = __hurd_sigthread_stack_base;
 #else
-      state.SP = __hurd_sigthread_stack_end;
+      if (__hurd_sigthread_stack_end == 0)
+       {
+         /* The signal thread has a normal stack assigned by cthreads.
+            The threadvar_stack variables conveniently tell us how
+            to get to the highest address in the stack, just below
+            the per-thread variables.  */
+         state.SP &= __hurd_threadvar_stack_mask;
+         state.SP += __hurd_threadvar_stack_offset;
+       }
+      else
+       state.SP = __hurd_sigthread_stack_end;
 #endif
       MACHINE_THREAD_STATE_SET_PC (&state,
                                   (unsigned long int) _hurd_msgport_receive);
@@ -494,6 +529,11 @@ __fork (void)
 
       /* Set the child user thread up to return 1 from the setjmp above.  */
       _hurd_longjmp_thread_state (&state, env, 1);
+
+      /* Do special thread setup for TLS if needed.  */
+      if (err = _hurd_tls_fork (thread, &state))
+       LOSE;
+
       if (err = __thread_set_state (thread, MACHINE_THREAD_STATE_FLAVOR,
                                    (natural_t *) &state, statecount))
        LOSE;
@@ -569,19 +609,22 @@ __fork (void)
       struct hurd_sigstate *oldstates;
 
       /* We are the child task.  Unlock the standard port cells, which were
-         locked in the parent when we copied its memory.  The parent has
-         inserted send rights with the names that were in the cells then.  */
+        locked in the parent when we copied its memory.  The parent has
+        inserted send rights with the names that were in the cells then.  */
       for (i = 0; i < _hurd_nports; ++i)
        __spin_unlock (&_hurd_ports[i].lock);
 
-      /* We are the only thread in this new task, so we will
-        take the task-global signals.  */
+      /* We are one of the (exactly) two threads in this new task, we
+        will take the task-global signals.  */
       _hurd_sigthread = ss->thread;
 
-      /* Unchain the sigstate structures for threads that existed in the
-        parent task but don't exist in this task (the child process).
-        Delay freeing them until later because some of the further setup
-        and unlocking might be required for free to work.  */
+      /* Claim our sigstate structure and unchain the rest: the
+        threads existed in the parent task but don't exist in this
+        task (the child process).  Delay freeing them until later
+        because some of the further setup and unlocking might be
+        required for free to work.  Before we finish cleaning up,
+        we will reclaim the signal thread's sigstate structure (if
+        it had one).  */
       oldstates = _hurd_sigstates;
       if (oldstates == ss)
        oldstates = ss->next;
@@ -603,7 +646,7 @@ __fork (void)
                                             &_hurd_orphaned));
 
       /* Forking clears the trace flag.  */
-      _hurd_exec_flags &= ~EXEC_TRACED;
+      __sigemptyset (&_hurdsig_traced);
 
       /* Run things that want to run in the child task to set up.  */
       RUN_HOOK (_hurd_fork_child_hook, ());
@@ -615,13 +658,26 @@ __fork (void)
       if (!err)
        err = __thread_resume (_hurd_msgport_thread);
 
-      /* Free the old sigstate structures.  */
+      /* Reclaim the signal thread's sigstate structure and free the
+        other old sigstate structures.  */
       while (oldstates != NULL)
        {
          struct hurd_sigstate *next = oldstates->next;
-         free (oldstates);
+
+         if (oldstates->thread == _hurd_msgport_thread)
+           {
+             /* If we have a second signal state structure then we
+                must have been through here before--not good.  */
+             assert (_hurd_sigstates->next == 0);
+             _hurd_sigstates->next = oldstates;
+             oldstates->next = 0;
+           }
+         else
+           free (oldstates);
+
          oldstates = next;
        }
+
       /* XXX what to do if we have any errors here? */
 
       pid = 0;
@@ -641,5 +697,6 @@ __fork (void)
 
   return err ? __hurd_fail (err) : pid;
 }
+libc_hidden_def (__fork)
 
 weak_alias (__fork, fork)