]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libkse/thread/thr_create.c
add pthread_cancel, obtained from OpenBSD.
[FreeBSD/FreeBSD.git] / lib / libkse / thread / thr_create.c
1 /*
2  * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by John Birrell.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $FreeBSD$
33  */
34 #include <errno.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <sys/time.h>
40 #include <sys/types.h>
41 #include <sys/mman.h>
42 #ifdef _THREAD_SAFE
43 #include <machine/reg.h>
44 #include <pthread.h>
45 #include "pthread_private.h"
46 #include "libc_private.h"
47
48 int
49 pthread_create(pthread_t * thread, const pthread_attr_t * attr,
50                void *(*start_routine) (void *), void *arg)
51 {
52         int             f_gc = 0;
53         int             ret = 0;
54         pthread_t       gc_thread;
55         pthread_t       new_thread;
56         pthread_attr_t  pattr;
57         void           *stack;
58
59         /*
60          * Locking functions in libc are required when there are
61          * threads other than the initial thread.
62          */
63         __isthreaded = 1;
64
65         /* Allocate memory for the thread structure: */
66         if ((new_thread = (pthread_t) malloc(sizeof(struct pthread))) == NULL) {
67                 /* Insufficient memory to create a thread: */
68                 ret = EAGAIN;
69         } else {
70                 /* Check if default thread attributes are required: */
71                 if (attr == NULL || *attr == NULL) {
72                         /* Use the default thread attributes: */
73                         pattr = &pthread_attr_default;
74                 } else {
75                         pattr = *attr;
76                 }
77                 /* Check if a stack was specified in the thread attributes: */
78                 if ((stack = pattr->stackaddr_attr) != NULL) {
79                 }
80                 /* Allocate memory for a default-size stack: */
81                 else if (pattr->stacksize_attr == PTHREAD_STACK_DEFAULT) {
82                         struct stack    *spare_stack;
83                         
84                         /* Allocate or re-use a default-size stack. */
85                         
86                         /*
87                          * Use the garbage collector mutex for synchronization
88                          * of the spare stack list.
89                          */
90                         if (pthread_mutex_lock(&_gc_mutex) != 0)
91                                 PANIC("Cannot lock gc mutex");
92                         
93                         if ((spare_stack = SLIST_FIRST(&_stackq)) != NULL) {
94                                 /* Use the spare stack. */
95                                 SLIST_REMOVE_HEAD(&_stackq, qe);
96                                 
97                                 /* Unlock the garbage collector mutex. */
98                                 if (pthread_mutex_unlock(&_gc_mutex) != 0)
99                                         PANIC("Cannot unlock gc mutex");
100                                 
101                                 stack = sizeof(struct stack)
102                                     + (void *) spare_stack
103                                     - PTHREAD_STACK_DEFAULT;
104                         } else {
105                                 /* Unlock the garbage collector mutex. */
106                                 if (pthread_mutex_unlock(&_gc_mutex) != 0)
107                                         PANIC("Cannot unlock gc mutex");
108                             
109                                 /* Allocate a new stack. */
110                                 stack = _next_stack + PTHREAD_STACK_GUARD;
111                                 /*
112                                  * Even if stack allocation fails, we don't want
113                                  * to try to use this location again, so
114                                  * unconditionally decrement _next_stack.  Under
115                                  * normal operating conditions, the most likely
116                                  * reason for an mmap() error is a stack
117                                  * overflow of the adjacent thread stack.
118                                  */
119                                 _next_stack -= (PTHREAD_STACK_DEFAULT
120                                                 + PTHREAD_STACK_GUARD);
121
122                                 /* Red zone: */
123                                 if (mmap(_next_stack, PTHREAD_STACK_GUARD, 0,
124                                          MAP_ANON, -1, 0) == MAP_FAILED) {
125                                         ret = EAGAIN;
126                                         free(new_thread);
127                                 }
128                                 /* Stack: */
129                                 else if (mmap(stack,
130                                               PTHREAD_STACK_DEFAULT,
131                                               PROT_READ | PROT_WRITE,
132 #ifdef __i386__
133                                               MAP_STACK,
134 #else
135                                               MAP_ANON,
136 #endif
137                                               -1, 0) == MAP_FAILED) {
138                                         ret = EAGAIN;
139                                         munmap(_next_stack,
140                                                PTHREAD_STACK_GUARD);
141                                         free(new_thread);
142                                 }
143                         }
144                 }
145                 /*
146                  * The user wants a stack of a particular size.  Lets hope they
147                  * really know what they want, and simply malloc the stack.
148                  */
149                 else if ((stack = (void *) malloc(pattr->stacksize_attr))
150                          == NULL) {
151                         /* Insufficient memory to create a thread: */
152                         ret = EAGAIN;
153                         free(new_thread);
154                 }
155
156                 /* Check for errors: */
157                 if (ret != 0) {
158                 } else {
159                         /* Initialise the thread structure: */
160                         memset(new_thread, 0, sizeof(struct pthread));
161                         new_thread->slice_usec = -1;
162                         new_thread->sig_saved = 0;
163                         new_thread->stack = stack;
164                         new_thread->start_routine = start_routine;
165                         new_thread->arg = arg;
166
167                         new_thread->cancelflags = PTHREAD_CANCEL_ENABLE |
168                             PTHREAD_CANCEL_DEFERRED;
169
170                         /*
171                          * Write a magic value to the thread structure
172                          * to help identify valid ones:
173                          */
174                         new_thread->magic = PTHREAD_MAGIC;
175
176                         /* Initialise the thread for signals: */
177                         new_thread->sigmask = _thread_run->sigmask;
178
179                         /* Initialise the jump buffer: */
180                         setjmp(new_thread->saved_jmp_buf);
181
182                         /*
183                          * Set up new stack frame so that it looks like it
184                          * returned from a longjmp() to the beginning of
185                          * _thread_start().
186                          */
187 #if     defined(__FreeBSD__)
188 #if     defined(__alpha__)
189                         new_thread->saved_jmp_buf[0]._jb[2] = (long) _thread_start;
190                         new_thread->saved_jmp_buf[0]._jb[4 + R_RA] = 0;
191                         new_thread->saved_jmp_buf[0]._jb[4 + R_T12] = (long) _thread_start;
192 #else
193                         new_thread->saved_jmp_buf[0]._jb[0] = (long) _thread_start;
194 #endif
195 #elif   defined(__NetBSD__)
196 #if     defined(__alpha__)
197                         new_thread->saved_jmp_buf[2] = (long) _thread_start;
198                         new_thread->saved_jmp_buf[4 + R_RA] = 0;
199                         new_thread->saved_jmp_buf[4 + R_T12] = (long) _thread_start;
200 #else
201                         new_thread->saved_jmp_buf[0] = (long) _thread_start;
202 #endif
203 #else
204 #error  "Don't recognize this operating system!"
205 #endif
206
207                         /* The stack starts high and builds down: */
208 #if     defined(__FreeBSD__)
209 #if     defined(__alpha__)
210                         new_thread->saved_jmp_buf[0]._jb[4 + R_SP] = (long) new_thread->stack + pattr->stacksize_attr - sizeof(double);
211 #else
212                         new_thread->saved_jmp_buf[0]._jb[2] = (int) (new_thread->stack + pattr->stacksize_attr - sizeof(double));
213 #endif
214 #elif   defined(__NetBSD__)
215 #if     defined(__alpha__)
216                         new_thread->saved_jmp_buf[4 + R_SP] = (long) new_thread->stack + pattr->stacksize_attr - sizeof(double);
217 #else
218                         new_thread->saved_jmp_buf[2] = (long) new_thread->stack + pattr->stacksize_attr - sizeof(double);
219 #endif
220 #else
221 #error  "Don't recognize this operating system!"
222 #endif
223
224                         /* Copy the thread attributes: */
225                         memcpy(&new_thread->attr, pattr, sizeof(struct pthread_attr));
226
227                         /*
228                          * Check if this thread is to inherit the scheduling
229                          * attributes from its parent: 
230                          */
231                         if (new_thread->attr.flags & PTHREAD_INHERIT_SCHED) {
232                                 /* Copy the scheduling attributes: */
233                                 new_thread->base_priority
234                                     = _thread_run->base_priority;
235                                 new_thread->attr.prio
236                                     = _thread_run->base_priority;
237                                 new_thread->attr.sched_policy
238                                     = _thread_run->attr.sched_policy;
239                         } else {
240                                 /*
241                                  * Use just the thread priority, leaving the
242                                  * other scheduling attributes as their
243                                  * default values: 
244                                  */
245                                 new_thread->base_priority
246                                     = new_thread->attr.prio;
247                         }
248                         new_thread->active_priority = new_thread->base_priority;
249                         new_thread->inherited_priority = 0;
250
251                         /* Initialise the join queue for the new thread: */
252                         TAILQ_INIT(&(new_thread->join_queue));
253
254                         /* Initialize the mutex queue: */
255                         TAILQ_INIT(&new_thread->mutexq);
256
257                         /* Initialise hooks in the thread structure: */
258                         new_thread->specific_data = NULL;
259                         new_thread->cleanup = NULL;
260                         new_thread->flags = 0;
261                         new_thread->poll_data.nfds = 0;
262                         new_thread->poll_data.fds = NULL;
263
264                         /*
265                          * Defer signals to protect the scheduling queues
266                          * from access by the signal handler:
267                          */
268                         _thread_kern_sig_defer();
269
270                         /*
271                          * Check if the garbage collector thread
272                          * needs to be started.
273                          */
274                         f_gc = (TAILQ_FIRST(&_thread_list) == _thread_initial);
275
276                         /* Add the thread to the linked list of all threads: */
277                         TAILQ_INSERT_HEAD(&_thread_list, new_thread, tle);
278
279                         if (pattr->suspend == PTHREAD_CREATE_SUSPENDED) {
280                                 new_thread->state = PS_SUSPENDED;
281                                 PTHREAD_WAITQ_INSERT(new_thread);
282                         } else {
283                                 new_thread->state = PS_RUNNING;
284                                 PTHREAD_PRIOQ_INSERT_TAIL(new_thread);
285                         }
286
287                         /*
288                          * Undefer and handle pending signals, yielding
289                          * if necessary.
290                          */
291                         _thread_kern_sig_undefer();
292
293                         /* Return a pointer to the thread structure: */
294                         (*thread) = new_thread;
295
296                         /* Schedule the new user thread: */
297                         _thread_kern_sched(NULL);
298
299                         /*
300                          * Start a garbage collector thread
301                          * if necessary.
302                          */
303                         if (f_gc && pthread_create(&gc_thread,NULL,
304                                     _thread_gc,NULL) != 0)
305                                 PANIC("Can't create gc thread");
306                 }
307         }
308
309         /* Return the status: */
310         return (ret);
311 }
312
313 void
314 _thread_start(void)
315 {
316         /* We just left the scheduler via longjmp: */
317         _thread_kern_in_sched = 0;
318
319         /* Run the current thread's start routine with argument: */
320         pthread_exit(_thread_run->start_routine(_thread_run->arg));
321
322         /* This point should never be reached. */
323         PANIC("Thread has resumed after exit");
324 }
325 #endif