initial commit
[glibc.git] / sysdeps / unix / bsd / bsd4.4 / kfreebsd / clone.c
1 /* Create a thread.
2 Copyright (C) 2002 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Bruno Haible <bruno@clisp.org>, 2002.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
20
21 #define __clone __no_broken_clone_decl
22 #include <sched.h>
23 #include <sys/rfork.h>
24 #include <errno.h>
25 #include <signal.h>
26 #include <stddef.h>
27 #include <getosreldate.h>
28 #undef __clone
29
30 /* __start_thread (flags, child_stack, fn, arg)
31 is roughly equivalent to
32
33 int retval = __rfork (flags);
34 if (retval == 0)
35 {
36 // Here we are in the child thread.
37 %stackpointer = child_stack;
38 _exit (fn (arg));
39 }
40 return retval;
41
42 but it cannot be done in portable C because it must access fn and arg
43 after having replaced the stack pointer. */
44
45 extern int __start_thread (int flags, void *child_stack,
46 int (*fn) (void *), void *arg);
47
48 int __clone (int (*fn) (void *), void *child_stack, int flags, void *arg)
49 {
50 int rfork_flags = RFPROC;
51
52 if (fn == NULL || child_stack == NULL)
53 {
54 __set_errno (EINVAL);
55 return -1;
56 }
57
58 /* This implementation of clone() does not support all Linux flags. */
59 if (flags & ~(CSIGNAL | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND
60 | CLONE_VFORK | CLONE_SYSVSEM))
61 {
62 __set_errno (EINVAL);
63 return -1;
64 }
65
66 if ((flags & CSIGNAL) != SIGCHLD)
67 {
68 if (__kernel_getosreldate() >= 802510)
69 /* we slightly cheat here, */
70 /* the 9.x snapshot prior to r223966 does not support it too */
71 {
72 if ((flags & CSIGNAL) & ~RFTSIGMASK)
73 {
74 __set_errno (EINVAL);
75 return -1;
76 }
77 rfork_flags |= (RFTSIGZMB | RFTSIGFLAGS(flags & CSIGNAL));
78 }
79 else
80 {
81 if ((flags & CSIGNAL) & ~RFTHPNMASK)
82 {
83 __set_errno (EINVAL);
84 return -1;
85 }
86 if ((flags & CSIGNAL) == 0)
87 rfork_flags |= (RFLINUXTHPN | ((SIGCHLD) << RFTHPNSHIFT));
88 else
89 rfork_flags |= (RFLINUXTHPN | ((flags & CSIGNAL) << RFTHPNSHIFT));
90 }
91 }
92 if (flags & CLONE_VM)
93 rfork_flags |= RFMEM;
94
95 if (flags & CLONE_FS)
96 /* Sharing the filesystem related info (umask, cwd, root dir)
97 is not supported by rfork. Ignore this; let's hope programs
98 will set their umask and cwd before spawning threads. */
99 ;
100
101 if (flags & CLONE_SYSVSEM)
102 /* Ignore this; it has been introduced into linuxthreads in post 2.4 glibc */
103 ;
104
105 if (!(flags & CLONE_FILES))
106 rfork_flags |= RFFDG;
107
108 if (flags & CLONE_SIGHAND)
109 {
110 rfork_flags |= RFSIGSHARE;
111 /* Also set the undocumented flag RFTHREAD. It has the effect that when
112 the thread leader exits, all threads belonging to it are killed. */
113 rfork_flags |= RFTHREAD;
114 }
115
116 if (flags & CLONE_VFORK)
117 rfork_flags |= RFPPWAIT;
118
119 return __start_thread (rfork_flags, child_stack, fn, arg);
120 }
121
122 weak_alias (__clone, clone)